JDBC的“那些事“之应当注意的问题(下)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: JDBC的“那些事“之应当注意的问题(下)

五、怎么执行批处理?

5.1 准备测试数据

👉 创建数据表t_department

CREATE TABLE `t_department` (
  `did` int NOT NULL AUTO_INCREMENT COMMENT '部门编号',
  `dname` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '员工名称',
  `description` varchar(200) DEFAULT NULL COMMENT '员工简介',
  PRIMARY KEY (`did`),
  UNIQUE KEY `dname` (`dname`)
) ENGINE=InnoDB AUTO_INCREMENT=5009 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

5.2 问题演示

👉概述:

批处理就是执行一组sql语句,大多数情况下都是批量执行insert语句。

👉目的:

为了提高效率

👉批处理方法原理:

  1. 每次设置完 “?” 之后,先不执行,先攒着pst.addBatch();
    把要执行sgL先添加到批处理队伍中,在缓存中攒着。
  2. 等所有的"?"设置完了之后,一口气执行

💡tips:

如果缓冲区溢满的时候,它也会自动执行一次。

如果缓冲区没有溢出,那么会最后一起执行。

❓怎么开启批处理?

MySQL服务器端,默认批处理功能没有开启。需要通过参数告知mysql服务器,开启批处理功能。

  1. 在url后面再加一个参数 rewriteBatchedStatements=true
//告知mysql服务器开启批处理功能
 String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC&rewriteBatchedStatements=true";
  1. PreparedStatement对象调用
    A:addBatch()
    B:executeBatch()
  2. 不要把values写错value,因为value效率太低了

案例:给部门表批量添加1000条部门测试信息

👉ps: 部门表由于之前测试过案例程序,自增长量为5009

⚪A.不使用批处理功能插入数据:

代码演示如下:

@Test
    public void test01() throws SQLException {
        long start=System.currentTimeMillis();
        //开始连接数据库,相当于网络编程中的socket
        String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC";
        Connection root = DriverManager.getConnection(url, "root", "123456");
        String sql="INSERT INTO t_department VALUES(null,?,?)";//t_employee中第一个字段是自增工长列,可不用填写
        PreparedStatement pst = root.prepareStatement(sql);//把sql语句装进去;相当于通过PreparedStatement对象把sql发送给MySQL服务端
        for (int i = 1; i <=1000 ; i++) {
            pst.setObject(1,"测试部门"+i);
            pst.setObject(2,"测试部门简介"+i);
            pst.executeUpdate();
        }
        //释放资源
        pst.close();
        root.close();
        long end=System.currentTimeMillis();
        //耗时:100141毫秒
        System.out.println("耗时:"+(end-start));
    }

⚪B.Java程序开了pst.executeBatch();//执行批处理命令,但是没告知MySQL服务器要开启批处理功能,不起作用,非常慢,必须二者才行。

不信?请看如下代码演示:

@Test
    //感觉还是很慢
    public void test02() throws SQLException {
        long start=System.currentTimeMillis();
        //开始连接数据库,相当于网络编程中的socket
        String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC";
        Connection root = DriverManager.getConnection(url, "root", "123456");
        String sql="INSERT INTO t_department VALUES(null,?,?)";//t_employee中第一个字段是自增工长列,可不用填写
        PreparedStatement pst = root.prepareStatement(sql);//把sql语句装进去;相当于通过PreparedStatement对象把sql发送给MySQL服务端
        for (int i = 1; i <=1000 ; i++) {
            pst.setObject(1,"测试部门"+i);
            pst.setObject(2,"测试部门简介"+i);
            pst.addBatch();//把所有的sql语句放到批处理队伍中,于缓存中先攒一波
        }
        pst.executeBatch();//执行批处理命令
        //释放资源
        pst.close();
        root.close();
        long end=System.currentTimeMillis();
        //MySQL没有开启批处理功能,耗时:86547
        System.out.println("耗时:"+(end-start));
    }

⚪ C.使用批处理功能并用values的sql:

代码演示如下:

