spring-boot+mybatis-plus多数据源切换
配置application信息
#多数据源配置如下: spring.datasource.db1.jdbc-url=jdbc:mysql://localhost:3306/db1?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true spring.datasource.db1.username=root spring.datasource.db1.password=123456 spring.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.db1.minimum-idle=1 spring.datasource.db1.maximum-pool-size=3 spring.datasource.db2.jdbc-url=jdbc:mysql://slocalhost:3307/db2?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true spring.datasource.db2.username=root spring.datasource.db2.password=123456 spring.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.db2.minimum-idle=1 spring.datasource.db2.maximum-pool-size=3 # mybatis-plus配置 #mybatis-plus.mapper-#locations=classpath:/mapper/**/*Mapper.xml #mybatis-plus.type-aliases-package=com.police.model.entity #mybatis-plus.configuration.log-#impl=org.apache.ibatis.logging.stdout.StdOutImpl #mybatis-plus.global-config.db-config.id-type=auto #mybatis-plus.global-config.db-config.logic-delete-value=1 #mybatis-plus.global-config.db-config.logic-not-delete-value=0
spring boot 启动类上加上响应的信息
在spring boot 启动类上添加扫描mapper注解 - @MapperScan(“com.police.biz.dao.*”)
DataSourceAutoConfiguration.class默认会帮我们自动配置单数据源,所以,如果想在项目中使用多数据源就需要排除它,手动指定多数据源 -@SpringBootApplication(scanBasePackages = "com.police", exclude = {DataSourceAutoConfiguration.class})
个人的项目结构
在springboot项目地址中创建config包(注意框架结构)并加入...
/** * 多数据源枚举 */ public enum DBTypeEnum { db1("db1"), db2("db2"); private String value; DBTypeEnum(String value) { this.value = value; } public String getValue() { return value; } }
/** * 创建数据源操作类DbContextHolder */ public class DbContextHolder { private static final ThreadLocal contextHolder = new ThreadLocal<>(); /** * 设置数据源 * @param dbTypeEnum */ public static void setDbType(DBTypeEnum dbTypeEnum) { contextHolder.set(dbTypeEnum.getValue()); } /** * 取得当前数据源 * @return */ public static String getDbType() { return (String) contextHolder.get(); } /** * 清除上下文数据 */ public static void clearDbType() { contextHolder.remove(); } }
/** * 扩展Spring的AbstractRoutingDataSource抽象类,实现动态数据源。 * AbstractRoutingDataSource中的抽象方法determineCurrentLookupKey是实现数据源的route的核心, * 这里对该方法进行Override。 【上下文DbContextHolder为一线程安全的ThreadLocal】 */ public class DynamicDataSource extends AbstractRoutingDataSource { /** * 取得当前使用哪个数据源 * @return */ @Override protected Object determineCurrentLookupKey(){ return DbContextHolder.getDbType(); } }
import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.type.JdbcType; 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.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration public class MybatisPlusConfig { /** * 分页拦截器 * @return */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } /** * 定义db1 与配置文件中的层次结构相同 * @return */ @Bean(name = "db1") @ConfigurationProperties(prefix = "spring.datasource.db1") public DataSource db1() { return DataSourceBuilder.create().build(); } /** * 定义db2 与配置文件中的层次结构相同 * @return */ @Bean(name = "db2") @ConfigurationProperties(prefix = "spring.datasource.db2") public DataSource db2() { return DataSourceBuilder.create().build(); } /** * 动态数据源配置 * @return */ @Bean @Primary public DataSource multipleDataSource(@Qualifier("db1") DataSource db1, @Qualifier("db2") DataSource db2) { DynamicDataSource dynamicDataSource = new DynamicDataSource(); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DBTypeEnum.db1.getValue(), db1); targetDataSources.put(DBTypeEnum.db2.getValue(), db2); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(db1); return dynamicDataSource; } @Bean("sqlSessionFactory") public SqlSessionFactory sqlSessionFactory() throws Exception { //***导入MybatisSqlSession配置*** MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); //指明数据源 sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2())); //指明mapper.xml位置(配置文件中指明的xml位置会失效用此方式代替,具体原因未知) sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/**Mapper.xml")); //指明实体扫描(多个package用逗号或者分号分隔) sqlSessionFactory.setTypeAliasesPackage("com.police.model.entity"); //***导入Mybatis配置*** MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setJdbcTypeForNull(JdbcType.NULL); configuration.setMapUnderscoreToCamelCase(true); configuration.setCacheEnabled(false); sqlSessionFactory.setConfiguration(configuration); sqlSessionFactory.setPlugins(new Interceptor[]{paginationInterceptor()}); //***导入全局配置*** // sqlSessionFactory.setGlobalConfig(globalConfiguration()); return sqlSessionFactory.getObject(); } /** * 在代码中配置MybatisPlus替换掉application.yml中的配置 * @return */ // @Bean // public GlobalConfiguration globalConfiguration() { // GlobalConfiguration conf = new GlobalConfiguration(new LogicSqlInjector()); // //主键类型 0:数据库ID自增, 1:用户输入ID,2:全局唯一ID (数字类型唯一ID), 3:全局唯一ID UUID // conf.setIdType(0); // //字段策略(拼接sql时用于判断属性值是否拼接) 0:忽略判断,1:非NULL判断,2:非空判断 // conf.setFieldStrategy(2); // //驼峰下划线转换含查询column及返回column(column下划线命名create_time,返回java实体是驼峰命名createTime,开启后自动转换否则保留原样) // conf.setDbColumnUnderline(true); // //是否动态刷新mapper // conf.setRefresh(true); // return conf; // } }
当更新操作时可以会需要给一些字段填充一些统一的数据
/** * 填充策略 */ @Component public class MyBatisPlusMeta implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.setFieldValByName("createTime", new Date(), metaObject); this.setFieldValByName("cjsj", new Date(), metaObject); this.setFieldValByName("cjjlsj", new Date(), metaObject); this.setFieldValByName("updateTime", new Date(), metaObject); } @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime", new Date(), metaObject); } }
模仿dynamic@DS切换数据源的注解
@Documented @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface DS { DBTypeEnum dataSource() default DBTypeEnum.db1; }
@Aspect @Component @Slf4j public class DSAspect { @Pointcut("execution(* com.police..*.*(..))") public void executionController() { } @Before("executionController()&&@annotation(DS)") public void methodBefore(JoinPoint joinPoint) { log.info("切换数据源"); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); DS ds = signature.getMethod().getAnnotation(DS.class); DBTypeEnum dataSource = ds.dataSource(); DbContextHolder.setDbType(dataSource); } }
测试一波
@RestController @RequestMapping("/test") public class TestController { @Autowired private SysUserMapper2 userMapper2; @Autowired private SysUserMapper1 userMapper1; @GetMapping("/a") public JsonResult a() { userMapper.insert(new SysUser()); return new JsonResult(userMapper1.selectList(null)); } @DS(dataSource = DBTypeEnum.db2) @GetMapping("/b") public JsonResult b() { return new JsonResult(userMapper2.selectList(null)); } }
由于个人项目不方便展示,这里找了CSDN的博客地址https://blog.csdn.net/qq_37502106/article/details/91044952后面有源码参考