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?的更多相关文章

  1. C#和Java中执行SQL文件脚本的代码(非常有用)

    原文:C#和Java中执行SQL文件脚本的代码(非常有用) 我们在做程序的时候有事后会涉及到利用sql文件 直接执行,可是在sql文件中有很多注释,我们要一句一句的执行首先必须的得把sql文件解析 去 ...

  2. EF中执行sql语句,以及事务

    EF to sql string sql = "select T_Task.BSID,T_Task.CloseDate,T_Task.CompleteDate,T_Task.CloseUse ...

  3. [转]在EntityFramework6中执行SQL语句

    本文转自:http://www.cnblogs.com/wujingtao/p/5412329.html 在上一节中我介绍了如何使用EF6对数据库实现CRDU以及事务,我们没有写一句SQL就完成了所有 ...

  4. 在EntityFramework6中执行SQL语句

    在EntityFramework6中执行SQL语句 在上一节中我介绍了如何使用EF6对数据库实现CRDU以及事务,我们没有写一句SQL就完成了所有操作.这一节我来介绍一下如何使用在EF6中执行SQL语 ...

  5. 在phpmyadmin中执行sql语句出现的错误:Unknown storage engine 'InnoDB'

    在phpmyadmin中执行sql语句出现的错误:Unknown storage engine 'InnoDB' 解决方法:解决方法:             1.关闭MySQL数据库       2 ...

  6. SSIS中执行SQL任务组件参数传递的问题

    原文:SSIS中执行SQL任务组件参数传递的问题 症状: 执行SQL任务,传递参数到子查询中,执行报错. 错误: 失败,错误如下:"无法从使用 sub-select 查询的 SQL 语句中派 ...

  7. .net(C#)在Access数据库中执行sql脚本

    自己写的一个工具类,主要是业务场景的需要. 主要有两个功能: ①执行包含sql语句的字符串 ②执行包含sql语句的文件 调用方式 /// <summary> /// 执行sql语句 /// ...

  8. 在EF中执行SQL语句(转载)

    在EF中执行SQL语句   你可能要问,我用EF不就为了避免写SQL吗?如果要写SQL我不如直接用ADO.NET得了.话虽然这么说没错,可有些时候使用EF操作数据还是有一些不方便,例如让你根据条件删除 ...

  9. Shell脚本中执行sql语句操作mysql的5种方法【转】

    对于自动化运维,诸如备份恢复之类的,DBA经常需要将SQL语句封装到shell脚本.本文描述了在Linux环境下mysql数据库中,shell脚本下调用sql语句的几种方法,供大家参考.对于脚本输出的 ...

随机推荐

  1. 【转】.NET程序员提高效率的70多个开发工具

    原文:.NET程序员提高效率的70多个开发工具 工欲善其事,必先利其器,没有好的工具,怎么能高效的开发出高质量的代码呢?本文为各ASP.NET 开发者介绍一些高效实用的工具,涉及SQL 管理,VS插件 ...

  2. SpringMVC 使用PUT请求遇到的问题小结

    最近在使用REST风格的URL进行CURD操作的学习过程中 发现使用PUT请求时候提交表单进行修改操作 报错:Request method 'PUT' not supported 在网上查找资料发现是 ...

  3. localhost换成127.0.0.1和本机IP打不开本地项目了的问题

    点击桌面右下角的小三角, iis express右键—>显示所有应用程序—>点击网站名称,配置文件路径,找到配置文件,以记事本打开, 按照configuration--system.app ...

  4. PHP 利用PHPExcel 文件导入(也可保存到本地或者服务器)、导出

    首先需要去官网http://www.php.cn/xiazai/leiku/1491,下载后只需要Classes目录下的文件即可. 1.PHPExcel导出方法实现过程 1 2 3 4 5 6 7 8 ...

  5. EasyPR源码剖析(5):车牌定位之偏斜扭转

    一.简介 通过颜色定位和Sobel算子定位可以计算出一个个的矩形区域,这些区域都是潜在车牌区域,但是在进行SVM判别是否是车牌之前,还需要进行一定的处理.主要是考虑到以下几个问题: 1.定位区域存在一 ...

  6. C#获取文件夹下面的所有文件名

    String path = @"X:\xxx\xxx";   //第一种方法 var files = Directory.GetFiles(path, "*.txt&qu ...

  7. sha1 算法源码

    原来指望sha1 这种烂大街的算法 不会出什么幺蛾子 结果<linux C编程实战Code>bt章节的sha1 代码 我在linux和windows下的结果不一样 然后用了哈希工具查看了下 ...

  8. Finance财务软件帮助

    我们在凭证录入的时候可以使用这些快捷键增加效率: 单元格 快捷键 功能 摘要 F7 弹出摘要选择框 科目 F7 弹出科目选择框 科目 F8 弹出扩展字段编辑界面 金额 Esc/小键盘- 清空 金额 = ...

  9. Effective C++ 笔记:条款 34 实现继承和接口继承

    Differentiate between inheritance of interface and inheritance of implementation. 行为含义 声明一个pure virt ...

  10. 2019.03.29 bzoj5463: [APIO2018] 铁人两项(圆方树+树形dp)

    传送门 题意简述:给你一张无向图,问你满足存在从a−>b−>ca->b->ca−>b−>c且不经过重复节点的路径的有序点对(a,b,c)(a,b,c)(a,b,c) ...