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 版本发布,系统全面优化升级,更快,更稳定! 导读 ⊙平台性能优化,系统更稳定,速度闪电般提升 ...
随机推荐
- JDBC学习笔记(2)
上一篇博客简单介绍了JDBC连接的简单知识,下面就详细介绍.李勇老师对JDBC连接有很好的比喻: 首先加载驱动,其实就是一些类,就是jar包,要加载到classpath里面的.实际的程序,服务和数据库 ...
- PCB设计检查表
PCB设计检查表 一.确保PCB网表与原理图描述的网表一致 二.布局大致完成后需检查 外形尺寸 确认外形图是最新的 确认外形图已考虑了禁止布线区.传送边.挡条边.拼板等问题 确认PCB 模板是最新的 ...
- 【Thinking in Java】Java Callable的使用
Callable<>和Runable类似,都是用于Java的并发执行. 唯一的区别是,Runable的run方法的返回是void,而Callable的call方法是有返回值的. call方 ...
- Thread and ThreadPool
C#中Thread与ThreadPool的比较 Thread类,一次使用一个线程,来创建和删除线程.这种方式建立和删除线程是很昂贵的(cpu密集型). Threadpool类 对于大多数的情况下是使用 ...
- Android多线程
final Handler handler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { ...
- PHPExcel(1.8.0) 帮助代码
以下代码不能直接运行,只是提供各种方法调用写法. require_once dirname(__FILE__) . '/Classes/PHPExcel.php'; $objPHPExcel = ne ...
- CPU与内存的关系
至今才对cpu和内存有一定了解了,下面有几个比喻的理解,很形象呦~ 1# 例如你要吃东西时,硬盘是锅,内存是碗,CPU是你,浅显点就是这样子了~ 2# 例如电脑是企业,内存是车间,cpu是生产线,硬盘 ...
- php面试题及答案收藏(转)
php面试题及答案收藏(这套试题是在网上看到的,不知作者是谁) 基础题 1.表单中 get与post提交方法的区别? 答:get是发送请求HTTP协议通过url参数传递进行接收,而post是实体数据, ...
- 【整理】--【字符设备】分配设备号register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev()
(1) 分配设备编号,注册设备与注销设备的函数均在fs.h中声明,如下: extern int register_chrdev_region(dev_t,unsigned int,const char ...
- hdu2604(递推,矩阵快速幂)
题目链接:hdu2604 这题重要的递推公式,找到公式就很easy了(这道题和hdu1757(题解)类似,只是这道题需要自己推公式) 可以直接找规律,推出递推公式,也有另一种找递推公式的方法:(PS: ...