SpringBoot多数据源的基本配置
配置文件将数据源连接信息配置好,注意配置数据源的name属性不要重复
-
创建配置类,构建DataSource、DataSourceTransactionManager、SqlSessionFactory、SqlSessionTemplate等对象。题主使用的springboot版本为2.2.6,要求在构建这些对象时添加
@Primary
注解,题主理解的是类似于指定主数据源。示例代码如下
package com.demo.config; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; 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 org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; @Configuration // 扫描 Mapper 接口并容器管理 @MapperScan(basePackages = Ds1DataSourceConfig.PACKAGE,sqlSessionFactoryRef = "ds1SqlSessionFactory")// public class Ds1DataSourceConfig { // 精确到 firm 目录,以便跟其他数据源隔离 static final String PACKAGE = "com.demo.mapper.ds1"; /**本数据源扫描的mapper路径*/ static final String MAPPER_LOCATION = "classpath:com/demo/mapper/ds1/*.xml"; @Value("${spring.datasource.ds1.url}") private String url; @Value("${spring.datasource.ds1.username}") private String user; @Value("${spring.datasource.ds1.password}") private String password; @Value("${spring.datasource.ds1.driver-class-name}") private String driverClass; /**创建数据源*/ @Bean(name = "ds1DataSource") @Primary public DataSource firmDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driverClass); dataSource.setUrl(url); dataSource.setUsername(user); dataSource.setPassword(password); return dataSource; } /**创建事务管理器*/ @Bean(name = "ds1TransactionManager") @Primary public DataSourceTransactionManager firmTransactionManager() { return new DataSourceTransactionManager(firmDataSource()); } /**创建SessionFactory*/ @Bean(name = "ds1SqlSessionFactory") @Primary public SqlSessionFactory firmSqlSessionFactory() throws Exception { final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(firmDataSource()); //支持属性使用驼峰的命名,mapper配置不需要写字段与属性的配置,会自动映射。 org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); //使用jdbc的getGeneratedKeys获取数据库自增主键值 configuration.setUseGeneratedKeys(true); //使用列别名替换列名 select user as User configuration.setUseColumnLabel(true); //-自动使用驼峰命名属性映射字段 userId user_id configuration.setMapUnderscoreToCamelCase(true); //打印sql--需要调试时打开 //configuration.setLogImpl(StdOutImpl.class); sessionFactory.setConfiguration(configuration); //设置mapper配置文件 sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources(Ds1DataSourceConfig.MAPPER_LOCATION)); return sessionFactory.getObject(); } /**创建SqlSessionTemplate*/ @Bean(name = "ds1SqlSessionTemplate") @Primary public SqlSessionTemplate firmSqlSessionTemplate(@Qualifier("ds1SqlSessionFactory") SqlSessionFactory sessionFactory) { return new SqlSessionTemplate(sessionFactory); } }
其余数据源配置类似,注意
@Primary
只在一个数据源中配置即可。多数据源mapper扫描位置尽量区分开 -
创建测试类进行测试,这里只贴serviceImpl,mapper以及xml自动生成即可
ds1:service
package com.demo.service.Impl; import com.demo.entity.ds1.Demo; import com.demo.mapper.ds1.DemoMapper; import com.demo.service.DemoService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; /** * @author zy * @version V1.0 * @date 2022/9/7 17:52 */ @Service public class DemoServiceImpl implements DemoService { @Autowired private DemoMapper demoMapper; //因为全局存在多个事务控制器,这里开启事务时需要指定事务控制器 @Transactional(transactionManager = "idsTransactionManager", rollbackFor = Exception.class) @Override public int insert(Demo demo) { demo.setId(new BigDecimal(66666)); int insert = demoMapper.insert(demo); int s = insert / 0; return insert; } }
ds2:service
package com.demo.service.Impl; import com.demo.entity.ds2.Student; import com.demo.mapper.ds2.StudentMapper; import com.demo.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * @author zy * @version V1.0 * @date 2022/9/7 17:53 */ @Service public class StudentServiceImpl implements StudentService { @Autowired private StudentMapper studentMapper; @Transactional(transactionManager = "ds2TransactionManager", rollbackFor = Exception.class) @Override public int insert(Student student) { student.setId(3L); int insert = studentMapper.insert(student); return insert; } }
testService
package com.demo.service.Impl; import com.demo.entity.ds1.Demo; import com.demo.entity.ds2.Student; import com.demo.service.DemoService; import com.demo.service.StudentService; import com.demo.service.TestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * @author zy * @version V1.0 * @date 2022/9/7 18:10 */ @Service public class TestServiceImpl implements TestService { @Autowired private DemoService demoService; @Autowired private StudentService studentService; @Transactional(rollbackFor = Exception.class) @Override public void testTransactional() { studentService.insert(new Student()); demoService.insert(new Demo()); } }
测试类
package com.demo; import com.demo.entity.ds1.Demo; import com.demo.entity.ds2.Student; import com.demo.service.DemoService; import com.demo.service.StudentService; import com.demo.service.TestService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.web.WebAppConfiguration; /** * @description: * @author: wzy * @date: 2021/12/24 11:24 * @version: V1.0 */ @RunWith(SpringRunner.class) @SpringBootTest @WebAppConfiguration public class DemoServiceImplTest { @Autowired private TestService testService; @Autowired private DemoService demoService; @Autowired private StudentService studentService; //测试事务 @Test public void testTransactional() { testService.testTransactional(); } //测试两个数据源 @Test public void testInsert() { demoService.insert(new Demo()); studentService.insert(new Student()); } }
执行testTransactional会发现,抛出异常,但是student数据没有回滚。
多数据源的事务管理
多数据源事务控制需要引入spring-boot-starter-jta-atomikos
,maven依赖如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
将上述数据源配置类只需要将DataSource创建为XAdDataSource并且使用AtomikosDataSourceBean进行管理即可。同时注掉DataSourceTransactionManager,因为我们的事务管交由spring管理,不需要为每个数据源指定事务管理器。
如下
/**创建数据源*/
@Bean(name = "ds1DataSource")
@Primary
public DataSource firmDataSource() throws SQLException {
DruidXADataSource xa = new DruidXADataSource();
xa.setUrl(url);
xa.setUsername(user);
xa.setPassword(password);
xa.setDbType(JdbcUtils.ORACLE);
xa.setDriverClassName(driverClass);
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(xa);
xaDataSource.setUniqueResourceName("ds1");
return xaDataSource;
}
/**创建事务管理器*/
/*@Bean(name = "ds1TransactionManager")
@Primary
public DataSourceTransactionManager firmSqlSessionFactory() {
return new DataSourceTransactionManager(firmDataSource());
}*/
同时修改service层的事务注解
@Transactional(rollbackFor = Exception.class)
//@Transactional(transactionManager = "idsTransactionManager", rollbackFor = Exception.class)
@Override
public int insert(Demo demo) {
demo.setId(new BigDecimal(66666));
int insert = demoMapper.insert(demo);
int s = insert / 0;
return insert;
}
// @Transactional(transactionManager = "yzxTransactionManager", rollbackFor = Exception.class)
@Transactional(rollbackFor = Exception.class)
@Override
public int insert(Student student) {
student.setId(3L);
int insert = studentMapper.insert(student);
return insert;
}
再次执行测试类,可以发现,抛出异常的同时,student也进行了回滚.