一、简介
一般的企业做一个项目,一般也就只会用到一个数据库,一个数据源就可以了。但是考虑到分库操作后,需要对多个数据库、数据表进行CRUD操作,此时则需要在一个服务层操作数据时,必须保证全局事务能够正常进行。
二、配置
整体的项目布局:
主要的配置方式:
1、依赖引入
在一般的springboot+mybatis配置中,只需要额外添加pom依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jta-atomikos</artifactId></dependency>
2、yml数据源参数配置
server:port: 80mysql:datasource:test1: url: jdbc:mysql://localhost:3306/banana1?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: rootminPoolSize: 3maxPoolSize: 25maxLifetime: 20000borrowConnectionTimeout: 30loginTimeout: 30maintenanceInterval: 60maxIdleTime: 60test-query: select 1test2: url: jdbc:mysql://localhost:3306/banana2?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: rootminPoolSize: 3maxPoolSize: 25maxLifetime: 20000borrowConnectionTimeout: 30loginTimeout: 30maintenanceInterval: 60maxIdleTime: 60test-query: select 1
3、数据源的配置文件编写
主数据源配置,必须在配置的项中添加 @Primary 注解,其次无需配置事务,多个数据源的事务,交由 jta 统一处理。
import javax.sql.DataSource;import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
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.core.io.support.PathMatchingResourcePatternResolver;import com.alibaba.druid.pool.xa.DruidXADataSource;import cn.linkpower.config.po.DBConfig1;/*** 第一个数据源配置 --- 不配置事务* @author 76519**/
@Configuration
@MapperScan(basePackages="cn.linkpower.dao.mapper1",sqlSessionFactoryRef="sqlSessionFactory1")
public class DataSourceConfig1 {@Bean(name="dataSource1")@Primarypublic DataSource dataSource1(DBConfig1 dbConfig1) throws Exception {DruidXADataSource druidXADataSource = new DruidXADataSource();druidXADataSource.setUrl(dbConfig1.getUrl());druidXADataSource.setUsername(dbConfig1.getUsername());druidXADataSource.setPassword(dbConfig1.getPassword());//使用AtomikosDataSourceBean封装com.alibaba.druid.pool.xa.DruidXADataSourceAtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();atomikosDataSourceBean.setXaDataSource(druidXADataSource);//atomikos要求为每个AtomikosDataSourceBean编辑名称atomikosDataSourceBean.setUniqueResourceName("dataSource1");atomikosDataSourceBean.setPoolSize(5);return atomikosDataSourceBean;}@Bean(name = "sqlSessionFactory1")@Primarypublic SqlSessionFactory masterSqlSessionFactory(@Qualifier("dataSource1") DataSource masterDataSource)throws Exception {final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(masterDataSource);sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis/bunana1/*Mapper.xml"));return sessionFactory.getObject();}
}
从数据源配置,和主数据源配置一样,不需要配置事务,事务交由 jta 同一处理。
import javax.sql.DataSource;import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
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.core.io.support.PathMatchingResourcePatternResolver;import com.alibaba.druid.pool.xa.DruidXADataSource;import cn.linkpower.config.po.DBConfig2;/*** 第二个数据源配置 --- 不配置事务* @author 76519**/
@Configuration
@MapperScan(basePackages="cn.linkpower.dao.mapper2",sqlSessionFactoryRef="sqlSessionFactory2")
public class DataSourceConfig2 {@Bean(name="dataSource2")public DataSource dataSource1(DBConfig2 dbConfig2) throws Exception {DruidXADataSource druidXADataSource = new DruidXADataSource();druidXADataSource.setUrl(dbConfig2.getUrl());druidXADataSource.setUsername(dbConfig2.getUsername());druidXADataSource.setPassword(dbConfig2.getPassword());AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();atomikosDataSourceBean.setXaDataSource(druidXADataSource);atomikosDataSourceBean.setUniqueResourceName("dataSource2");atomikosDataSourceBean.setPoolSize(5);return atomikosDataSourceBean;}@Bean(name = "sqlSessionFactory2")public SqlSessionFactory masterSqlSessionFactory(@Qualifier("dataSource2") DataSource masterDataSource)throws Exception {final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(masterDataSource);sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis/bunana1/*Mapper.xml"));return sessionFactory.getObject();}
}
4、到此我们发现我分别引入了 DBConfig1.class 和 DBConfig2.class 这两个文件是什么呢?
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import lombok.Data;@Data
@Component
@ConfigurationProperties(prefix="mysql.datasource.test1")
public class DBConfig1 {private String driverClassName;private String url;private String username;private String password;
}
此处只写明了 DBConfig1.java 类的属性,同时使用了 Lombok 插件简化代码。
注意:
配置类文件要想能够使用 application.yml 或者 application.properties 文件中的配置信息,必须使用 @Component 注解注释,否则会出现
Parameter 0 of method dataSource1 in cn.linkpower.config.DataSourceConfig1 required a bean of type 'cn.linkpower.config.po.DBConfig1' that could not be found
类似的报错信息。
5、使用 jta 配置全局事务管理
import javax.transaction.UserTransaction;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 com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;/*** 整体事务配置* @author 76519**/
@Configuration
public class TXManagerConfig {@Bean(name = "transactionManager")@Primarypublic JtaTransactionManager regTransactionManager () {UserTransactionManager userTransactionManager = new UserTransactionManager();UserTransaction userTransaction = new UserTransactionImp();return new JtaTransactionManager(userTransaction, userTransactionManager);}
}
四、数据库文件配置
CREATE TABLE `users` (`userid` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`descript` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,`createtime` date DEFAULT NULL,PRIMARY KEY (`userid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;SET FOREIGN_KEY_CHECKS = 1;
注意:要想 java 事务能够回滚,必须保证数据库本身支持事务,即数据库表类型为 ENGINE = InnoDB
五、测试
本身还有很多代码,像业务层和控制层等,此处的重点不在全部的代码编写上,所以未添加完整代码,需要完整代码的在下面的github中可以clone和下载。
自己完善了单数据源的CRUD操作,事务正常,操作两个数据源的事务,设定异常也都能进行回滚操作。
六、完整代码下载
https://github.com/765199214/springboot-jta-mybatis
参考资料:《atomikos JTA/XA全局事务》、田守枝《4.0 atomikos JTA/XA全局事务》