grails+atomikos 多事务管理 grailsatomikostransaction
一、问题:
使用的grails开发项目当配置多个数据源时,非默认数据源的事务会失效
二、探索:
网上找到spring同样存在这样的问题,必须使用atomikos
三、atomikos插件
grails官网上有atomikos插件,但是好多年不用了,并且这个插件是在grails1.3.5基础上开发的,并且依赖的jar也下载不下来了,更不要谈安装插件了
最后尝试将插件代码下载下来,重新修改,修改需要注意几个问题
1、grails版本要改为你项目用的版本
2、BuildConfig文件中dependencies引用到的jar包都下载不下来,需要自己手动下载相应的jar包,注意版本问题,并且注释掉dependencies里面的所有compile
3、修改文件AtomikosGrailsPlugin.groovy中
System.setProperty 'com.grails.atomikos.icatch.service',UserTransactionServiceFactory.name
的“grails”去掉
上面的三个问题修改后,就可以打自己的atomikos的插件了,见附件
四、grails+atomikos
在项目中安装自己打好的插件,开始配置了
1、BuildConfig.groovy
plugins { ... compile ':atomikos:1.0' }
2、Config.groovy
grails.plugin.atomikos.uts = [ 'com.atomikos.icatch.console_file_name' : 'tm.out', 'com.atomikos.icatch.log_base_name' : 'tmlog', 'com.atomikos.icatch.tm_unique_name' : 'tsTransactionManager', 'com.atomikos.icatch.console_log_level' : 'ERROR', 'com.atomikos.icatch.log_base_dir' : 'target/atomikos', 'com.atomikos.icatch.output_dir' : 'target/atomikos', 'com.atomikos.icatch.force_shutdown_on_vm_exit': 'true']
3、数据源配置DataSource.groovy
development { //默认数据源 dataSource { pooled = true url = 'jdbc:mysql://localhost:3309/main driverClassName = 'com.mysql.jdbc.Driver' properties { username = "root" password = "root" maxIdle = "30" maxWait = "10000" maxActive = -1 } xaConfig = [ driverClassName: 'com.mysql.jdbc.jdbc2.optional.MysqlXADataSource', driverProperties: [ URL: 'jdbc:mysql://localhost:3309/main', user: 'root', password: 'root', pinGlobalTxToPhysicalConnection: "true", autoReconnect: true, autoReconnectForConnectionPools: true, autoReconnectForPools: true], minPoolSize: 1, maxPoolSize: 50 ] } //非默认数据源 dataSource_ts { pooled = true url = 'jdbc:mysql://localhost:3309/ts?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull' driverClassName = 'com.mysql.jdbc.Driver' properties { username = "root" password = "root" maxIdle = "30" maxWait = "10000" maxActive = -1 } xaConfig = [ driverClassName: 'com.mysql.jdbc.jdbc2.optional.MysqlXADataSource', driverProperties: [ URL: 'jdbc:mysql://localhost:3309/ts?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull', user: 'root', password: 'root', pinGlobalTxToPhysicalConnection: "true", autoReconnect: true, autoReconnectForConnectionPools: true, autoReconnectForPools: true], minPoolSize: 1, maxPoolSize: 50 ] } }
pinGlobalTxToPhysicalConnection: "true"其中这个一定要配置,否则会有问题,请参见http://www.atomikos.com/Documentation/KnownProblems#MySQL_XA_bug
4、测试代码
1)分别创建两个类MainTask使用默认数据源,Task使用ts
2)Service代码
class TaskService { //默认为true,当改为false抛任何异常,数据都会保存 static transactional = true; def saveTask() { // Task t = Task.get(92l);//更新 Task t = new Task(); t.taskName = t.taskName + "testdhw_noclick" t.save(); saveOpenTask() } Object saveOpenTask() { OpenTask ot = new OpenTask(); ot.taskName = ot.taskName + "open_task_test" ot.save(); throw new RuntimeException("save task fail!"); } }
验证事务已经起作用了,当 transactional =false,即使最后抛出RunTimeException异常,数据同样会保存
五、另外一个问题
多数据源事务失效问题引起的另外一个“分页查询”问题也同时得到了解决
grails分页的确很方便,但是当你所查询的类不是默认数据源时,悲剧了,你必须将得transactional =false才能正常查询,(ps:曾经啊,,,曾经就让这个问题悄悄溜过去了)
控制类代码如下
class TaskController { TaskService taskService; def list = { def resultMap = [:]; try { PagedResultList pageList = taskService.queryTaskByCondition(params); //Task为非默认数据源,在不使用插件的情况下,在事务开启状态时,控制类里获取总数方法会报connection is close!!! if (pageList.getTotalCount() == 0) { resultMap << ['total': 0]; resultMap << ['rows': []]; } else { resultMap << ['total': pageList.getTotalCount()]; resultMap << ['rows': pageList.list]; } } catch (Exception e) { log.error("查询任务列表出错:" + e.getMessage(), e); resultMap << ['total': 0]; resultMap << ['rows': []]; } render(resultMap as JSON); } }
service代码如下
class TaskService { static transactional = true; PagedResultList queryTaskByCondition(GrailsParameterMap params) { def currPage = params.page ? Integer.valueOf(params.page) : 1; def pageSize = params.rows ? Integer.valueOf(params.rows) : 10; def offset = (currPage - 1) * pageSize; params.max = pageSize; params.offset = offset; def searchParam = { order('updateTime', "desc"); order('id', "desc"); } def criteria = Task.createCriteria(); PagedResultList pageResult = criteria.list(params, searchParam); return pageResult; } }
六、相关查阅资料
1、spring存在同样的问题解决方案
http://loftor.com/archives/spring-mybatis-tomcat-jta.html
2、插件依赖的jar下载,注意版本注意版本注意版本(重要的事说三遍)
http://mvnrepository.com/artifact/com.atomikos
3、官方插件文档
http://grails-plugins.github.io/grails-atomikos/docs/manual/guide/2%20Getting%20Started.html
4、github插件源码
http://grails-plugins.github.io/grails-atomikos/
5、jtapropertie介绍
http://www.atomikos.com/Documentation/JtaProperties