JDBC 学习笔记

JDBC 学习笔记

JDBC 四大核心参数

  1. 驱动类名 MySQL 8.x:com.mysql.cj.jdbc.Driver(必须带 .cj) MySQL 5.x:com.mysql.jdbc.Driver(不需要 .cj)
  2. 数据库URL 格式:jdbc:mysql://主机IP:端口号/数据库名?参数
  • 主机IP:本地 localhost / 127.0.0.1,远程填写服务器公网IP
  • MySQL 默认端口:3306
  • 常用参数:
  • useSSL=false:开发环境关闭SSL加密
  • serverTimezone=UTC:指定时区,8.0版本必填
  1. 数据库用户名
  2. 数据库密码
// 示例配置
String driverName = "com.mysql.cj.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/stock?useSSL=false&serverTimezone=UTC";
String username = "root";
String password = "123456";

JDBC 标准执行步骤

  1. 加载驱动
Class.forName(driverName);
  1. 建立数据库连接
Connection conn = DriverManager.getConnection(url, username, password);
  1. 编写SQL语句,使用PreparedStatement(防止SQL注入)

❌ 不推荐:Statement,存在SQL注入风险 ✅ 推荐:PreparedStatement,SQL预先编译,使用占位符 ?

占位符说明:

  • ? 为占位符,后续动态赋值
  • SQL中字符串、日期使用单引号' ',不要自己拼接字符串
// 查询SQL示例
String sql = "select * from user where userName = ? and pwd = ? ";
// 新增SQL示例
String sql = "insert into user(userName,pwd) values(?,?)";

PreparedStatement statement = conn.prepareStatement(sql);
// 给占位符赋值,下标从1开始
String usename = "admin";
String pwd = "123456";
statement.setString(1, usename);
statement.setString(2, pwd);
  1. 执行SQL语句
  • 增 / 删 / 改:executeUpdate() → 返回受影响行数
  • 查询:executeQuery() → 返回 ResultSet 结果集

示例:登录校验(查询单行数据)

ResultSet rset = statement.executeQuery();
// next():指针向下移动一行,有数据返回true,无数据返回false
if (rset.next()) {
    System.out.println("登录成功");
}else  {
    System.out.println("登录失败");
}

示例:查询多条数据,遍历结果集

String sql = "select * from user";
ResultSet rset = statement.executeQuery(sql);

while(rset.next()){
    //方式1:根据列名获取(推荐,可读性高)
    String userNo = rset.getString("userNo");
    String userName = rset.getString("userName");
    int pwd = rset.getInt("pwd");

    //方式2:根据列下标获取(从1开始)
    //String userNo = rset.getString(1);
    //int pwd = rset.getInt(2);
    //String userName = rset.getString(3);

    System.out.println(userNo + " " + userName + " " + pwd);
}
  1. 关闭资源(逆序关闭)
rset.close();
statement.close();
conn.close();

常见异常以及原因

  1. java.lang.ClassNotFoundException: com.mysql.jdbc.Driver 原因:缺少MySQL驱动包 或者 驱动类名写错(8.0漏写cj)
  2. java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES) 原因:数据库用户名或者密码错误
  3. MySQLSyntaxErrorException: Unknown database 'xxx' 原因:URL中填写的数据库名称不存在
  4. Communications link failure 原因:
  • MySQL服务未启动
  • IP/端口填写错误
  • 防火墙拦截3306端口
  • 远程服务器未开放MySQL访问权限

补充知识点

  • SQL注入产生原因:Statement直接拼接字符串,SQL语句先拼接,后编译
  • PreparedStatement:SQL先编译,再填充参数,从根源避免SQL注入
  • 占位符下标从1开始,不是0!

完整代码

 @Test
    public void test6()  {

        ResultSet resultSet = null;
        Statement statement = null;
        Connection connection = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/study?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2b8", "root", "123456");
            String sql = "select * from student";
            statement = connection.createStatement();
            resultSet = statement.executeQuery(sql);
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                int age = resultSet.getInt("age");
                String gender = resultSet.getString("gender");
                Student student = new Student(id, name, age, gender);
                System.out.println(student);
            }

        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {

            try {
                resultSet.close();
                statement.close();
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }

        }
    }