@Test
    //sql语句使用values,感觉飞快,坐火箭一样
    public void test03() throws SQLException {
        long start=System.currentTimeMillis();
        //开始连接数据库,相当于网络编程中的socket
        String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC&rewriteBatchedStatements=true";
        Connection root = DriverManager.getConnection(url, "root", "123456");
        String sql="INSERT INTO t_department VALUES(null,?,?)";//t_employee中第一个字段是自增工长列,可不用填写
        PreparedStatement pst = root.prepareStatement(sql);//把sql语句装进去;相当于通过PreparedStatement对象把sql发送给MySQL服务端
        for (int i = 1; i <=1000 ; i++) {
            pst.setObject(1,"测试部门"+i);
            pst.setObject(2,"测试部门简介"+i);
            pst.addBatch();//把所有的sql语句放到批处理队伍中,于缓存中先攒一波
        }
        pst.executeBatch();//执行批处理命令
        //释放资源
        pst.close();
        root.close();
        long end=System.currentTimeMillis();
        //MySQL开启批处理后,耗时:1614毫秒
        System.out.println("耗时:"+(end-start));
    }

⚪ D.使用批处理但使用value的sql:

代码演示如下:

@Test
    //其他代码不变,观察sql语句insert 表名 value(值列表)的执行效率
    //value效率太低了
    public void test04() throws SQLException {
        long start=System.currentTimeMillis();
        //开始连接数据库,相当于网络编程中的socket
        String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC&rewriteBatchedStatements=true";
        Connection root = DriverManager.getConnection(url, "root", "123456");
        String sql="INSERT INTO t_department VALUE(null,?,?)";//t_employee中第一个字段是自增工长列,可不用填写
        PreparedStatement pst = root.prepareStatement(sql);//把sql语句装进去;相当于通过PreparedStatement对象把sql发送给MySQL服务端
        for (int i = 1; i <=1000 ; i++) {
            pst.setObject(1,"测试部门"+i);
            pst.setObject(2,"测试部门简介"+i);
            pst.addBatch();//把所有的sql语句放到批处理队伍中,于缓存中先攒一波
        }
        pst.executeBatch();//执行批处理命令
        //释放资源
        pst.close();
        root.close();
        long end=System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));//MySQL开启批处理后,耗时:87386
    }

👉以上四种方式运行耗时差异图如下所示:


六、怎么执行事务?

6.1 准备测试数据

👉创建数据表t_department

CREATE TABLE `t_department` (
  `did` int NOT NULL AUTO_INCREMENT COMMENT '部门编号',
  `dname` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '员工名称',
  `description` varchar(200) DEFAULT NULL COMMENT '员工简介',
  PRIMARY KEY (`did`),
  UNIQUE KEY `dname` (`dname`)
) ENGINE=InnoDB AUTO_INCREMENT=5009 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

6.2 问题演示

👉如何管理事务?

  1. mysql默认是自动提交事务,每执行一条语句成功后,自动提交。
    需要开启手动提交模式。

Connection连接对象.setAutoCommit(false);

//取消自动提交模式,开始手动提交模式

  1. sql执行成功,别忘了提交事务

Connection连接对象.commit();

  1. sql执行失败,回滚事务

Connection连接对象.rollback();

案例:修改部门表中的 两条记录,故意分开用两个update语句来修改。

然后希望这两条update语句,构成一个事务,要么一起成功,然后提交,要么一起失败,还原回滚。

update t_department set description = ‘xx’ where did = 2;

update t_department set description = ‘yy’ where did = 3;

代码演示如下:

👉 ①故意写错sql2语句的,观察是否回滚

