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对性能提升不会有显著作用。
相关推荐
超级架构师5 小时前
让 COBOL 长出 AI 大脑:原生 Agent 核心框架 praxo-core 深度剖析
c语言·开发语言·c++·人工智能·python·架构·ai编程
Non-existent9875 小时前
在 WPS/Excel 里做地理数据处理:功能市场 + 奥维 / Google Earth / QGIS 三种跳转实战
开发语言·javascript·ecmascript
甜到心里的蛋糕5 小时前
抖音评论自动回复机器人部署实践:无头 Chrome + DeepSeek + NapCat(2C2G 云主机)
java·jvm·chrome·python·抖音·deepseek
Thanks_ks6 小时前
从 0.1+0.2 不等于 0.3 说起:聊聊浮点数精度问题
java·python·数值计算·精度丢失·bigdecimal·浮点数·ieee754
xiaolang_8616_wjl6 小时前
c++游戏_宝可梦大世界(源代码)
开发语言·c++·游戏
(●—●)橘子……6 小时前
力扣 第525场周赛 练习理解
数据结构·python·学习·算法·leetcode
镜子AI6 小时前
镜子AI工程实践:用 Python 自动生成 GEO 问答语料与意图覆盖矩阵(零依赖)
人工智能·python·矩阵
长沙三为智能科技6 小时前
多平台内容矩阵怎么配合GEO:4层分工与3条去重规则
python·aigc·geo优化
ycydynq6 小时前
django 添加信息时,对外键设置条件
数据库·python·django
清水白石0086 小时前
Python 安装与环境配置:Windows、macOS、Linux 完整指南(第2集)
windows·python·macos