Java EE开发平台随手记4——Mybatis扩展3
接着昨天的Mybatis扩展——IDaoTemplate接口。
扩展9:批量执行
1、明确什么是批量执行
首先说明一下,这里的批量执行不是利用<foreach>标签生成一长串的sql字符串——这并不是真正的JDBC批量执行,我这里所说的批量是指在内核调用JDBC的addBatch、executeBatch等方法来实现的。类似下面的示例:
private void batchUpdate(Connection conn) throws SQLException {
PreparedStatement ps = null;
try{
String sql = " INSERT INTO BF_PARAM_ENUM_DATA "
+ " (PARAM_CODE,DATA_CODE,DATA_TEXT,DATA_PARAM,SEQNO,DES) "
+ " VALUES(?,?,?,?,?,?) ";
ps = conn.prepareStatement(sql);
for(int i = 0; i < 10; i++){
String random = RandomStringUtils.randomAlphabetic(8);
ps.setString(1, "TEST");//PARAM_CODE
ps.setString(2, random);//DATA_CODE
ps.setString(3, "数据" + random);//DATA_TEXT
ps.setString(4, "参数" + random);//DATA_PARAM
ps.setInt(5, i);//SEQNO
ps.setString(6, "");//MEMO
ps.addBatch();
}
int[] rs = ps.executeBatch();
System.out.print("batch insert : ");
for(int r : rs){
System.out.print(r+",");
}
System.out.println();
}finally{
try{
ps.close();
}catch(Exception e){}
}
}
2、批量执行的API
明白了批量执行的含义,接着看批量执行的API,再看一次IDaoTemplate接口中批量执行相关方法:
/**
* 执行批量:一个SQL执行多次
* @param sqlId SQL-ID
* @param parameters 参数对象数组
* @return 批量执行的影响记录数组
*/
public int[] executeBatch(String sqlId, List<?> parameters); /**
* 执行批量:一次执行多个SQL
* @param sqlIds 要执行的一组SQL-ID
* @return 批量执行的影响记录数组
*/
public int[] executeBatch(List<String> sqlIds); /**
* 执行批量:一次执行多个SQL
* @param sqlIds 要执行的一组SQL-ID
* @param parameters 参数对象数组
* @return 批量执行的影响记录数组
*/
public int[] executeBatch(List<String> sqlIds, List<?> parameters);
简单的理解,批量执行就是需要执行一组操作,要么是相同SQL不同的参数,要么就是不同的SQL(参数相同或不同都可以)。至于这些接口怎么实现,都比较简单,根据SqlSessionFactory,创建批量执行的会话,大概如下:
this.batchSqlSession = new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH);
然后和普通会话一样去执行update/insert/delete等操作,完了再调用批量会话的flushBatch方法返回结果即可。
这里有几个问题:
- 批量执行的事务:批量执行怎么保证事务,是否通过添加Transactional注解即可?事实上,事务注解也只是一个注解,它本身不会对程序运行起任何影响,只有解析这个注解的程序,根据注解属性执行不同的业务逻辑,才会真正体现注解的作用。而另外一方面,Spring注解必须要求调用方法是容器管理的Bean方法,而IDaoTemplate接口的实现类,并不一定是Spring容器中的bean。因此,这里通过事务注解并不能生效,而需要使用编程式事务。相关代码大致如下:
SqlSession batchSqlSession = getExecuteSqlSession();
PlatformTransactionManager txManager = this.getTransactinManager(dataSource);
if(null == txManager){
for(int i = 0, s = sqlIds.size(); i < s; i++){
batchSqlSession.update(SqlManager.getExecuteSqlId(sqlIds.get(i)), parameters==null?null : parameters.get(i));
}
List<BatchResult> r = batchSqlSession.flushStatements();
return resolveBatchResult(r);
}else{
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
for(int i = 0, s = sqlIds.size(); i < s; i++){
batchSqlSession.update(SqlManager.getExecuteSqlId(sqlIds.get(i)), parameters==null?null : parameters.get(i));
}
List<BatchResult> r = batchSqlSession.flushStatements();
txManager.commit(status);
return resolveBatchResult(r);
} catch (Throwable e) {
txManager.rollback(status);
throw Throw.createRuntimeException(e);
}
}
- 执行方式的混合:如果在一个服务方法中,既有一般方式的dao访问,也有批量执行的dao访问,同时还处于一个事务中。由于Mybatis自身的限制,不能在同一个事务中切换执行方法,因此会抛出异常。那怎么办呢?我是利用ThreadLocal变量保存当前线程是否开启了批量执行模式,然后提供了如下三个管理方法作为批量执行的支持接口:
3、批量执行模式管理API
/**
* 打开批量执行模式
*/
public void openBatchType(); /**
* 恢复打开批量执行模式之前的执行模式
*/
public void resetBatchType(); /**
* 获取批量执行结果
* @return
*/
public int[] flushBatch();
这样,在前面提到的情况下,可以在服务方法开始,就调用openBatchType打开批量执行方式,然后依次执行各sql脚本,调用flushBatch()获取批量执行结果,在finally快再将当前线程的执行方式切换为调用openBatchType方法之前的执行方式。
批量执行先说到这里,下一次再结合Dao接口的方法签名、Dao接口的自动代理子类等详细说明怎么使用自动代理调用批量执行。
扩展10:merge
在实际业务中,常常会有存在更新、不存在插入的数据库操作——merge,这种操作在Oracle数据库中可以直接利用SQL语法去更新,但是这种方法并不通用,更好的方法就是在Java中添加这个接口方法,但是添加接口方法也面临一个问题:怎么保证事务性?
其实在看过前面的批量执行的事务处理之后,就没什么秘密可言了,类似的使用编程式事务就可以了。
遗憾的是——由于平台组评审过于谨慎,这个接口并没有添加到IDaoTemplate中:
/**
* 存在更新、不存在插入
* @param insertSqlId 插入的SQL-ID
* @param updateSqlId 更新的SQL-ID
* @param parameter 参数对象
* @return 影响的记录条数
*/
public int merge(String insertSqlId, String updateSqlId, Object parameter);
类似于merge,还可进一步做更多的扩展,只是通用性比较低,这里举一个例子来说明一下:
/**
* 根据条件执行不同sql
* <p>
* <ul>
* <li> 将参数对象parameter作为根对象,对条件表达式caseExpression求值
* <li> 根据求值结果,从sqlId映射中查找对应的sqlId,如果没有找到,就返回默认的sqlId
* <li> 根据计算的sqlId和参数对象,执行数据库操作,并返回结果
* </ul>
* </p>
* @param caseExpression 条件表达式,可为OGNL/SpEL
* @param sqlIdMapping sqlId映射
* @param defaultSqlId 默认的sqlId
* @param parameter 参数对象
* @return 影响的记录条数
*/
public int switchExecute(String caseExpression, Map<String, String> sqlIdMapping, String defaultSqlId, Object parameter);
今天到此为止。
Java EE开发平台随手记4——Mybatis扩展3的更多相关文章
- Java EE开发平台随手记3——Mybatis扩展2
忙里偷闲,继续上周的话题,记录Mybatis的扩展. 扩展5:设置默认的返回结果类型 大家知道,在Mybatis的sql-mapper配置文件中,我们需要给<select>元素添加resu ...
- Java EE开发平台随手记2——Mybatis扩展1
今天来记录一下对Mybatis的扩展,版本是3.3.0,是和Spring集成使用,mybatis-spring集成包的版本是1.2.3,如果使用maven,如下配置: <properties&g ...
- Java EE开发平台随手记6——Mybatis扩展4
这篇博客中来说一下对Mybatis动态代理接口方式的扩展,对于Mybatis动态代理接口不熟悉的朋友,可以参考前一篇博客,或者研读Mybatis源码. 扩展11:动态代理接口扩展 我们知道,真正在My ...
- Java EE开发平台随手记5——Mybatis动态代理接口方式的原生用法
为了说明后续的Mybatis扩展,插播一篇广告,先来简要说明一下Mybatis的一种原生用法,不过先声明:下面说的只是Mybatis的其中一种用法,如需要更深入了解Mybatis,请参考官方文档,或者 ...
- Java EE开发平台随手记1
过完春节以来,一直在负责搭建公司的新Java EE开发平台,所谓新平台,其实并不是什么新技术,不过是将目前业界较为流行的框架整合在一起,做一些简单的封装和扩展,让开发人员更加易用. 和之前负责具体的项 ...
- Java EE开发课外事务管理平台
Java EE开发课外事务管理平台 演示地址:https://ganquanzhong.top/edu 说明文档 一.系统需求 目前课外兴趣培训学校众多,完善,但是针对课外兴趣培训学校教务和人事管理信 ...
- Java EE开发环境——MyEclipse2017破解 和 Tomcat服务器配置
Java EE开发,我们可以搭建如下开发环境: 底层运行环境:jdk 和 jre. Web服务器:Tomcat 后台数据库:SQL Server 可视化集成开发环境:MyEclipse Java EE ...
- JEECG 3.7.1 版本发布,企业级JAVA快速开发平台
JEECG 3.7.1 版本发布,企业级JAVA快速开发平台 ---------------------------------------- Version: Jeecg_3.7.1项 目: ...
- JEECG 4.0 版本发布,JAVA快速开发平台
JEECG 4.0 版本发布,系统全面优化升级,更快,更稳定! 导读 ⊙平台性能优化,系统更稳定,速度闪电般提升 ...
随机推荐
- 关于netty
现在我们一般使用应用程序或者链接库相互进行通信.例如,我们经常通过一个使用http协议的客户端链接库,从网站服务器中获得信息,并且通过网站服务,调用一些远程程序. 然而,一些通用协议或者它们的实现方式 ...
- 『TCP/IP详解——卷一:协议』读书笔记——17
2013-08-27 15:37:42 6.5 ICMP端口不可达差错 端口不可达报文是ICMP差错报文的一种,它是ICMP不可达报文中的一种,以此来看一看ICMP差错报文中所附加的信息.使用UDP来 ...
- linux(centos)源码安装git
最近使用一个开源库,部署的的时候需要用git克隆依赖库.刚好系统没安装git.就尝试了源码安装git. 源码下载地址:http://codemonkey.org.uk/projects/git-sna ...
- MFC程序中使用调试宏ASSERT()、ASSERT_VALID()、VERIFY()和TRACE()的区别
其实这篇文章说的很明白了:http://dev.gameres.com/Program/Other/DebugMacro.htm 结论如下: 1.ASSERT()测试它的参数,若参数为0,则中断执行并 ...
- 正则表达式统计java代码空白行,有效代码
import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import jav ...
- maven环境终于可以了
说说maven可以后小小的体会吧,虽然还没有用maven运行过工程,体会是pom.xml中的dependency属性可以帮助管理项目中的jar包,只要在这里配置下需要的jar包,保存后就会自动从中央仓 ...
- JavaScript 基础第六天
一.引言 前面我们介绍了有关于内置对象的很多很多的API,讲道理得话如果想彻底的掌握那一定要经过一定的代码段沉淀下.大家可以想象一下,既然在程序中有很多的内置对象供我们使用,那我们是不是也可以定义一些 ...
- PHP学习第一天笔记——php的基本语法
1.php嵌入到html中的方式 (1) <?php.....?> 标准风格(推荐) (2)<script language="php">......< ...
- 微软connect教程系列—EntityFramework7(三)
随着Asp.NET5的开源,以及跨平台,ORM框架EF7也与时俱进,支持asp.net core,也支持关系型数据库和非关系型数据库,以及在linux和mac上跨平台使用. 下面演示的即通过使用E ...
- 使用Immutable优化复制注意事项
这是Orleans中对于序列化检查类型是否支持Orleans内置的高速序列化时,使用Immutable<>包装和类型声明时,有ImmutableAttribute,效果是一样的.所以无需重 ...