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',
                            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',
                            autoReconnect: true,
                            autoReconnectForConnectionPools: true,
                            autoReconnectForPools: true],
                    minPoolSize: 1,
                    maxPoolSize: 50
            ]
        }
    }

     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

 

 

 

 

你可能感兴趣的:(transaction,grails,atomikos)