Utils工具类封装数据

jdbc.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/stock?useSSL=false
username=root
password=123456

JdbcUtils


/**
 * JDBC工具类
 * 作用:封装数据库连接获取、资源关闭的重复代码
 * 从classpath下的jdbc.properties配置文件读取数据库信息
 */
public class JdbcUtils {
    // 数据库驱动类名
    private static String driver;
    // 数据库连接地址
    private static String url;
    // 数据库登录账号
    private static String username;
    // 数据库登录密码
    private static String password;

    /**
     * 私有构造方法
     * 禁止外部new创建工具类对象,工具类全部使用静态方法
     */
    private JdbcUtils() {

    }

    /**
     * 静态代码块
     * 类加载的时候只执行一次:读取配置文件、加载数据库驱动
     */
    static {
        try {
            //1.通过当前类获得类加载器,读取resources下的配置文件
            ClassLoader classLoader = JdbcUtils.class.getClassLoader();
            // 2. 通过类加载器获取资源文件输入流,读取jdbc.properties
            InputStream inputStream = classLoader.getResourceAsStream("jdbc.properties");
            //3.创建Properties集合,专门读取properties配置文件
            Properties properties = new Properties();
            // 将输入流中的配置信息加载到Properties对象
            properties.load(inputStream);
            // 取出配置文件中的参数赋值给静态变量
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

        } catch (IOException e) {
            // 配置文件读取失败,包装为运行时异常,终止程序
            throw new RuntimeException("读取jdbc.properties配置文件失败", e);
        }

        try {
            // 加载数据库驱动
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            // 驱动类找不到,抛出运行异常
            throw new RuntimeException("数据库驱动类加载失败,请检查驱动类名", e);
        }
    }

    /**
     * 获取数据库连接对象
     * @return Connection 数据库连接
     * @throws SQLException 获取连接失败抛出SQL异常
     */
    public static Connection getConections() throws SQLException {
        Connection connection = DriverManager.getConnection(url, username, password);
        return connection;
    }

    /**
     * 统一关闭JDBC资源
     * @param connection 数据库连接对象
     * @param statement SQL执行对象
     * @param resultSet 查询结果集
     */
    public static void close(Connection connection, Statement statement, ResultSet resultSet) {
        // 关闭结果集
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                throw new RuntimeException("关闭ResultSet失败", e);
            }
        }

        // 关闭Statement
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                throw new RuntimeException("关闭Statement失败", e);
            }
        }

        // 关闭数据库连接
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException("关闭Connection失败", e);
            }
        }
    }
}

Demo

import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * JDBC CRUD测试类
 * 使用JdbcUtils工具类完成数据库增删改查操作
 */
public class JdbcCrudTest {

