JDBC和Mybatis中的批处理
src目录下创建jdbc.properties mysql驱动5.1.6之后,只需要配置url,username,password
mysql 5.1.6之后可以无需Class.forName("com.mysql.jdbc.Driver") * 从jdk1.5之后可以通过配置文件来配置 * 会自动加载mysql驱动jar包下META-INF/services/java.sql.Driver文本中的类名去注册
JDBC工具类
package com.example;import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;/*** JDBC工具类* @author hrui* @date 2024/8/29 16:45*/
public class JDBCUtils {private static String url;private static String username;private static String password;private static Properties properties=new Properties();static{try {properties.load(new FileInputStream("src\\jdbc.properties"));url = properties.getProperty("url");username = properties.getProperty("username");password = properties.getProperty("password");}catch (IOException e){e.printStackTrace();//throw new RuntimeException(e);}}public static Connection getConnection() throws SQLException {//如果这样写,虽然不需要定义url,username,password,但是每次都要从properties获取url,username,password,效率不高 所以还是定义url,username,password 静态块里赋值//return DriverManager.getConnection(properties.getProperty("url"), properties.getProperty("username"), properties.getProperty("password"));return DriverManager.getConnection(url, username, password);}public static void closed(ResultSet rs, Statement stmt, Connection conn){try {if(rs!=null){rs.close();}if(stmt!=null){stmt.close();}if(conn!=null){conn.close();}}catch (SQLException e){e.printStackTrace();}}
}
以下执行代码数据一多,执行非常慢
package com.example;import java.sql.Connection;
import java.sql.PreparedStatement;/*** @author hrui* @date 2024/8/29 17:51*/
public class BathDemo {/*** 批处理* JDBC的批处理语句包括下面方法:* addBatch():添加需要批量处理的sql语句或参数* executeBatch():执行批处理语句* clearBatch():清空批处理语句* JDBC连接MySQL时,如果要使用批处理功能,再url中加参数?rewriteBatchedStatements=true* 批处理往往和PreparedStatement一起搭配使用,可以减少编译次数,效率提高* @param args*/public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;long start = System.currentTimeMillis();System.out.println("开始批处理:"+start);try {conn = JDBCUtils.getConnection();//conn.setAutoCommit(false);String sql="insert into admin2 values(null,?,?)";ps = conn.prepareStatement(sql);for (int i = 0; i < 100000; i++) {ps.setObject(1,"hrui"+i);ps.setObject(2,"123456");ps.executeUpdate();}// conn.commit();long end = System.currentTimeMillis();System.out.println("结束批处理:"+end);}catch (Exception e){e.printStackTrace();
// if(conn!=null){
// try {
// conn.rollback();
// } catch (Exception e1) {
// e1.printStackTrace();
// }
// }}finally {JDBCUtils.closed(null,ps,conn);}}
}
以下代码注意在url后面加
?rewriteBatchedStatements=true
JDBC中正确的批处理
package com.example;import java.sql.Connection;
import java.sql.PreparedStatement;/*** @author hrui* @date 2024/8/29 18:59*/
public class BathDemo2 {//正确的批处理方式public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;long start = System.currentTimeMillis();System.out.println("开始批处理:"+start);try {conn = JDBCUtils.getConnection();conn.setAutoCommit(false);String sql="insert into admin3 values(null,?,?)";ps = conn.prepareStatement(sql);//这里执行完 sql已经发送到DBMS进行编译for (int i = 0; i < 1000000; i++) {ps.setObject(1,"hrui"+i);ps.setObject(2,"123456");ps.addBatch();//循环一次 添加到批处理 添加的是参数if((i+1)%2000==0){ps.executeBatch();//满1000条执行一次ps.clearBatch();//执行完清空}}conn.commit();long end = System.currentTimeMillis();System.out.println("结束批处理:"+end);//所用时间秒 一般5000条这样数据大概耗时10秒System.out.println("所用时间:"+(end-start));}catch (Exception e){e.printStackTrace();
// if(conn!=null){
// try {
// conn.rollback();
// } catch (Exception e1) {
// e1.printStackTrace();
// }
// }}finally {JDBCUtils.closed(null,ps,conn);}}
}
DataSource是扩展版就是企业用的,我们现在所有的连接都是从DataSource里拿的 那么像HikariCP,druid,C3P0等数据库连接池都是实现DataSource的 默认我们在使用SpringBoot时候 使用的是HikariCP数据库连接池,也就是说DataSource其实就是数据库连接池的接口,在使用SpringBoot的时候,如果非不想使用默认或者druid,就是不想使用连接池,那么必须要指定一个DataSource的实现类,否则无法连接,Spring提供了一个DriverManagerDataSource,另外一种方式是你自己实现DataSource 然后指定type为自定义的.获取连接之后 还是通过Statement或者PreparedStatement去执行sql
下次是使用连接池的,可以有多个数据库连接去执行SQL操作 因为内存中循环肯定很快
insert into admin(name,pwd) values(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),
配置DriverManagerDataSource
那么再执行下面代码就是一个每次一个连接 insert into admin(name,pwd) values(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx),(xxx,xxx) 执行完关闭conn,
然后再打开 再执行 每次都只有一个连接在处理
速度会慢很多
另外 mybatis的这种执行方式 对于?rewriteBatchedStatements=true好像并没有多大作用,不过有应该是好的
对<foreach>
标签的影响
- 不需要设置
rewriteBatchedStatements=true
:因为你使用的是MyBatis的<foreach>
标签,这种方式本质上已经生成了一条带有多个值对的SQL语句,类似INSERT INTO admin (name, pwd) VALUES ('name1', 'pwd1'), ('name2', 'pwd2'), ...
,这与rewriteBatchedStatements=true
的效果类似。 - 因此,在这种情况下,加上
rewriteBatchedStatements=true
对性能提升不会有显著作用。