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 版本发布,系统全面优化升级,更快,更稳定! 导读 ⊙平台性能优化,系统更稳定,速度闪电般提升 ...
随机推荐
- STM32正交编码器驱动电机
1.编码器原理 什么是正交?如果两个信号相位相差90度,则这两个信号称为正交.由于两个信号相差90度,因此可以根据两个信号哪个先哪个后来判断方向. 这里使用了TI12模式,例如当T1上升 ...
- 再谈CSHELL对C程序员的价值
几个礼拜前,介绍了CSHELL.http://www.cnblogs.com/hhao020/p/4974542.html今天再试着介绍下,希望能有更多C程序员留意到它,从中获益. 很多年前,我在调试 ...
- 可变长度的Fibonacci数列
原题目: Write a recursive program that extends the range of the Fibonacci sequence. The Fibonacci sequ ...
- Python成长笔记 - 基础篇 (八)
socket编程 应用层: 表示层: 会话层: 传输层: 网络层: ip地址 数据链路层: MAC地址 物理层: 协议类型: TCP/IP协议:三次握手,四次断开 2. Socket 参数 ...
- .NET环境下上传和下载Word文件
一.上传Word文档或者其他文档 1.简单地上传文件的web服务方法如下 [WebMethod] public void UploadFile() { using (TransactionScope ...
- iOS 开发-- enum与typeof enum用法
一, 两者的用法 枚举类型定义用关键字enum标识,形式为: enum标识符 { 枚举数据表 }; enum用来定义一系列宏定义常量区别用,相当于一系列的#define ** **,当然它后面的标识符 ...
- 条件随机场matlab程序下载
19:44:23 1 http://www.cs.ubc.ca/~murphyk/Software/CRF/crf.html matlab程序包: 该条件随机场程序(CRF)是针对语句进行标注,mat ...
- 解决ViewPage中嵌套有ListView或者滑动手势等造成滑动的冲突
public class ViewPagerCompat extends ViewPager { //mViewTouchMode表示ViewPager是否全权控制滑动事件,默认为false,即不控制 ...
- Web应用数据库配置参数读取方法之一
jsp页面: <% //从配置中获取数据库驱动 String driver=application.getInitParameter("driver"); //从数据库中得到 ...
- 可在广域网部署运行的QQ高仿版 -- GG叽叽V3.2,增加离线消息、离线文件功能(源码)
(几句题外话:虽然就如何将GG发展为一个有商业价值的产品,我还没有很清晰明确的思路,但是从GG发布以来,通过GG认识了一些朋友,也接了一些小单子,赚了一点小钱.有了一点甜头,目前和2.3个好朋友一起做 ...