    /**
     * 查询测试:查询student表全部数据
     * 使用Statement实现查询(存在SQL注入风险)
     */
    @Test
    public void test7()  {
        ResultSet resultSet = null;
        Statement statement = null;
        Connection connection = null;
        try {
            // 获取数据库连接
            connection = JdbcUtils.getConections();
            // 编写查询SQL
            String sql = "select * from student";
            // 创建Statement执行对象
            statement = connection.createStatement();
            // 执行查询,得到结果集
            resultSet = statement.executeQuery(sql);
            // 遍历结果集,封装为Student对象
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                int age = resultSet.getInt("age");
                String gender = resultSet.getString("gender");
                Student student = new Student(id, name, age, gender);
                System.out.println(student);
            }
        } catch (SQLException e) {
            // SQL异常包装为运行时异常
            throw new RuntimeException("查询学生数据失败", e);
        } finally {
            // 统一关闭所有资源
            JdbcUtils.close(connection,statement,resultSet);
        }
    }

    /**
     * 新增测试:向student表插入一条学生数据
     * 使用PreparedStatement预编译对象,防止SQL注入
     */
    @Test
    public void test8()  {
        Connection connection = null;
        ResultSet resultSet = null;
        PreparedStatement preparedStatement = null;

        try {
            // 获取数据库连接
            connection = JdbcUtils.getConections();
            // 带占位符的新增SQL
            String sql = "insert into student(name,age,gender) values(?,?,?)";
            // 创建预编译执行对象
            preparedStatement = connection.prepareStatement(sql);
            // 给占位符赋值
            preparedStatement.setString(1,"zs");
            preparedStatement.setInt(2,1);
            preparedStatement.setString(3,"1");
            // 执行增删改操作
            preparedStatement.executeUpdate();
            // 获取受影响的行数
            int count = preparedStatement.getUpdateCount();

            System.out.println("新增影响行数:" + count);
        } catch (SQLException e) {
            throw new RuntimeException("新增学生失败", e);
        }finally {
            // 释放资源
            JdbcUtils.close(connection,preparedStatement,resultSet);
        }
    }

    /**
     * 删除测试:根据id删除学生记录
     * PreparedStatement方式执行删除
     */
    @Test
    public void test9()  {
        Connection connection = null;
        ResultSet resultSet = null;
        PreparedStatement preparedStatement = null;

        try {
            connection = JdbcUtils.getConections();
            // 根据id删除数据,?为占位符
            String sql = "delete from student where id = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1,7);
            preparedStatement.executeUpdate();
            // 获取删除影响行数
            int count = preparedStatement.getUpdateCount();

            System.out.println("删除影响行数:" + count);
        } catch (SQLException e) {
            throw new RuntimeException("删除学生失败", e);
        }finally {
            JdbcUtils.close(connection,preparedStatement,resultSet);
        }
    }

    /**
     * 修改测试:根据id更新学生年龄
     * PreparedStatement执行更新操作
     */
    @Test
    public void test10()  {
        Connection connection = null;
        ResultSet resultSet = null;
        PreparedStatement preparedStatement = null;

        try {
            connection = JdbcUtils.getConections();
            // 更新语句,占位符代替参数
            String sql = "update student set age = ? where id = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1,80);
            preparedStatement.setInt(2,8);
            preparedStatement.executeUpdate();
            int count = preparedStatement.getUpdateCount();

            System.out.println("修改影响行数:" + count);
        } catch (SQLException e) {
            throw new RuntimeException("更新学生信息失败", e);
        }finally {
            JdbcUtils.close(connection,preparedStatement,resultSet);
        }
    }
}

Druid池化技术优化Utils工具类

jdbc.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/study?useSSL=false
username=root
password=123456
#初始化连接数量
initialSize=10
#最大连接数量
maxActive=50
#最小空闲连接
minIdle=5

初级用法

public class Mytext {
    public static void main(String[] args) throws Exception {

        //创建连接池对象
        DruidDataSource dataSource = new DruidDataSource();
        //设置连接池参数
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/stock?useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        //初始化连接数量
        dataSource.setInitialSize(20);
        //最大连接数量
        dataSource.setMaxActive(100);
        //最小空闲连接
        dataSource.setMinIdle(5);

        //获取连接
        Connection connection = dataSource.getConnection();

        //获取PreparedStatement
        PreparedStatement preparedStatement = connection.prepareStatement("select * from user where id=?");
        //设置参数
        preparedStatement.setInt(1, 1);
        ResultSet resultSet = preparedStatement.executeQuery();
        if (resultSet.next()) {
            String uerNo = resultSet.getString("userNo");
            String userName = resultSet.getString("userName");
            int pwd = resultSet.getInt("pwd");
            System.out.println(uerNo + "-" + userName + "-" + pwd);
        }
        //关闭
        resultSet.close();
        preparedStatement.close();
        connection.close();


    }
}

进阶

