能不能在FOR循环中执行SQL?
JDBC最基础的For循环处理SQL的方式 以及执行时间
package javaee.net.cn.jdbc;
import java.sql.*;
public class TestTransaction { public static void main(String[] args) {
Long startTime = System.currentTimeMillis();
Connection conn = null;
PreparedStatement stmt = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager
.getConnection("jdbc:mysql://192.168.1.105:3306/sunkun?user=root&password=********");
String sql = "insert into user (name) values (?)";
stmt = conn.prepareStatement(sql);
for(int i=0;i<1000;i++){
stmt.setString(1,"test"+i);
stmt.execute();
}
stmt.executeBatch();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch(SQLException e) {
e.printStackTrace();
try {
if(conn != null)
{
conn.rollback();
conn.setAutoCommit(true);
}
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally {
try {
if(stmt != null)
stmt.close();
if(conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
Long endTime = System.currentTimeMillis();
Long time = endTime-startTime;
System.err.println("一千条数据所需要的时间"+time);//一千条数据所需要的时间48262
} }
for循环一千条SQL时间是48262ms
设置setAutoCommit(false) FOR循环执行
package javaee.net.cn.jdbc;
import java.sql.*;
public class TestTransaction { public static void main(String[] args) {
Long startTime = System.currentTimeMillis();
Connection conn = null;
PreparedStatement stmt = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager
.getConnection("jdbc:mysql://192.168.1.105:3306/sunkun?user=root&password=*******");
String sql = "insert into user (name) values (?)";
stmt = conn.prepareStatement(sql);
conn.setAutoCommit(false);
for(int i=0;i<1000;i++){
stmt.setString(1,"test"+i);
stmt.execute();
}
conn.commit();
conn.setAutoCommit(true);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch(SQLException e) {
e.printStackTrace();
try {
if(conn != null)
{
conn.rollback();
conn.setAutoCommit(true);
}
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally {
try {
if(stmt != null)
stmt.close();
if(conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
Long endTime = System.currentTimeMillis();
Long time = endTime-startTime;
System.err.println("一千条数据所需要的时间"+time);//一千条数据所需要的时间578
} }
578ms 厉害吧 同样是for循环执行SQL 效率提高了百倍
setAutoCommit介绍
void setAutoCommit(boolean autoCommit)
throws SQLException
- 将此连接的自动提交模式设置为给定状态。如果连接处于自动提交模式下,则它的所有 SQL 语句将被执行并作为单个事务提交。否则,它的 SQL 语句将聚集到事务中,直到调用
commit方法或rollback方法为止。默认情况下,新连接处于自动提交模式。提交发生在语句完成时。语句完成的时间取决于 SQL 语句的类型:
- 对于 DML 语句(比如 Insert、Update 或 Delete)和 DDL 语句,语句在执行完毕时完成。
- 对于 Select 语句,语句在关联结果集关闭时完成。
- 对于
CallableStatement对象或者返回多个结果的语句,语句在所有关联结果集关闭并且已获得所有更新计数和输出参数时完成。
注:如果在事务和自动提交模式更改期间调用此方法,则提交该事务。如果调用
setAutoCommit而自动提交模式未更改,则该调用无操作(no-op)。 -
-
- 参数:
autoCommit- 为true表示启用自动提交模式;为false表示禁用自动提交模式- 抛出:
SQLException- 如果发生数据库访问错误,在参与分布式事务的同时调用 setAutoCommit(true),或者在关闭的连接上调用此方法- 另请参见:
getAutoCommit()
测试批处理一千条数据的时间
package javaee.net.cn.jdbc;
import java.sql.*;
public class TestTransaction { public static void main(String[] args) {
Long startTime = System.currentTimeMillis();
Connection conn = null;
PreparedStatement stmt = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager
.getConnection("jdbc:mysql://192.168.1.105:3306/sunkun?user=root&password=******");
String sql = "insert into user (name) values (?)";
stmt = conn.prepareStatement(sql);
conn.setAutoCommit(false);
for(int i=0;i<1000;i++){
stmt.setString(1,"test"+i);
stmt.addBatch();
}
stmt.executeBatch();
conn.commit();
conn.setAutoCommit(true);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch(SQLException e) {
e.printStackTrace();
try {
if(conn != null)
{
conn.rollback();
conn.setAutoCommit(true);
}
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally {
try {
if(stmt != null)
stmt.close();
if(conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
Long endTime = System.currentTimeMillis();
Long time = endTime-startTime;
System.err.println("一千条数据所需要的时间"+time);//一千条数据所需要的时间502
} }看到如果设置了批处理502 比for循环执行一千条SQL578快了一点(和网络延迟 GC有关 其实只要不让事物自动提交 for循环处理SQL和批处理的效率差不多)
实验Mybatis
不管什么dao框架 底层都是jdbc
工作中用的最多的是Mybatis
下面对Mybatis做一下For循环处理SQL的实验
@Test
public void testForDao(){
//获取配置在Spring中的SqlSessionFactory实例
SqlSessionFactory sqlSessionFactory =SpringContextHolder.getBean(SqlSessionFactory.class);
//
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH,false);
Long startTime = System.currentTimeMillis();
for(int i=0;i<1000;i++){
ClientClassify classify = new ClientClassify();
String name = "testFor_"+i;
classify.setClientClassify(name);
classify.setOwnerId(2154L);
classify.setCreateBy("kk6");
classify.setLastUpdateBy("kk6");
classify.setCreateDate(new Date());
classify.setLastUpdateDate(new Date());
classify.setClientType(ClientType.customer);
classify.setIsDel(false);
classify.setVersion(1);
classify.setDelDate(new Date());
sqlSession.insert("com.ydcfo.common.model.crm.ClientClassifyMapper.insert",classify);
}
sqlSession.flushStatements();
sqlSession.commit();
sqlSessionFactory.openSession(true);
Long endTime = System.currentTimeMillis();
System.err.println("1000条数据时间"+(endTime-startTime));
}下面直接说结论
sqlSessionFactory.openSession(ExecutorType.BATCH,false); 时间:47010
sqlSessionFactory.openSession(ExecutorType.SIMPLE,false); 时间:50357
sqlSessionFactory.openSession(ExecutorType.REUSE,false); 时间:50398
sqlSessionFactory.openSession(ExecutorType.BATCH,true); 时间:49178
在Mybatis中,我试图和JDBC一样 通过设置 不让事物自动提交,发现效率并没有提高
/**
* @author Clinton Begin
*/
public enum ExecutorType {
SIMPLE, REUSE, BATCH
}用 Mybatis BATCH的ExecutorType执行器,FOR循环执行SQL的效率一样很低
总结,在原始的JDBC中,我们可以通过设置AutoCommit来提高FOR循环中执行SQL的效率
但是在Mybatis中 这样行不通,至于原因,还未知(我估计是Mybatis内部优化不好)。
所以在工作中 Mybatis不能FOR循环执行SQL 一定要拼装成一个SQL(通过字符串拼接SQL)然后在执行(也就是Mybatis的批处理)
这是一篇介绍事物的文章 我觉得核心的一句话是where条件中用到的num 会收到其他未commit事物的影响 这也是乐观锁为什么可以防止并发的原因
补充:
经过测试发现, SqlSession openSession(ExecutorType execType, boolean autoCommit);
当第一个参数是ExecutorType.BATCH的时候 第二个参数是true还是false 每循环一次都不会落地到数据库
当第一个参数不是ExecutorType.BATCH的时候 第二个参数是true还是false 每循环一次都会落地到数据库 即使 还没有 sqlSession.commit() 我个人认为这是Mybatis的bug
不过这个细节不会影响我们日常的开发,因为事物都交给了Spring去处理。
能不能在FOR循环中执行SQL?的更多相关文章
- C#和Java中执行SQL文件脚本的代码(非常有用)
原文:C#和Java中执行SQL文件脚本的代码(非常有用) 我们在做程序的时候有事后会涉及到利用sql文件 直接执行,可是在sql文件中有很多注释,我们要一句一句的执行首先必须的得把sql文件解析 去 ...
- EF中执行sql语句,以及事务
EF to sql string sql = "select T_Task.BSID,T_Task.CloseDate,T_Task.CompleteDate,T_Task.CloseUse ...
- [转]在EntityFramework6中执行SQL语句
本文转自:http://www.cnblogs.com/wujingtao/p/5412329.html 在上一节中我介绍了如何使用EF6对数据库实现CRDU以及事务,我们没有写一句SQL就完成了所有 ...
- 在EntityFramework6中执行SQL语句
在EntityFramework6中执行SQL语句 在上一节中我介绍了如何使用EF6对数据库实现CRDU以及事务,我们没有写一句SQL就完成了所有操作.这一节我来介绍一下如何使用在EF6中执行SQL语 ...
- 在phpmyadmin中执行sql语句出现的错误:Unknown storage engine 'InnoDB'
在phpmyadmin中执行sql语句出现的错误:Unknown storage engine 'InnoDB' 解决方法:解决方法: 1.关闭MySQL数据库 2 ...
- SSIS中执行SQL任务组件参数传递的问题
原文:SSIS中执行SQL任务组件参数传递的问题 症状: 执行SQL任务,传递参数到子查询中,执行报错. 错误: 失败,错误如下:"无法从使用 sub-select 查询的 SQL 语句中派 ...
- .net(C#)在Access数据库中执行sql脚本
自己写的一个工具类,主要是业务场景的需要. 主要有两个功能: ①执行包含sql语句的字符串 ②执行包含sql语句的文件 调用方式 /// <summary> /// 执行sql语句 /// ...
- 在EF中执行SQL语句(转载)
在EF中执行SQL语句 你可能要问,我用EF不就为了避免写SQL吗?如果要写SQL我不如直接用ADO.NET得了.话虽然这么说没错,可有些时候使用EF操作数据还是有一些不方便,例如让你根据条件删除 ...
- Shell脚本中执行sql语句操作mysql的5种方法【转】
对于自动化运维,诸如备份恢复之类的,DBA经常需要将SQL语句封装到shell脚本.本文描述了在Linux环境下mysql数据库中,shell脚本下调用sql语句的几种方法,供大家参考.对于脚本输出的 ...
随机推荐
- Spring Cloud Eureka的集群配置(六)
1.再次创建2个Eureka工程 工程名:microservicecloud-eureka-7002 工程名:microservicecloud-eureka-7003 2.pom.xml文件 < ...
- linux wc使用详解
转载:https://www.cnblogs.com/peida/archive/2012/12/18/2822758.html Linux系统中的wc(Word Count)命令的功能为统计指定文件 ...
- 每天五分钟,玩转Docker。-Day2
Day2 镜像仓库(Docker registry) Docker registry是存储容器镜像的仓库,用户可以通过Docker client 与Docker register 进行通信,以此来完成 ...
- Filter的介绍及使用
转:http://blog.csdn.net/zhaozheng7758/article/details/6105749 一.Filter的介绍及使用 什么是过滤器? 与Servlet相似,过滤器是一 ...
- 项目管理-工作量评估 Manday
People's suggestion, 逻辑有待验证 1. Project sponsor - a new request 2. Study the related issue, to define ...
- JSP·随笔
1.简介 > HTML - HTML擅长显示一个静态的网页,但是不能调用Java程序. > Servlet - Servlet擅长调用Java程序和后台进 ...
- ili 一例业务系统框架
ili即ilinei的简称,像名字一样,是ILINEI团队的内部项目简化而来.2017年金鸡报晓,我们为同行送来了一个简单.快速.轻量级的PHP开源系统,它的任务当然也是唯一的任务,就是提高WEB开发 ...
- 找出数组中最大值and索引
找出数组中的最大值和和最大值的索引位置..... 第一中方法: /** * 找出数组中最大值和最大值的索引 * @param args */ public static void main(Strin ...
- mybatis-plus 3.X 配置
官网配置参数说明地址:https://mp.baomidou.com/config/#logicdeletevalue 本地配置:yml mybatis-plus: mapper-locations: ...
- Codeforces Round #548 (Div. 2) E 二分图匹配(新坑) or 网络流 + 反向处理
https://codeforces.com/contest/1139/problem/E 题意 有n个学生,m个社团,每个学生有一个\(p_i\)值,然后每个学生属于\(c_i\)社团, 有d天,每 ...