Java中JDBC的使用

1 基本介绍

1.1 什么是 JDBC?

在Java中,我们势必会使用到访问数据库的操作,而现有的数据库种类又十分的多,比较常用的如MySQL、Oracle、SQL Server等等,不同的数据库设计时本身就不一样,因此在访问上,也会有所不同,为了便于开发者使用,Java提供了一套标准的、统一的访问接口,JDBC,全称 Java DataBase Connectivity,即Java数据库连接。

1.2 基本概念

  1. 注册驱动:其实就是使用 Class.forName()方法,使用反射去获取 jar 包内的东西。
  2. 连接对象 connection:使用这个对象才能进行后续的数据库操作,connection 的主要功能有:
    1. 获取执行 SQL 语句的对象:Statement 和 PrepareStatement
    2. 管理事务,三个基本的方法,提交,回滚,详情请看
  3. Statement 对象,执行 SQL 语句的对象,在 JDBC 中, 想要执行 SQL 语句,使用 Statement 是一种方式;
  4. prepareStatement 对象:执行 SQL 语句的另一种方式,其有两种功能,其一就是执行 SQL 语句,另一个就是预编译 SQL;
  5. ResultSet 对象:结果集对象,在查询语句时,会返回查询的结果,JDBC 会将查询到的数据放入这个对象中,可以遍历该对象获取查询的结果;

2 代码实践

会用到一些数据库的信息,但是仅仅是为了测试,所以数据库的特别简单,脚本如下

/*
为学习JDBC准备的数据库环境的脚本:
    1.db1   学习的数据库
        1.1 account 一个测试数据表,无特殊说明

*/
# 创建数据库
CREATE DATABASE if not exists db1;

USE db1;
SELECT DATABASE();

CREATE TABLE account
(
    `id`   INTEGER primary key auto_increment comment '主键',
    `name` varchar(20) comment '账户名',
    `money` INTEGER comment '账户余额'
) comment '银行账户表';


-- 插入数据
INSERT INTO account (name, money) values ('张三', 100), ('李四', 50);

2.1 JDBC 的基本使用

2.2.1 可运行代码示例

package cn.edu.njust.jdbcdemo;

import java.sql.*;

public class TestJDBCDemo01 {
    // MySQL 5.x 版本的驱动加载
    // private final static String DRIVER = "com.mysql.jdbc.Driver";

    // MySQL8.0 版本的驱动加载
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";

    /*
    * 以下是数据库连接的信息,包括连接地址,用户名和密码
    * */
    private static final String URL = "jdbc:mysql://localhost:3306/db1";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "root";

    public static void main(String[] args) throws Exception {
        /*
        * 注册驱动
        * 这一步使用反射来注册驱动
        * 而且在MySQL5.0以后,这一句代码不用自己书写,在jar包中的META-INF/services/java.sql.Driver
        * Java会自动扫描这个文件,注册驱动
        * */
        Class.forName(DRIVER);

        /*
        * 获取一个数据库连接
        * connection对象有两个功能
        *   1. 获取执行SQL语句的对象
        *   2. 对JDBC事务进行管理
        * */
        Connection con = DriverManager.getConnection(URL, USERNAME, PASSWORD);

        // 自定义的SQL语句
        String querySql = "SELECT * FROM account";
        String updateSql = "UPDATE account set money = 0 WHERE name = '张三'";

        /*
        * 获取执行SQL的statement对象
        * 该方式执行SQL语句时,不会采用预编译,在执行大量的SQL语句时效率较低
        * */
        Statement stmt = con.createStatement();

        /*
        * 第二种执行SQL语句的方式
        * 这种方式会采用预编译的方式,提高SQL执行效率,同时还能预防SQL注入
        * */
        PreparedStatement ps = con.prepareStatement(updateSql);
        try {
            // 关闭MySQL的自动提交事务
            con.setAutoCommit(false);

            // 执行SQL语句
            ResultSet res = stmt.executeQuery(querySql);
            // update方法返回的是收到影响的行数
            int update = ps.executeUpdate();

            System.out.println(res);
            System.out.println(update);

            // 提交MySQL事务
            con.commit();
        } catch (SQLException e) {
            // 如果出现异常,回滚事务
            con.rollback();

            throw new RuntimeException(e);
        }
        // 关闭资源
        stmt.close();
        con.close();
    }
}

