天天看点

SpringBoot中整合使用jFinal的ActiveRecord并支持多数据源和事务

在一些特殊场景,我们用到了Spring Boot,数据ORM层并没有使用流行的hibernate或mybatis,而采用了国内开源的jFinal中的ActiveRecord。不要问为什么不直接使用jFinal,就是这么个特殊需求。

在我们的需求中,需要使用多个数据源,并支持事务,下面是配置的详细过程。

依赖包的版本:

springboot 2.3.0.RELEASE

jFinal ActiveRecord 4.8

mysql connector java 5.1.20

druid 1.1.22

一、spring boot 整合jFinal的ActiveRecord

1.1 pom.xml中的包依赖:

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <springboot.version>2.3.0.RELEASE</springboot.version>
        <jfinal.activerecord.version>4.8</jfinal.activerecord.version>
        <mysql.version>5.1.20</mysql.version>
        <druid.version>1.1.22</druid.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.jfinal</groupId>
            <artifactId>activerecord</artifactId>
            <version>${jfinal.activerecord.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${springboot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${springboot.version}</version>
            <exclusions>
                <!-- 移除 tomcat 以使用 undertow -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
                <!-- 去掉logback配置 -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 使用实务 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>${springboot.version}</version>
            <exclusions>
                <!-- 去掉logback配置 -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
            <version>${springboot.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
            <version>${springboot.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>${springboot.version}</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>
    </dependencies>
           

1.2 新建 JfinalActiveRecordConfig java文件:

如下:

@Configuration
public class JfinalActiveRecordConfig {
    /**
     * 主数据源名称
     */
    private static final String MAIN_DATA_SOURCE_CONFIG = "main";


    @Bean
    @ConfigurationProperties("spring.datasource.main")
    public DruidDataSource masterDataSource(){
        return new DruidDataSource();
    }

    /**
     * 主数据源
     * @return
     */
    @Bean
    public ActiveRecordPlugin initMainActiveRecord() {
        ActiveRecordPlugin arp = new ActiveRecordPlugin(MAIN_DATA_SOURCE_CONFIG, masterDataSource());

        arp.addMapping("emp", Emp.class);

        arp.start();

        return arp;
    }
}
           

1.3 在application.yml配置文件里增加

spring:
  # 数据源配置
  datasource:
    main: #主数据源
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/csdn1
      username: root
      password: 123456
           

至此已经完成了spingboot和activerecord的整合,接下来加入多数据源的支持。

二、多数据源支持

2.1 在application.yml增加第二个数据源

spring:
  # 数据源配置
  datasource:
    main: #主数据源
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/csdn1
      username: root
      password: 123456
    biz: #业务数据源
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/csdn2
      username: root
      password: 123456
           

2.2 修改JfinalActiveRecordConfig,加入第二个数据源的支持

/**
     * 主数据源名称
     */
    private static final String MAIN_DATA_SOURCE_CONFIG = "main";

    /**
     * 业务数据源名称
     */
    private static final String BIZ_DATA_SOURCE_CONFIG = "biz";

    @Bean
    @ConfigurationProperties("spring.datasource.main")
    public DruidDataSource masterDataSource(){
        return new DruidDataSource();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.biz")
    public DruidDataSource bizDataSource(){
        return new DruidDataSource();
    }

    /**
     * 主数据源
     * @return
     */
    @Bean
    public ActiveRecordPlugin initMainActiveRecord() {
        ActiveRecordPlugin arp = new ActiveRecordPlugin(MAIN_DATA_SOURCE_CONFIG, masterDataSource());

        arp.addMapping("emp", Emp.class);

        arp.start();

        return arp;
    }

    /**
     * 业务数据源
     * @return
     */
    @Bean
    public ActiveRecordPlugin initBizActiveRecord() {
        ActiveRecordPlugin arp = new ActiveRecordPlugin(BIZ_DATA_SOURCE_CONFIG, bizDataSource());
// 第二个数据源如何和第一个数据有相同的表,则不需要重复映射,只需要在调用时指定数据源即可
//        arp2.addMapping("emp", Emp.class);

        arp.start();

        return arp;
    }
           

2.3 调用不同数据源的方式:

// 默认使用主数据源
Emp emp = Emp.dao.findById(1);
System.out.println(emp.getStr("name"));

// 调用其他数据源
Emp emp3 = new Emp().use("biz").findById(1);
System.out.println(emp3.getStr("name"));

// 也可以这样
Record emp2 = Db.use("biz").findFirst("select * from emp where id = 1");
System.out.println(emp2.getStr("name"));
           

三、数据库的事务支持

(注意:只支持同一数据源的事务)

修改JfinalActiveRecordConfig

@Configuration
public class JfinalActiveRecordConfig {
    /**
     * 主数据源名称
     */
    private static final String MAIN_DATA_SOURCE_CONFIG = "main";

    /**
     * 业务数据源名称
     */
    private static final String BIZ_DATA_SOURCE_CONFIG = "biz";

    @Bean
    @ConfigurationProperties("spring.datasource.main")
    public DruidDataSource masterDataSource(){
        return new DruidDataSource();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.biz")
    public DruidDataSource bizDataSource(){
        return new DruidDataSource();
    }

    /**
     * 主数据源
     * @return
     */
    @Bean
    public ActiveRecordPlugin initMainActiveRecord() {
        ActiveRecordPlugin arp = new ActiveRecordPlugin(MAIN_DATA_SOURCE_CONFIG, masterTransactionAwareDataSourceProxy());

        arp.addMapping("emp", Emp.class);
        arp.addMapping("emp_balance", EmpBalance.class);

        arp.start();

        return arp;
    }

    /**
     * 业务数据源
     * @return
     */
    @Bean
    public ActiveRecordPlugin initBizActiveRecord() {
        ActiveRecordPlugin arp = new ActiveRecordPlugin(BIZ_DATA_SOURCE_CONFIG, bizTransactionAwareDataSourceProxy());

//        arp2.addMapping("emp", Emp.class);

        arp.start();

        return arp;
    }

    /**
     * 设置数据源代理
     */
    @Bean
    public TransactionAwareDataSourceProxy masterTransactionAwareDataSourceProxy() {
        TransactionAwareDataSourceProxy transactionAwareDataSourceProxy = new TransactionAwareDataSourceProxy();
        transactionAwareDataSourceProxy.setTargetDataSource(masterDataSource());
        return transactionAwareDataSourceProxy;
    }

    @Bean
    public TransactionAwareDataSourceProxy bizTransactionAwareDataSourceProxy() {
        TransactionAwareDataSourceProxy transactionAwareDataSourceProxy = new TransactionAwareDataSourceProxy();
        transactionAwareDataSourceProxy.setTargetDataSource(bizDataSource());
        return transactionAwareDataSourceProxy;
    }

    /**
     * 设置事务管理
     */
    @Bean(name="mainDataSourceTransactionManager")
    public DataSourceTransactionManager masterDataSourceTransactionManager() {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(masterTransactionAwareDataSourceProxy());
        return dataSourceTransactionManager;
    }

    @Bean(name="bizDataSourceTransactionManager")
    public DataSourceTransactionManager bizDataSourceTransactionManager() {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(bizTransactionAwareDataSourceProxy());
        return dataSourceTransactionManager;
    }
}
           

之前的 ActiveRecordPlugin arp = new ActiveRecordPlugin(MAIN_DATA_SOURCE_CONFIG, masterDataSource());

换成了 

ActiveRecordPlugin arp = new ActiveRecordPlugin(MAIN_DATA_SOURCE_CONFIG, masterTransactionAwareDataSourceProxy());

接着在service中加入注解 @Transactional(transactionManager="mainDataSourceTransactionManager", rollbackFor = Exception.class)

如下:

@Service
public class EmpBalanceService {

    @Transactional(transactionManager="mainDataSourceTransactionManager", rollbackFor = Exception.class)
    public void addBalance() {
        // 这个应该不能保存进表中才对
        new EmpBalance().set("emp_id", 1)
                .set("balance", 10).save();

        Emp emp = Emp.dao.findById(1);

        // 这里故意写错,以测试实务
        emp.set("name", "超出长度的字符串用于测试错误").update();
    }
}
           

四、源码下载

源码的test里面有测试用例,sql文件夹下有建表语句,下载地址:https://github.com/Liamliu/spring-jfinal-activerecord

继续阅读