概述
Spring Boot2.x-09 基于Spring Boot + Mybatis使用自定义注解实现数据库切换 通过自定义注解实现了数据库的切库。多数据源的支持我们通过这篇博文来梳理。
单个数据源 见 Spring Boot2.x-07Spring Boot2.1.2整合Mybatis
思路
让不同的数据源绑定不同的mybatis配置,再细化一点就是让不同的数据源扫描不同的包,让不同包下的mapper连接不同的数据源去处理业务逻辑。
多说一句,对于跨库的多表操作,这种整合是支持不了的
步骤
我们基于 Spring Boot2.x-09 基于Spring Boot + Mybatis使用自定义注解实现数据库切换 来改造下,让其支持多数据源。
Step1 多数据源配置文件applicaiton.yml
自定义前缀,在标注了@Configuration的配置类中通过prefix 将数据源关联起来。 这里的配置类为DatasourceConfig。
# datasource db1 前缀为自定义的datasource-db1 spring: datasource-db1: driver-class-name: com.mysql.cj.jdbc.Driver # JDBC连接Mysql6以上com.mysql.cj.jdbc.Driver (服务端为Mysql8) url: jdbc:mysql://localhost:3306/db1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: root # datasource db2 前缀为自定义的datasource-db2 datasource-db2: driver-class-name: com.mysql.cj.jdbc.Driver # JDBC连接Mysql6以上com.mysql.cj.jdbc.Driver (服务端为Mysql8) url: jdbc:mysql://localhost:3306/db2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: root # mybatis mybatis_db1: # 映射文件的路径 , 这个切换数据源的场景下不能配置 * 通配符,有多个 逗号隔开,继续跟 classpath:mapper_db1/XXX # 在MybatisConfig.java#sqlSessionFactoryBean方法中通过sqlSessionFactoryBean设置classpath:mapper_db1/*.xml ,不然每次都要改这个地方,不好维护。 # mapper-locations: classpath:mapper_db1/*.xml # 类型别名包配置,只能指定具体的包,多个配置可以使用英文逗号隔开 # 也可以通过sqlSessionFactoryBean.setTypeAliasesPackage("com.artisan.domain.db1") type-aliases-package: com.artisan.domain.db1 # Mybatis SQL语句控制台打印 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # mybatis mybatis_db2: # 映射文件的路径 , 这个切换数据源的场景下不能配置 * 通配符,有多个 逗号隔开,继续跟 classpath:mapper/XXX # 在MybatisDB2Config.java#sqlSessionFactoryBean方法中通过sqlSessionFactoryBean设置classpath:mapper_db2/*.xml ,不然每次都要改这个地方,不好维护。 #mapper-locations: classpath:mapper_db2/*.xml # 类型别名包配置,只能指定具体的包,多个配置可以使用英文逗号隔开 type-aliases-package: com.artisan.domain.db2 # Mybatis SQL语句控制台打印 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
Step2 初始化多个数据源
这里同时也初始化了JdbcTemplate和事务管理,当然了对这个例子是可选操作。 主要是不同DataSource的初始化
多个数据源,操作JdbcTemplate和Transaction,需要指定使用哪个数据源,否则Spring根据type找到多个bean,不知道注入哪个。 【使用@Primary或者@Qualifier都可以】
package com.artisan.config; import javax.sql.DataSource; 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.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import com.alibaba.druid.pool.DruidDataSource; @Configuration public class DatasourceConfig { //destroy-method="close":当数据库连接不使用的时候,将该连接重新放到数据池中 @Bean(name=DataSources.DB1,destroyMethod="close") @ConfigurationProperties(prefix = "spring.datasource-db1") public DataSource dataSourceDB1() { // 创建数据源 return DataSourceBuilder.create().type(DruidDataSource.class).build(); } @Bean(name=DataSources.DB2,destroyMethod="close") @ConfigurationProperties(prefix = "spring.datasource-db2") public DataSource dataSourceDB2() { // 创建数据源 return DataSourceBuilder.create().type(DruidDataSource.class).build(); } // 支持JdbcTemplate (可选) @Bean(DataSources.DB1_JDBCTEMPLATE) public JdbcTemplate db1JdbcTemplate( @Qualifier(DataSources.DB1) DataSource dataSource) { return new JdbcTemplate(dataSource); } @Bean(DataSources.DB2_JDBCTEMPLATE) public JdbcTemplate db2JdbcTemplate( @Qualifier(DataSources.DB2) DataSource dataSource) { return new JdbcTemplate(dataSource); } // 支持事务(可选) @Bean(DataSources.DB1_TRANSACTION) public DataSourceTransactionManager db1TransactionManager( @Qualifier(DataSources.DB1) DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(DataSources.DB2_TRANSACTION) public DataSourceTransactionManager db2TransactionManager( @Qualifier(DataSources.DB2) DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
Step3 配置多个数据源
1. 扫描包的路径,不同的数据源扫描的包路径不同
2. 通过@Qualifier指定注入的数据源
3. Mybatis如果使用xml方式,配置文件中有配置项,通过前缀加载对应的配置项
MybatisDB1Config
package com.artisan.config; import javax.sql.DataSource; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; 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; @Configuration // db1的接口类的包名 @MapperScan(basePackages = { "com.artisan.dao.db1" }, sqlSessionFactoryRef = "db1SqlSessionFactoryBean") public class MybatisDB1Config { @Autowired // 必须指定注入哪个数据源,否则找到多个会注入失败 @Qualifier(DataSources.DB1) private DataSource db1; @Bean(name = "db1SqlSessionFactoryBean") @ConfigurationProperties(prefix = "mybatis-db1") // 和 配置文件中的前缀保持一致 // @Primary 如果SqlSessionFactoryBean的名字和MybatisDB2Config中的一致(默认方法名),需要加上这个注解,优先注入该SqlSessionFactoryBean // 这里我们通过bean指定了name,并且方法名也不一样,所以如果情况不一样,看是否需要加入@Primary 。 如果需要两个方法上加一个就行了,都加的话,spring又找不到bean注入啦。。 public SqlSessionFactoryBean db1SqlSessionFactoryBean() throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); // 配置数据源 sqlSessionFactoryBean.setDataSource(db1); // 如下的两行代码仅仅用于*.xml文件,如果整个持久层操作没用到xml文件的话,比如使用注解的方式,则无需加 // 解决配置到配置文件中通过*配置找不到mapper文件的问题。 如果不设置这一行,在配置文件中,只能使用数组的方式一个个的罗列出来,并且要指定具体的文件名 sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper_db1/*.xml")); // 也可以通过在application.yml中配置 //sqlSessionFactoryBean.setTypeAliasesPackage("com.artisan.domain.db1"); return sqlSessionFactoryBean; } // 可选,如果需要通过SqlSessionTemplate来操作持久层就通过@Bean实例化,我们这个例子中没用到,随手写出来了 @Bean(name="db1SqlSessionTemplate") public SqlSessionTemplate db1SqlSessionTemplate() throws Exception { SqlSessionTemplate template = new SqlSessionTemplate(db1SqlSessionFactoryBean().getObject()); return template; } }
MybatisDB2Config
package com.artisan.config; import javax.sql.DataSource; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; @Configuration @MapperScan(basePackages = { "com.artisan.dao.db2" },sqlSessionFactoryRef="db2SqlSessionFactoryBean" ) // 扫描的mybatis接口类的包名 public class MybatisDB2Config { @Autowired // 必须指定注入哪个数据源,否则找到多个会注入失败 @Qualifier(DataSources.DB2) private DataSource db2; @Bean(name="db2SqlSessionFactoryBean") @ConfigurationProperties(prefix = "mybatis-db2") // 和 配置文件中的前缀保持一致 public SqlSessionFactoryBean db2SqlSessionFactoryBean() throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); // 配置数据源 sqlSessionFactoryBean.setDataSource(db2); // 解决配置到配置文件中通过*配置找不到mapper文件的问题。 如果不设置这一行,在配置文件中,只能使用数组的方式一个个的罗列出来,并且要指定具体的文件名 sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper_db2/*.xml")); return sqlSessionFactoryBean; } // 可选,如果需要通过SqlSessionTemplate来操作持久层就通过@Bean实例化,我们这个例子中没用到,随手写出来了 @Bean(name = "db2SqlSessionTemplate") public SqlSessionTemplate db2SqlSessionTemplate() throws Exception { SqlSessionTemplate template = new SqlSessionTemplate(db2SqlSessionFactoryBean().getObject()); return template; } }
验证测试
数据库表、Domain , Dao , Service,Controller中的代码见下面的Github连接,很简单,就不贴了,只说重点
贴下数据库的初始数据吧
db1:
db2:
启动Spring Boot工程,
访问 http://localhost:8080/db1?id=1
访问 http://localhost:8080/db2?id=1
支持事务
Step1 配置类中通过@Bean初始化DataSourceTransactionManager
Step2 如何使用呢? @Transactional(DataSources.DB1_TRANSACTION)
Step3 验证事务
访问: http://localhost:8080/db1/transTest
数据库中的数据
为了方便验证 artisanMapper.updateArtisan(artisan)中故意写错了个字段名,让其抛出异常,测试回滚。 执行方法后,可以看到因为第二个方法报错,第一个方法中的插入的数据也回滚了 。
顺便说下 集成JdbcTemplate
持久层我们这里用的Mybatis,有些同学说我想用JdbcTemplate呢
Step1 配置类中通过@Bean初始化每个数据库实例对应的JdbcTemplate
Step2 如何使用呢? @Autowired @Qualifier(DataSources.DB1_JDBCTEMPLATE)
Step3 验证
启动工程,访问 http://localhost:8080/db1/jdbcTemplateTest
查看数据