配置多个数据源
yml配置两个数据源, act和business:
datasource:
act:
jdbcUrl: jdbc:mysql://localhost:3306/lmwy_product_act?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driverClassName: com.mysql.jdbc.Driver
business:
jdbc-url: jdbc:mysql://localhost:3306/lmwy_product?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
分别对应到两个数据源的配置:
package com.zhirui.lmwy.flow.config;
import org.activiti.spring.boot.AbstractProcessEngineAutoConfiguration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
/**
* 多数据源配置类
* 1.activiti数据库连接池
* 默认, 我们也无法去修改源码故默认
* 2.工作流业务数据库连接池
* 明确指定 businessDataSource
*/
@Configuration
public class FlowDatasourceConfig extends AbstractProcessEngineAutoConfiguration {
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.act")
@Qualifier("activitiDataSource")
public DataSource activitiDataSource() {
DataSource build = DataSourceBuilder.create().build();
return build;
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.business")
@Qualifier("businessDataSource")
public DataSource businessDataSource() {
DataSource build = DataSourceBuilder.create().build();
return build;
}
}
业务的springdata配置和事物管理器配置
package com.zhirui.lmwy.flow.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
/**
* 需要在配置类的上面加上@EnableJpaRepositories(basePackages={"dao层对应的包路径"}),这样jpa的dao层就注入进来了。结果启动spring boot 时发现,又有 Not a managed type: class ******的错误,经查询发现少了jpa entity路径的配置,在配置类的头部加上标记:@EntityScan("entity对应的包路径")
*/
@Configuration
@EnableJpaRepositories(
basePackages = {"com.zhirui.lmwy.flow.dao"},//代理的dao接口所在的包
entityManagerFactoryRef = "flowEntityManager",
transactionManagerRef = "flowTransactionManager"
)
public class JpaRepositoriesConfig {
@Autowired
private Environment env;
@Autowired
@Qualifier("businessDataSource")
private DataSource businessDataSource;
/**
* 创建entityManagerFactory工厂
*/
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean flowEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(businessDataSource);
//配置扫描的实体类包 ,否则报错:No persistence units parsed from {classpath*:META-INF/persistence.xml}
em.setPackagesToScan(new String[]{"com.zhirui.lmwy.flow.entity"});
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
// application.yaml配置文件的ddl-auto的值
// properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.format_sql", "true");
// application.yaml配置文件的database-platform的值
// properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
properties.put("hibernate.implicit_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
properties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
em.setJpaPropertyMap(properties);
return em;
}
/**
* 创建事务管理器
*/
@Primary
@Bean
public PlatformTransactionManager flowTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(flowEntityManager().getObject());
return transactionManager;
}
}
activiti中使用act数据源
package com.zhirui.lmwy.flow.config;
import lombok.AllArgsConstructor;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
/**
* Activiti 配置
*/
@Configuration
@AllArgsConstructor
public class ActivitiConfig {
private final DataSource dataSource;
@Autowired
@Qualifier("activitiDataSource")
private DataSource activitiDataSource;
@Bean
public SpringProcessEngineConfiguration getProcessEngineConfiguration() {
SpringProcessEngineConfiguration config =
new SpringProcessEngineConfiguration();
config.setDataSource(activitiDataSource);
config.setTransactionManager(activitiTransactionManager());
return config;
}
@Bean
public DataSourceTransactionManager activitiTransactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(activitiDataSource);
return transactionManager;
}
}
事物管理问题
service方法中只能一个生效,business的事务管理器是"flowTransactionManager",且配置了@Primary组件,所以下面的方法中使用的数据源就是"flowTransactionManager",那么针对数据源act的操作将无法回滚。
@Override
@Transactional(rollbackFor = Exception.class)
public void commit(FlowTaskInstance flowTaskInstance, String tenantId) throws Exception {
针对数据源act的操作
针对数据源business的操作
...
可以将@Transactional(rollbackFor = Exception.class) 指定事务管理器名称:
@Transactional(rollbackFor = Exception.class, transactionManager = "activitiTransactionManager")
那么方法上使用事物管理器为“activitiTransactionManager”, 但是针对数据源business的操作也将无法回滚。
我们需要一种分布式事物管理器。
* 如果是单应用单节点服务的多数据源事务,可以采用下午的 atomikos实现分布式市委 的方案。
* 如果是微服务架构,那么直接在改基础上整合seata即可!
atomikos实现分布式事务
先了解下spring中事务的管理器
PlatformTransactionManager顶级接口定义了最核心的事务管理方法,下面一层是AbstractPlatformTransactionManager抽象类,实现了PlatformTransactionManager接口的方法并定义了一些抽象方法,供子类拓展。最下面一层是2个经典事务管理器:
1.DataSourceTransactionmanager: 即本地单资源事务管理器,也是spring默认的事务管理器。
2.JtaTransactionManager: 即多资源事务管理器(又叫做分布式事务管理器),其实现了JTA规范,使用XA协议进行两阶段提交。
3. atomikos是JTA规范的具体技术,比较火和流行。
pom配置
<!-- 分布式事务管理 参考:https://blog.csdn.net/qq_35387940/article/details/103474353 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
yaml配置
#datasource
spring:
jta:
enabled: true
atomikos:
datasource:
act:
xa-properties.url: jdbc:mysql://localhost:3306/lmwy_product_act?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&pinGlobalTxToPhysicalConnection=true
xa-properties.user: root
xa-properties.password: 123456
xa-data-source-class-name: com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
unique-resource-name: act
max-pool-size: 10
min-pool-size: 1
max-lifetime: 10000
borrow-connection-timeout: 10000
business:
xa-properties.url: jdbc:mysql://localhost:3306/lmwy_product?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&pinGlobalTxToPhysicalConnection=true
xa-properties.user: root
xa-properties.password: 123456
xa-data-source-class-name: com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
unique-resource-name: business
max-pool-size: 10
min-pool-size: 1
max-lifetime: 10000
borrow-connection-timeout: 10000
配置多数据源和事务管理器
package com.zhirui.lmwy.flow.config;
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import org.activiti.spring.boot.AbstractProcessEngineAutoConfiguration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.jta.JtaTransactionManager;
import javax.sql.DataSource;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
/**
* 多数据源配置类
* 1.activiti数据库连接池
* 默认
* 2.工作流业务数据库连接池
* 明确指定 businessDataSource
*/
@Configuration
public class AtomikosConfig extends AbstractProcessEngineAutoConfiguration {
@Primary
@Bean(name = "actDatasource")
@Qualifier("actDatasource")
@ConfigurationProperties(prefix="spring.jta.atomikos.datasource.act")
public DataSource actDatasource() {
return new AtomikosDataSourceBean();
}
@Bean(name = "businessDatasource")
@Qualifier("businessDatasource")
@ConfigurationProperties(prefix="spring.jta.atomikos.datasource.business")
public DataSource businessDatasource() {
return new AtomikosDataSourceBean();
}
@Bean("jtaTransactionManager")
@Primary
public JtaTransactionManager activitiTransactionManager() throws SystemException {
UserTransactionManager userTransactionManager = new UserTransactionManager();
UserTransaction userTransaction = new UserTransactionImp();
return new JtaTransactionManager(userTransaction, userTransactionManager);
}
}
业务jpa绑定数据源
package com.zhirui.lmwy.flow.config;
import org.hibernate.engine.transaction.jta.platform.internal.AtomikosJtaPlatform;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import java.util.HashMap;
import java.util.Map;
/**
* 需要在配置类的上面加上@EnableJpaRepositories(basePackages={"dao层对应的包路径"}),这样jpa的dao层就注入进来了。结果启动spring boot 时发现,又有 Not a managed type: class ******的错误,经查询发现少了jpa entity路径的配置,在配置类的头部加上标记:@EntityScan("entity对应的包路径")
*/
@Configuration
@EnableJpaRepositories(
basePackages = {"com.zhirui.lmwy.flow.dao"},//代理的dao接口所在的包
entityManagerFactoryRef = "flowEntityManager",
transactionManagerRef = "jtaTransactionManager" //指定jta的事务管理器
)
public class JpaRepositoriesConfig {
@Autowired
private Environment env;
@Autowired
@Qualifier("businessDatasource")
private DataSource businessDataSource;
/**
* 创建entityManagerFactory工厂
*/
@Bean
// @Primary
public LocalContainerEntityManagerFactoryBean flowEntityManager() {
// *** jta 事务管理 ***
// AtomikosJtaPlatform.setTransactionManager(transactionManager);
// AtomikosJtaPlatform.setUserTransaction(userTransaction);
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
// *** jta datasource ***
em.setJtaDataSource(businessDataSource);
// em.setDataSource(businessDataSource);
// 配置扫描的实体类包 ,否则报错:No persistence units parsed from {classpath*:META-INF/persistence.xml}
em.setPackagesToScan(new String[]{"com.zhirui.lmwy.flow.entity"});
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
// *** jta datasource ***
properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
properties.put("javax.persistence.transactionType", "JTA");
// application.yaml配置文件的ddl-auto的值
// properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.format_sql", "true");
// application.yaml配置文件的database-platform的值
// properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
properties.put("hibernate.implicit_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
properties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
em.setJpaPropertyMap(properties);
return em;
}
}
activiti绑定数据源
package com.zhirui.lmwy.flow.config;
import lombok.AllArgsConstructor;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.jta.JtaTransactionManager;
import javax.sql.DataSource;
/**
* Activiti 配置
*/
@Configuration
@AllArgsConstructor
public class ActivitiConfig {
@Autowired
@Qualifier("actDatasource")
private DataSource activitiDataSource;
@Bean
@Primary
public SpringProcessEngineConfiguration getProcessEngineConfiguration(JtaTransactionManager jtaTransactionManager) {
SpringProcessEngineConfiguration config =
new SpringProcessEngineConfiguration();
config.setDataSource(activitiDataSource);
config.setTransactionManager(jtaTransactionManager);
return config;
}
}
测试:
执行任务提交方法,报错后都会进行回滚
public void commit(){
// 操作1:activiti执行任务,用的act数据源
taskService.complete(task.getId());
...
// 操作2:更新业务流程对象,保存业务对象,用的business数据源
flowInstanceDao.save(flowInstance);
...
int a = 100 / 0;
}
参考:
http://www.manongjc.com/detail/6-anzuqtksygeselj.html