SpringBoot+Mybatis plus实现多数据源整合的实践

网友投稿 545 2022-09-21


SpringBoot+Mybatis plus实现多数据源整合的实践

SpringBoot 版本为1.5.10.RELEASE,Mybatis plus 版本为2.1.8。

第一步:填写配置信息:

spring:

aop:

proxy-target-class: true

auto: true

datasource:

druid:

# 数据库 1

db1:

url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true

username: root

password: root

driver-class-name: com.mysql.jdbc.Driver

initialSize: 5

minIdle: 5

maxActive: 20

# 数据库 2

db2:

url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true

username: root

password: root

driver-class-name: com.mysql.jdbc.Driver

initialSize: 5

minIdle: 5

maxActive: 20

第二步: 数据源配置:

@Configuration

@MapperScan({"com.warm.system.mapper*"})

public class MybatisPlusConfig {

/**

* mybatis-plus分页插件

* 文档:http://mp.baomidou.com

*/

@Bean

public PaginationInterceptor paginationInterceptor() {

PaginationInterceptor paginationInterceptor = new PaginationInterceptor();

//paginationInterceptor.setLocalPage(true);// 开启 PageHelper 的支持

return paginationInterceptor;

}

/**

* mybatis-plus SQL执行效率插件【生产环境可以关闭】

*/

@Bean

public PerformanceInterceptor performanceInterceptor() {

return new PerformanceInterceptor();

}

@Bean(name = "db1")

@ConfigurationProperties(prefix = "spring.datasource.druid.db1" )

public DataSource db1 () {

return DruidDataSourceBuilder.create().build();

}

@Bean(name = "db2")

@ConfigurationProperties(prefix = "spring.datasource.druid.db2" )

public DataSource db2 () {

return DruidDataSourceBuilder.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 {

MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();

sqlSessionFactory.setDataSource(multipleDataSource(db1(),db2()));

//sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/*/*Mapper.xml"));

MybatisConfiguration configuration = new MybatisConfiguration();

//configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);

configuration.setJdbcTypeForNull(JdbcType.NULL);

configuration.setMapUnderscoreToCamelCase(true);

configuration.setCacheEnabled(false);

sqlSessionFactory.setConfiguration(configuration);

sqlSessionFactory.setPlugins(new Interceptor[]{ //PerformanceInterceptor(),OptimisticLockerInterceptor()

paginationInterceptor() //添加分页功能

});

sqlSessionFactory.setGlobalConfig(globalConfiguration());

return sqlSessionFactory.getObject();

}

@Bean

public GlobalConfiguration globalConfiguration() {

GlobalConfiguration conf = new GlobalConfiguration(new LogicSqlInjector());

conf.setLogicDeleteValue("-1");

conf.setLogicNotDeleteValue("1");

conf.setIdType(0);

conf.setMetaObjectHandler(new MyMetaObjectHandler());

conf.setDbColumnUnderline(true);

conf.setRefresh(true);

return conf;

}

}

第三步:利用AOP进行数据源的动态切换:

@Component

@Aspect

@Order(-100) //这是为了保证AOP在事务注解之前生效,Order的值越小,优先级越高

@Slf4j

public class DataSourceSwitchAspect {

@Pointcut("execution(* com.warm.system.service.db1..*.*(..))")

private void db1Aspect() {

}

@Pointcut("execution(* com.warm.system.service.db2..*.*(..))")

private void db2Aspect() {

}

@Before( "db1Aspect()" )

public void db1() {

log.info("切换到db1 数据源...");

DbContextHolder.setDbType(DBTypeEnum.db1);

}

@Before("db2Aspect()" )

public void db2 () {

log.info("切换到db2 数据源...");

DbContextHolder.setDbType(DBTypeEnum.db2);

}

}

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();

}

}

public enum DBTypeEnum {

db1("db1"), db2("db2");

private String value;

DBTypeEnum(String value) {

this.value = value;

}

public String getValue() {

return value;

}

}

public class DynamicDataSource extends AbstractRoutingDataSource {

/**

* 取得当前使用哪个数据源

* @return

*/

@Override

protected Object determineCurrentLookupKey() {

return DbContextHolder.getDbType();

}

}

OK!写个单元测试来验证一下:

@SpringBootTest

@RunWith(SpringJUnit4ClassRunner.class)

public class DataTest {

@Autowired

private UserService userService;

@Autowired

private OrderService orderService;

@Test

public void test() {

userService.getUserList().stream().forEach(item -> System.out.println(item));

orderService.getOrderList().stream().forEach(item -> System.out.println(item));

}

}

如图所示,证明数据源能动态切换了。

具体项目结构和代码参考github。

踩坑记录:

直接调用Mybatis plus 的service方法AOP不会生效,即数据源不会动态切换,解决方法:在自己的service层中封装一下,调用自定义的service方法AOP即能正常生效了,如下所示:

@Service

public class UserServiceImpl extends ServiceImpl implements UserService {

@Override

public List getUserList() {

return selectList(null);

}

}

application.yml 定义的mybatis plus 配置信息不生效,如:

#MyBatis

mybatis-plus:

mapper-locations: classpath:/mapper/*/*Mapper.xml

#实体扫描,多个package用逗号或者分号分隔

typeAliasesPackage: com.jinhuatuo.edu.sys.entity

global-config:

#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";

id-type: 0

#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"

field-strategy: 2

#驼峰下划线转换

db-column-underline: true

#刷新mapper 调试神器

refresh-mapper: true

#数据库大写下划线转换

#capital-mode: true

#序列接口实现类配置

#key-generator: com.baomidou.springboot.xxx

#逻辑删除配置

#logic-delete-value: 0

#logic-not-delete-value: 1

#自定义填充策略接口实现

meta-object-handler: com.jinhuatuo.edu.config.mybatis.MyMetaObjectHandler

#自定义SQL注入器

#sql-injector: com.baomidou.springboot.xxx

configuration:

map-underscore-to-camel-case: true

cache-enabled: false

解决方法: 所有这些配置在MybatisPlusConfig 类中用代码的方式进行配置,分页插件亦是如此,否则统计列表总数的数据会拿不到,参考代码即可。

在application.yml配置

logging:

level: debug

控制台也不会打印Mybatis 执行的SQL语句,解决方法:自定义日志输出方案,如在classpath下直接引入日志配置文件如logback-spring.xml即可,同时application.yml无需再配置日志信息。

logback-shttp://pring.xml配置参考:

true

[ %-5level] [%date{yyyy-MM-dd HH:mm:ss}] %logger{96} [%line] - %msg%n

utf-8

${LOG_HOME}/${PROJECT_NAME}-%d{yyyy-MM-dd}.%i.log

50 MB

30

true

[ %-5level] [%date{yyyy-MM-dd HH:mm:ss}] %logger{96} [%line] - %msg%n

utf-8

${LOG_HOME}/${PROJECT_NAME}-error-%d{yyyy-MM-dd}.%i.log

50 MB

30

ERROR

[ %-5level] [%date{yyyy-MM-dd HH:mm:ss}] %logger{96} [%line] - %msg%n


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:路由器NAT 类型检测实现(路由器nat是什么意思)
下一篇:网络工程师行业有哪些
相关文章

 发表评论

暂时没有评论,来抢沙发吧~