public class JdbcUtil {
    //定义一个类属性
    private static DataSource dataSource;
//没有使用连接池之前
//private static String driverName;
//private static String url;
//private static String username;
//private static String password;

static {
    try {
        Properties prop = new Properties();
        //加载配置文件的另一种方式
        //InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
        //prop.load(in); 使用此方法注意moven项目properties要在resources
        prop.load(new FileInputStream("jdbc.properties"));
        //创建连接池(读取一个 Properties 对象,根据其中的配置信息,创建并返回一个配置好的 DruidDataSource 实例)
        dataSource = DruidDataSourceFactory.createDataSource(prop);
        //初始化四参数
        //driverName = prop.getProperty("jdbc.driverName");
        //url = prop.getProperty("jdbc.url");
        //username = prop.getProperty("jdbc.username");
        //password = prop.getProperty("jdbc.password");
        //加载驱动
        //Class.forName(driverName);
    } catch (IOException e) {
        throw new RuntimeException(e);
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    }
}
//获取链接
public  static Connection getConnection() throws SQLException {
    return dataSource.getConnection();
    //Connection conn = DriverManager.getConnection(url, username, password);
    return conn;
}
//关闭
public  static void  closerAll(Connection conn, PreparedStatement  ps, ResultSet rs) throws SQLException {
    if (rs != null) {
        rs.close();
    }
    if (ps != null) {
        ps.close();
    }
    if (conn != null) {
        conn.close();
    }
}
}

使用

public class text1 {

    public static void main(String[] args)throws Exception {
        //获取链接
       Connection conn= JdbcUtil.getConnection();
        //获取PreparedStatement
        PreparedStatement statement = conn.prepareStatement("select * from user where id= ?");
        //设置参数
        statement.setInt(1,1);
        //发送sql
        ResultSet resultSet =Statement.executeQuery();
        //处理结果
        while(resultSet.next())
        {
            String userNo = resultSet.getString("userNo");
            int Pwd = resultSet.getInt("Pwd");
            String userName = resultSet.getString("userName");
            System.out.println(userNo+" "+Pwd+" "+userName);
        }
        JdbcUtil.closerAll(conn,statement,resultSet);
    }
}

三种加载方式

prop.load(new FileInputStream("jdbc.properties"));


InputStream in = JdbcUtil.class.getResourceAsStream("/jdbc.properties");

prop.load(in);


InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");

prop.load(in);
方式 代码 资源查找起点 文件存放位置要求 打包 Jar 后能否使用 路径书写注意事项 推荐程度
方式 1 prop.load(new FileInputStream("jdbc.properties")); 操作系统文件系统,【项目根目录】 jdbc.properties 直接放在项目根文件夹(和 src 同级) ❌ 不能!jar 包内资源无法通过 FileInputStream 读取 写相对磁盘路径;不要写 classpath 相关路径 ⭐ 仅本地临时测试,禁止上线
方式 2 JdbcUtil.class.getResourceAsStream("jdbc.properties") 默认:JdbcUtil 所在包内路径开头加/ → classpath 根目录 不加/:和 JdbcUtil.java 同一个包加/:放到 resources 下(classpath 根) ✅ 支持 jar 包读取 不带/= 当前包;带/代表 classpath 根 ⭐⭐ 资源和类放在一起时使用
方式 3 JdbcUtil.class.getClassLoader().getResourceAsStream("jdbc.properties") classpath(类路径根目录) resources文件夹内(编译后进入 classes 根目录) ✅ 支持 jar 包读取 路径开头不能写 /,直接写文件名 ⭐⭐⭐⭐⭐【企业首选,你的 JdbcUtil 就用这个】

简答题背诵

  1. FileInputStream 操作磁盘上独立文件,和 classpath 无关,只能读取解压出来的文件,无法读取 jar 包内部资源。
  2. xxx.class.getResourceAsStream() 基于类的包路径进行查找,支持相对包路径,加斜杠切换到 classpath 根。
  3. 类加载器.getResourceAsStream() 统一从 classpath 根目录 搜索资源,所有 resources 下的文件编译后都在 classpath 根,写法统一,工业界标准写法。
上一篇
下一篇