1.使用的Atomikos:一个为Java平台提供增值服务的并且开源类事务管理器。
2.数据源配置:
@Configuration
public class DatasourceConfig {
@Bean(destroyMethod = "close", name = DataSources.MASTER_DB)
@Primary
@Autowired
public DataSource dataSource(Environment env) throws Exception {
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
Properties prop = build(env, "spring.datasource.");
ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
ds.setUniqueResourceName(DataSources.MASTER_DB);
ds.setPoolSize(5);
ds.setXaProperties(prop);
return ds;
}
@Autowired
@Bean(destroyMethod = "close", name = DataSources.CLUSTER_DB)
public DataSource datasourceCluster(Environment env) {
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
Properties prop = build(env, "spring.datasourceCluster.");
ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
ds.setUniqueResourceName(DataSources.CLUSTER_DB);
ds.setPoolSize(5);
ds.setXaProperties(prop);
return ds;
}
@Bean("clusterJdbcTemplate")
public JdbcTemplate busJdbcTemplate(@Qualifier("businessDataSource") DataSource ds) {
return new JdbcTemplate(ds);
}
private Properties build(Environment env, String prefix) {
Properties prop = new Properties();
prop.put("url", env.getProperty(prefix + "url"));
prop.put("username", env.getProperty(prefix + "username"));
prop.put("password", env.getProperty(prefix + "password"));
prop.put("driverClassName", env.getProperty(prefix + "driverClassName", ""));
prop.put("initialSize", env.getProperty(prefix + "initialSize", Integer.class));
prop.put("maxActive", env.getProperty(prefix + "maxActive", Integer.class));
prop.put("minIdle", env.getProperty(prefix + "minIdle", Integer.class));
prop.put("maxWait", env.getProperty(prefix + "maxWait", Integer.class));
prop.put("poolPreparedStatements", env.getProperty(prefix + "poolPreparedStatements", Boolean.class));
prop.put("maxPoolPreparedStatementPerConnectionSize",
env.getProperty(prefix + "maxPoolPreparedStatementPerConnectionSize", Integer.class));
prop.put("maxPoolPreparedStatementPerConnectionSize",
env.getProperty(prefix + "maxPoolPreparedStatementPerConnectionSize", Integer.class));
prop.put("validationQuery", env.getProperty(prefix + "validationQuery"));
prop.put("validationQueryTimeout", env.getProperty(prefix + "validationQueryTimeout", Integer.class));
prop.put("testOnBorrow", env.getProperty(prefix + "testOnBorrow", Boolean.class));
prop.put("testOnReturn", env.getProperty(prefix + "testOnReturn", Boolean.class));
prop.put("testWhileIdle", env.getProperty(prefix + "testWhileIdle", Boolean.class));
prop.put("timeBetweenEvictionRunsMillis",
env.getProperty(prefix + "timeBetweenEvictionRunsMillis", Integer.class));
prop.put("minEvictableIdleTimeMillis", env.getProperty(prefix + "minEvictableIdleTimeMillis", Integer.class));
prop.put("filters", env.getProperty(prefix + "filters"));
return prop;
}
}
public interface DataSources {
String MASTER_DB = "masterDB";
String CLUSTER_DB = "clusterDB";
}
@Configuration
public class DruidMonitorConfig {
@Bean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
reg.addInitParameter("loginUsername", "admin");
reg.addInitParameter("loginPassword", "123456");
return reg;
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions",
"*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
filterRegistrationBean.addInitParameter("profileEnable", "true");
filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION");
return filterRegistrationBean;
}
}
配置扫描mybatis扫描mapper:
@Configuration
@MapperScan(basePackages = { "com.xxx.mapper.cluster" }, sqlSessionFactoryRef = "clusterSqlSessionFactory")
public class MybatisCluster {
@Autowired
@Qualifier(DataSources.CLUSTER_DB)
private DataSource clusterDB;
@Bean(name = "clusterSqlSessionFactory")
@ConfigurationProperties(prefix = "mybatisCluster")
public SqlSessionFactory sqlSessionFactoryCluster() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(clusterDB);
return sqlSessionFactoryBean.getObject();
}
@Bean("clusterJdbcTemplate")
public JdbcTemplate sysJdbcTemplate() {
return new JdbcTemplate(clusterDB);
}
@Bean
public SqlSessionTemplate sqlSessionTemplateCluster() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactoryCluster());
return template;
}
}
@Configuration
@MapperScan(basePackages = { "com.xxx.mapper.master" }, sqlSessionFactoryRef = "SqlSessionFactory")
public class MybatisConfig {
@Autowired
@Qualifier(DataSources.MASTER_DB)
private DataSource masterDB;
@Bean
@Primary
@ConfigurationProperties(prefix = "mybatis")
public SqlSessionFactory SqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(masterDB);
return sqlSessionFactoryBean.getObject();
}
@Bean("masterJdbcTemplate")
public JdbcTemplate sysJdbcTemplate() {
return new JdbcTemplate(masterDB);
}
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(SqlSessionFactory()); // 使用上面配置的Factory
return template;
}
}
事务管理:
@Configuration
@ComponentScan
@EnableTransactionManagement
public class TransactionManagerConfig {
@Bean(name = "userTransaction")
public UserTransaction userTransaction() throws Throwable {
UserTransactionImp userTransactionImp = new UserTransactionImp();
userTransactionImp.setTransactionTimeout(10000);
return userTransactionImp;
}
@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
public UserTransactionManager atomikosTransactionManager() throws Throwable {
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setForceShutdown(false);
return userTransactionManager;
}
@Bean(name = "transactionManager")
@DependsOn({ "userTransaction", "atomikosTransactionManager" })
public JtaTransactionManager transactionManager() throws Throwable {
UserTransaction userTransaction = userTransaction();
JtaTransactionManager manager = new JtaTransactionManager(userTransaction, atomikosTransactionManager());
return manager;
}
@Bean(name = "transactionInterceptor")
public TransactionInterceptor transactionInterceptor(PlatformTransactionManager platformTransactionManager) {
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
// 事物管理器
transactionInterceptor.setTransactionManager(platformTransactionManager);
Properties transactionAttributes = new Properties();
// 新增
transactionAttributes.setProperty("insert*", "PROPAGATION_REQUIRED,-Throwable");
transactionAttributes.setProperty("save*", "PROPAGATION_REQUIRED,-Throwable");
// 修改
transactionAttributes.setProperty("update*", "PROPAGATION_REQUIRED,-Throwable");
// 删除
transactionAttributes.setProperty("delete*", "PROPAGATION_REQUIRED,-Throwable");
// 查询
transactionAttributes.setProperty("select*", "PROPAGATION_REQUIRED,-Throwable,readOnly");
transactionInterceptor.setTransactionAttributes(transactionAttributes);
return transactionInterceptor;
}
// 代理到ServiceImpl的Bean
@Bean
public BeanNameAutoProxyCreator transactionAutoProxy() {
BeanNameAutoProxyCreator transactionAutoProxy = new BeanNameAutoProxyCreator();
transactionAutoProxy.setProxyTargetClass(true);
transactionAutoProxy.setBeanNames("*ServiceImpl");
transactionAutoProxy.setInterceptorNames("transactionInterceptor");
return transactionAutoProxy;
}
}
3.application.yml
spring:
mail:
host:
username:
password:
properties:
mail:
smtp:
auth: true
datasource:
# 数据源基本配置
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/xxxx?characterEncoding=UTF-8&useSSL=false
type: com.alibaba.druid.pool.DruidDataSource
# 数据源其他配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
validationQueryTimeout: 10000
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
# 合并多个DruidDataSource的监控数据
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
datasourceCluster:
# 数据源基本配置
username: admin
password: admin@aliyun.com
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/xxx?characterEncoding=UTF-8&useSSL=false
type: com.alibaba.druid.pool.DruidDataSource
# 数据源其他配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
validationQueryTimeout: 10000
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# 合并多个DruidDataSource的监控数据
useGlobalDataSourceStat: true
#jta相关参数配置
jta:
log-dir: classpath:tx-logs
transaction-manager-id: txManager
mybatis:
# 指定全局配置文件位置
config-location: classpath:mybatis/mybatis-config.xml
# 指定sql映射文件位置
mapper-locations: classpath:com/xxx/mapper/master/*.xml
mybatisCluster:
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:com/xxx/mapper/cluster/*.xml
4.maven配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
5.测试:
@Override
public ServerResponseDTO<String> saveTest() {
User user = new User();
user.setUsername("test");
user.setUpdateTime(new Date());
user.setCreateTime(user.getUpdateTime());
user.setPassword("123456");
userMapper.insertSelective(user);
TbUser tbUser = new TbUser();
tbUser.setUsername("test");
tbUser.setPassword("123456");
tbUser.setUpdated(new Date());
tbUser.setCreated(new Date());
int a = tbUserMapper.insertSelective(tbUser);
if(a < 1)
throw new RuntimeException("1");
return ServerResponseDTO.createBySuccess();
}