欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

grails+atomikos 多事务管理

程序员文章站 2022-03-03 12:28:42
...

一、问题:

    使用的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