Java JDBC 使用方法

497 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情

1. JDBC 是什么?

JDBC,全称 Java DataBase Connectivity, 是 Java 程序访问数据库的标准接口。

  • 使用 ava 码访问数据库时,并不会直接访问数据库,而是通过 JDBC 接口来进行访问,JDBC 接口则通过 JDBC 驱动来实现真正对数据库的连接和访问。

  • JDBC 接口是 Java JDK 标准库中自带的,可以直接编译,而 JDBC 驱动是由数据厂商提供的。比如在 Java 代码中访问 MySQL 时,除了引入 MySQL 的依赖信息外,还需要引入 MySQL 连接驱动。

  • Java 标准库自带的 JDBC 接口其实就是定义了一组接口,而某个具体的JDBC驱动就是实现了这些接口的类。

  • 引入驱动后就可以通过编写代码操作 JDBC 接口,这样保证了 Java 程序只需要编写一套代码,而通过引入不同的驱动,实现访问各种不同的数据库。

  • 总的来讲,在使用它 JDBC 时,本地编写的代码只需要对 Java 中自带的 JDBC 接口进行操作,在 MySQL 对应的 JDBC 驱动中会对接口进行具体的实现,代码在调用接口时就会实现对 MySQL 服务器进行连接操作。

使用 JDBC 带来的好处

  1. JDBC 接口是 Java 标准库中定义的,提供了标准的规范,Java 程序开发和编译期只需要考虑接口定义,不需要了解具体数据库是如何实现的

  2. 各个数据厂商使用相同的接口,Java 代码中可随时替换底层的数据库,访问数据库的 Java 代码基本不变

2. JDBC 怎么使用?

实际使用时 JDBC 并不知道最终使用哪种数据库,需要使用哪个数据库,就去使用那个数据库的实现类,即对应的 JDBC 驱动。

厂商提供的 JDBC 驱动通常也是 Java 开发的程序并打包为 Jar 类型,可以通过 Maven 引入。

本文通过使用 MySQL 数据库来演示 JDBC 使用流程。

2.1 引入依赖信息

  • MySQL 依赖
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.23</version>
</dependency>
  • MySQL 连接驱动依赖 Jar,即 JDBC 驱动包 可以使用下载 JDBC 的 Jar 包到本地进行引用的方式,而使用 Maven 进行统一管理更方便。
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.23</version>
</dependency>

2.2 建立JDBC连接

JDBC 使用之前需要先创建一个连接,之后所有 Java 代码对数据库的操作都可以通过该连接进行。

建立一个 Connection 连接需要准备的内容有:

  • URL,即连接数据库的地址,是数据库厂商指定的格式
  • user/password,即访问数据库所需要的用户名和密码

URL 作为数据连接地址,需要指定目标数据库类型标识、服务器地址、端口号、对应数据库名等信息,不同数据库定义了不同的 URL

  • MySql:dbc:mysql://localhost:3306/[mysql-db]?key1=value1&key2=value2
  • SqlServer:dbc:microsoft:sqlserver://localhost:1433;DatabaseName=[sqlserver-db]
  • Oracle:dbc:oracle:thin:@localhost:1521:[oracle-db]
  • useSSL=false 代表不使用 SSL 加密;
  • characterEncoding=utf8 即使用 UTF-8 编码(MySQL 默认 utf8)

完整的数据库连接代码如下:

@Test
void testJdbcConnection(){
    String url = "jdbc:mysql://localhost:3306/jdbc_test?useSSL=false";
    String username = "username";
    String password = "password";

    Connection conn = null;
    try {
        //1.加载驱动(开发推荐的方式,可以省略)
        //Class.forName("com.mysql.jdbc.Driver");

        //2.获取与数据库的链接
        conn = DriverManager.getConnection(url, username, password);
        ...
        conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
  • Class.forName("com.mysql.jdbc.Driver"); 用于将驱动类进行加载使用,而在 JDK1.5 之后引入的驱动 Jar 包会默认加载驱动类,因此可以省略。

  • DriverManager.getConnection(),该静态方法调用时会自动扫描 classpath,找到所有的 JDBC 驱动,然后根据传入的 URL 挑选一个合适的驱动。

  • JDBC 连接是一个昂贵的资源,使用完成后要及时释放,还可以自动释放连接的方法创建

    try(conn=DriverManager.getConnection(url, username, password)){
        ...
    }
    

连接时可能遇到的问题

  1. Establishing SSL connection without server's identity verification is not recommended.

    • 这是因为在连接数据库时 URL 中没有指定 useSSL 属性,提示需要指定
  2. Path does not chain with any of the trust anchors

    • 如果数据库连接字符串中设置了属性 useSSL=true 则会报上述问题,需要设置为 useSSL=false 才可以正常连接使用

3. JDBC 操作数据库

3.1 相关操作对象

获取到数据库连接 Connection 后,就可以在连接的基础上进行数据库操作了。

Connection 中提供了方法来创建一个 Statement 对象,该对象用来执行一条 SQL 语句实现对数据库的操作,执行的结果使用 ResultSet 对象接收。

Statement

命令 Statement stmt = conn.createStatement() 可以创建 Statement 对象,该对象中提供了多个执行 SQL 的方法

  • executeQuery(String sql),用于执行 select 查询语句
  • executeUpdate(String sql),用于执行 insert、update、delete 语句
  • execute(String sql),用于执行任意操作语句
  • addBatch(String sql),用于将多条 SQL 语句加入批处理中
  • executeBatch(),用于批量执行 SQL 语句

ResultSet

ResultSet 对象用来接收 Statement 中方法执行返回的结果内容,该对象中封装一系列方法来读取结果内容。

  1. 获取指定行的数据

    • next(),指针移动到下一行,用于获取下一行数据
    • Previous(),指针移动到上一行
    • absolute(int row),指针移动到指定行
    • beforeFirst(),指针移动到最前面
    • afterLast(),指针移动到最后面
  2. 通过索引或者列名来获取指定列的数据,列索引从 1 开始

    • getObject(int index),获取结果集中指定下标的数据
    • getObject(string columnName),获取结果集中指定列名的数据结果
    • getString(int index),获取指定下标的指定类型(String)数据
    • getString(string columnName),获取指定列名指定类型(String)数据

3.1 查询数据

查询操作的完整实现流程代码如下

@Test
void testJdbcConnection(){
    String url = "jdbc:mysql://localhost:3306/jdbc_test?useSSL=false";
    String username = "root";
    String password = "root";

    Connection conn = null;
    try {
        //1.加载驱动(开发推荐的方式,可以省略)
        //Class.forName("com.mysql.jdbc.Driver");

        //2.获取与数据库的链接
        conn = DriverManager.getConnection(url, username, password);

        try(Statement stmt = conn.createStatement()){
            try(ResultSet rs = stmt.executeQuery("select * from user_account")){
                while(rs.next()){
                    long id = rs.getLong(1);    //索引列从1开始,数据类型要一致,否则报错
                    String name = rs.getString("name");
                    String pwd = rs.getString(3);
                    System.out.println("id: " + id + "name: " + name + "pwd: " + pwd);
                }
            }
        }
        conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

得到输出结果如下

id: 1name: tompwd: 123456
id: 2name: lilypwd: 13579
  • Statement 和 ResultSet 都是需要关闭的资源,使 try 块确保及时关闭
  • ResultSet 索引列从 1 开始而不是 0

3.2 增删改操作

增加、修改、删除数据的操作均通过 stmt.executeUpdate(String sql) 方法实现,只是对应的 SQL 语句不同而已。

4. Statement 和 PrepaerStatement

使用 Statement 的方法传入 SQL 语句执行时,如 SQL 语句中使用变量传入值,则需要使用字符串拼接的方式,而字符串拼接容易引发 SQL 注入的问题。

Java 的 sql 库中还提供了一个 PerparedStatement 接口,该接口继承了 Statement 并可以避免 SQL 注入问题。

PreparedStatement 执行的 SQL 语句中可以使用 ? 作为占位符,然后将 SQL 和对应的数据传给数据库,不仅可以避免 SQL 注入,还能高效利用数据库本身对查询的缓存。

使用 PreparedStatement 替换 Statement 可写为:

//将sql语句提前,并且固定话,只是传入的参数值不同,避免了sql注入
try(PreparedStatement ps = conn.prepareStatement("select * from <tablename> where id = ? and name = ?")){
    //对于占位符进行赋值,同样是从1开始
    ps.setObject(1,10); 
    ps.setObject(2,"M");
    try(ResultSet rs = ps.executeQuery){
         while (rs.next()) {
            long id = rs.getLong("id");
            long grade = rs.getLong("grade");
            String name = rs.getString("name");
            String gender = rs.getString("gender");
        }
    }
}
  • 对于动态的 SQL 语句,尽量避免使用 Statement,而是使用 PreparedStatement 对象