@Test
    //故意写错sql2语句的,观察是否回滚;观察结果:发生了回滚
    public void test01() throws SQLException {
        //开始连接本机的MySQL服务端
        String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC";
        Connection root = DriverManager.getConnection(url, "root", "123456");
        root.setAutoCommit(false);//不开启这个,更新失败后不能回滚,
        String sql1="update t_department set description = 'xx' where did = 2";
        String sql2="update t_department set description = 'yy' what did = 3";
        PreparedStatement pst1 = root.prepareStatement(sql1);//把sql1语句装进PreparedStatement对象中,准备发送
        PreparedStatement pst2 = root.prepareStatement(sql2);//把sql2语句装进PreparedStatement对象中,准备发送
        try {
            pst1.executeUpdate();//真正发送sql1语句
            pst2.executeUpdate();//真正发送sql2语句
            System.out.println("都更新成功了");
            root.commit();//提交事务
        } catch (SQLException e) {
            System.out.println("有一个更新失败了");
            root.rollback();//回滚事务
        }
        pst1.close();
        root.close();
    }

💡观察结果: 事务回滚成功

👉②更正之前写错的sql2语句,观察是否提交

@Test
    //更正之前写错的sql2语句,观察是否提交;观察结果:发生了提交,数据已经更新
    public void test02() throws SQLException {
        //开始连接本机的MySQL服务端
        String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC";
        Connection root = DriverManager.getConnection(url, "root", "123456");
        root.setAutoCommit(false);//不开启这个,更新失败后不能回滚,
        String sql1="update t_department set description = 'xx' where did = 2";
        String sql2="update t_department set description = 'yy' where did = 3";
        PreparedStatement pst1 = root.prepareStatement(sql1);//把sql1语句装进PreparedStatement对象中,准备发送
        PreparedStatement pst2 = root.prepareStatement(sql2);//把sql2语句装进PreparedStatement对象中,准备发送
        try {
            pst1.executeUpdate();//真正发送sql1语句
            pst2.executeUpdate();//真正发送sql2语句
            System.out.println("都更新成功了");
            root.commit();//提交事务
        } catch (SQLException e) {
            System.out.println("有一个更新失败了");
            root.rollback();//回滚事务
        }
        pst1.close();
        pst2.close();
        //这里习惯上,在连接对象close之前,把连接重新设置为自动提交模式
        root.setAutoCommit(true);//虽然在本例中没有意义,但在数据库连接池中,连接会重复使用,在关闭(还给连接池时)需求恢复自动提交模式
        root.close();
    }

💡观察结果: 事务提交成功

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3月前
|
SQL 数据库
jdbc10
jdbc10
22 1
|
XML 存储 Java
什么是jdbc?为什么要用jdbc
什么是jdbc?为什么要用jdbc
|
SQL Oracle 数据可视化
|
缓存 druid Java
JDBC2.0
JDBC2.0在1.0版本java.sql.*上增加了一个扩展包javax.sql.*包,这个包里面包含两个接口: 1.DataSource,数据源。 2.ConnectionPoolDataSource,数据池。 DataSource的源码可以看到,这个接口的作用就是拿来获取connection连接的。 各个数据库厂商只负责实现connection, 至于获取connection,在JDBC1.0里面是由DriverManager来负责的。 JDBC2.0版本之所新增DataSource,原因很简单——数据库连接池。1.0版本时每用一次connection都需要重新new,而da
131 0
JDBC2.0
|
SQL 人工智能 数据可视化
JDBC的“那些事“之应当注意的问题(上)
JDBC的“那些事“之应当注意的问题(上)
|
SQL 存储 关系型数据库
JDBC的“那些事“之应当注意的问题(中)
JDBC的“那些事“之应当注意的问题(中)
|
Java 关系型数据库 MySQL
JDBC1.0
JDBC是JAVA十三大规范之一,由JAVA定义顶级接口API,各个数据库厂商去实现。常用的四大接口——Driver,DriverManager,Connection,Statement。 这里先给出结论: 所谓的JAVA制定标准,就是指JAVA给出接口,厂家各自去实现。 比如Driver,Connection,Statement等都是有接口,有实现。 通过向DriverManager注册不同Driver来确定Connection,Statement的类型
66 0
|
SQL 安全 Java
33 JDBC实用详解
info 💡 概念:JDBC是java提供的一套用于操作数据库的接口API:java程序员只需要面向这套接口编程即可;
93 0
|
SQL 安全 Java
|
SQL druid Java