2.1.2 补充说明

在 JDBC 中,使用 Class.forName()方法,这个方法是之前在 Java 基础中常用的反射,那么这里自己就有一个疑问?为什么没有使用一个对象去接收调用方法后的返回值?JDBC 是怎么获得这个返回对象的呢?

在 MySQL5.0 以后,也不需要调用这个方法了,查看引入的 jar 包,发现其下有一个目录文件,当运行项目的时候会扫描这个文件并自动加载这句话。如下:

  1. 加载的类

image.png

  1. 自动加载的配置文件

image.png

2.2 ResultSet 的基本使用

2.2.1 代码示例

package cn.edu.njust.jdbcdemo;

import java.sql.*;

public class TestJDBCDemo02 {
    // MySQL 5.x 版本的驱动加载
    // private final static String DRIVER = "com.mysql.jdbc.Driver";

    // MySQL8.0 版本的驱动加载
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";

    /*
     * 以下是数据库连接的信息,包括连接地址,用户名和密码
     * */
    private static final String URL = "jdbc:mysql://localhost:3306/db1";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "root";

    public static void main(String[] args) throws Exception {
        /*
         * 注册驱动
         * 这一步使用反射来注册驱动
         * 而且在MySQL5.0以后,这一句代码不用自己书写,在jar包中的META-INF/services/java.sql.Driver
         * Java会自动扫描这个文件,注册驱动
         * */
        Class.forName(DRIVER);

        /*
         * 获取一个数据库连接
         * connection对象有两个功能
         *   1. 获取执行SQL语句的对象
         *   2. 对JDBC事务进行管理
         * */
        Connection con = DriverManager.getConnection(URL, USERNAME, PASSWORD);

        // 自定义的SQL语句
        String querySql = "SELECT * FROM account";

        /*
         * 获取执行SQL的statement对象
         * 该方式执行SQL语句时,不会采用预编译,在执行大量的SQL语句时效率较低
         * */
        Statement stmt = con.createStatement();

        // 查询的结果集
        ResultSet resultSet1 = stmt.executeQuery(querySql);


        /*
         * 遍历结果集
         * 第一种方式,需要知道某列的属性和位于第几列
         * */
        while (resultSet1.next()) {

            // 获取第一列
            int aid = resultSet1.getInt(1);
            // 获取第二列
            String aname = resultSet1.getString(2);
            // 获取第三列
            int amoney = resultSet1.getInt(3);

            System.out.println(aid + ' ' + aname + ' ' + amoney);
        }
        System.out.println("====================");

        ResultSet resultSet2 = stmt.executeQuery(querySql);
        /*
        * 另一种获取结果的方式
        * */
        while (resultSet2.next()) {
            // 获取id
            int id = resultSet2.getInt("id");
            // 获取name
            String name = resultSet2.getString("name");
            // 获取money
            int money = resultSet2.getInt("money");
            System.out.println(id + ' ' + name + ' ' + money);
        }

        resultSet1.close();
        resultSet2.close();
        // 关闭资源
        stmt.close();
        con.close();
    }
}

2.2.2 问题补充

在测试示例代码的过程中,发现一个问题,使用同一个 stmt 执行方法获得 ResultSet 结果集后,需要不能连续、交替的访问,具体是:

  1. 起初像这样书写代码
Statement stmt = con.createStatement();

// 查询的结果集
ResultSet resultSet1 = stmt.executeQuery(querySql);
ResultSet resultSet2 = stmt.executeQuery(querySql);

/*
         * 遍历结果集
         * 第一种方式,需要知道某列的属性和位于第几列
         * */
while (resultSet1.next()) {

    // 获取第一列
    int aid = resultSet1.getInt(1);
    // 获取第二列
    String aname = resultSet1.getString(2);
    // 获取第三列
    int amoney = resultSet1.getInt(3);

    System.out.println(aid + ' ' + aname + ' ' + amoney);
}
/*
        * 另一种获取结果的方式
        * */
