grails+atomikos 多事务管理
一、问题:
使用的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
上一篇: 分布式事务之xa:atomikos