在一些特殊场景,我们用到了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