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: [email protected] 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(); }