while (resultSet2.next()) {
    // 获取id
    int id = resultSet2.getInt("id");
    // 获取name
    String name = resultSet2.getString("name");
    // 获取money
    int money = resultSet2.getInt("money");
    System.out.println(id + ' ' + name + ' ' + money);
}

即在前面使用同一个 stmt 执行方法获得 ResultSet 结果集后,先访问 resultSet1,紧接着访问 resultSet2,看似代码没有问题,但是运行会报错,如下:
image.png
查找原因和处理如下:
错误:Operation not allowed after ResultSet closed_关闭的resultset-CSDN博客

修正后的代码书写顺序如:代码示例,先获取一个 ResultSet 访问完后,再获取另一个 resultSet。

2.3 prepareStatement 的基本使用

2.3.1 代码示例

package cn.edu.njust.jdbcdemo;

import java.sql.*;

public class TestJDBCDemo03 {
    // MySQL 5.x 版本的驱动加载
    // private final static String DRIVER = "com.mysql.jdbc.Driver";

    // MySQL8.0 版本的驱动加载
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";

    /*
     * 以下是数据库连接的信息,包括连接地址,用户名和密码
     * 开启预编译功能
     * */
    private static final String URL = "jdbc:mysql://localhost:3306/db1?useServerPrepStmts=true";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "root";

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

        // 自定义的SQL语句
        String querySql = "SELECT * FROM account WHERE name = ?";

        /*
         * 注册驱动
         * 这一步使用反射来注册驱动
         * 而且在MySQL5.0以后,这一句代码不用自己书写,在jar包中的META-INF/services/java.sql.Driver
         * Java会自动扫描这个文件,注册驱动
         * */
        Class.forName(DRIVER);

        /*
         * 获取一个数据库连接
         * connection对象有两个功能
         *   1. 获取执行SQL语句的对象
         *   2. 对JDBC事务进行管理
         * */
        Connection con = DriverManager.getConnection(URL, USERNAME, PASSWORD);

        // 获取 PreparedStatement 对象,用于执行SQL语句
        PreparedStatement ps = con.prepareStatement(querySql);
        // 传递参数
        ps.setString(1, "张三");
        ResultSet rs = ps.executeQuery();

        while (rs.next()) {
            int id = rs.getInt("id");
            String name = rs.getString("name");
            int money = rs.getInt("money");
            System.out.println(id + " " + name + " " + money);
        }

        rs.close();
        ps.close();
        con.close();
    }
}

2.3.2 补充说明

想要开启预编译的功能,必须在 URL 连接数据库的时候指定属性useServerPrepStmts=true,如果没有设置这个参数,那么使用PreparedStatement 也不会预编译;
具体查看预编译,需要选择配置 MySQL 的日志,查看日志文件可以观察预编译效果。

3 常用方法汇总

3.1 基本方法

** 方法原型**说明
public static Connection getConnection(String url, String user, String password)获取 connection 对象
Statement createStatement()获取执行 SQL 语句的普通对象
PreparedStatement prepareStatement(String sql)获取另一个执行 SQL 语句的对象
ResultSet executeQuery(String sql)执行查询语句
int executeUpdate()执行更新语句,返回受影响的行数

3.2 connection 的事务管理

** 方法原型 **** 说明 **
void setAutoCommit(boolean autoCommit)设置是否自动提交事务
void commit()提交事务
void rollback()回滚事务

3.3 ResultSet

** 方法原型**说明
boolean next()检查下一行是否有数据
int getInt(String columnLabel)根据列名获取某一列的值,且该字段在数据库中是 int 类型
String getString(String columnLabel)根据列名获取某一列的值,且该字段在数据库中是字符串
int getInt(int columnIndex)获取某一列的值,且该字段在数据库中是 int 类型

说明:
在 ResultSet 的方法中,获取返回值的有两种方式:

  1. 一种是在参数里面传递字符串,该字符串匹配数据库数据表中相应的字段名,较为直观;
  2. 在参数中传递入“第几列”,列数重 1 开始,这种方式不太直观,不推荐使用。

所有的方法都是形如:getXxx() 类型的,其中 Xxx 表示某种数据类型,与该字段在数据库数据表中定义的类型相匹配。
具体的使用可以看ResultSet代码示例

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拜见老天師

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值