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 版本发布,系统全面优化升级,更快,更稳定! 导读 ⊙平台性能优化,系统更稳定,速度闪电般提升 ...
随机推荐
- android wifi obtainmessage sendmessage解析
obtainmessage 从message pool获取一个对象 sendmessage 将message插入message queue java中wait和notify是一对,wait进入睡眠等待 ...
- AutoLayout - VFL
其实很早就像整理出一份VFL的总结出来,觉得有很多东西可以写,但是真正去总结的时候发现,VFL也并没有很多东西,其实应该是在布局的过程中直接用VFL语句思考的过程比较难以形成,多数布局问题其实是在实际 ...
- css各项水平居中
当被设置元素为块状元素时用text-align:center就不起作用了,这也分两种情况,定宽块状元素和不定宽块状元素. 一.定宽块状元素:快状元素的宽度width为固定值,之后我们可以将左右marg ...
- oracle数据文件不小心被删除,导致对应的表空间删不掉的处理方法
问题现象:数据文件被无意删除,导致数据库表空间无法删除,并且重新刷表空间提示表空间已经存在:解决方法:1.oracle用户登录服务器,使用sysdba连接数据库 >sqlplus / as sy ...
- c语言到汇编的学习
[内存结构] C程序通过编译-汇编-连接,最后到可执行文件.载入内存有这几个部分: text:正文段,存放的是可执行的机器码段 data:存放初始化之后的全局变量和静态变量 bbs:存放未初始化的静态 ...
- 转帖:Python应用性能分析指南
原文:A guide to analyzing Python performance While it’s not always the case that every Python program ...
- Async Console Programs 异步控制台程序
如果你正在写一个控制台程序,你可能最终想要一个异步的main方法,像这样: class Program { static async void Main(string[] args) { ... } ...
- 再谈扩展方法,从string.IsNullOrEmpty()说起
string.IsNullOrEmpty()这个方法算得上是.net中使用频率最高的方法之一.此方法是string的一个静态方法,类似的静态方法在string这个类中还有很多.那么这样的方法作为静态方 ...
- C#中的线程四(System.Threading.Thread)
C#中的线程四(System.Threading.Thread) 1.最简单的多线程调用 System.Threading.Thread类构造方法接受一个ThreadStart委托,改委托不带参数,无 ...
- MySQL1:MySQL函数汇总
前言 MySQL提供了众多功能强大.方便易用的函数,使用这些函数,可以极大地提高用户对于数据库的管理效率,从而更加灵活地满足不同用户的需求.本文将MySQL的函数分类并汇总,以便以后用到的时候可以随时 ...