这篇文章主要介绍了SpringBoot+Mybatis如何实现动态数据源切换,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
springboot一种全新的编程规范,其设计目的是用来简化新Spring应用的初始搭建以及开发过程,SpringBoot也是一个服务于框架的框架,服务范围是简化配置文件。
电商订单项目分正向和逆向两个部分:其中正向数据库记录了订单的基本信息,包括订单基本信息、订单商品信息、优惠卷信息、发票信息、账期信息、结算信息、订单备注信息、收货人信息等;逆向数据库主要包含了商品的退货信息和维修信息。数据量超过500万行就要考虑分库分表和读写分离,那么我们在正向操作和逆向操作的时候,就需要动态的切换到相应的数据库,进行相关的操作。
现在项目的结构设计基本上是基于MVC的,那么数据库的操作集中在dao层完成,主要业务逻辑在service层处理,controller层处理请求。假设在执行dao层代码之前能够将数据源(DataSource)换成我们想要执行操作的数据源,那么这个问题就解决了
1.实体类
@Data public class Product { private Integer id; private String name; private Double price; }
2.ProductMapper
public interface ProductMapper { @Select("select * from product") public List<Product> findAllProductM(); @Select("select * from product") public List<Product> findAllProductS(); }
3.ProductService
@Service public class ProductService { @Autowired private ProductMapper productMapper; public void findAllProductM(){ // 查询Master List<Product> allProductM = productMapper.findAllProductM(); System.out.println(allProductM); } public void findAllProductS(){ // 查询Slave List<Product> allProductS = productMapper.findAllProductS(); System.out.println(allProductS); } }
第一步:配置多数据源
首先,我们在application.properties中配置两个数据源
spring.druid.datasource.master.password=root spring.druid.datasource.master.username=root spring.druid.datasource.master.jdbc- url=jdbc:mysql://localhost:3306/product_master? useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC spring.druid.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver spring.druid.datasource.slave.password=root spring.druid.datasource.slave.username=root spring.druid.datasource.slave.jdbc- url=jdbc:mysql://localhost:3306/product_slave? useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC spring.druid.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver 在SpringBoot的配置代码中,我们初始化两个数据源: @Configuration public class MyDataSourceConfiguratioin { Logger logger = LoggerFactory.getLogger(MyDataSourceConfiguratioin.class); /*** Master data source. */ @Bean("masterDataSource") @ConfigurationProperties(prefix = "spring.druid.datasource.master") DataSource masterDataSource() { logger.info("create master datasource..."); return DataSourceBuilder.create().build(); } /*** Slave data source. */ @Bean("slaveDataSource") @ConfigurationProperties(prefix = "spring.druid.datasource.slave") DataSource slaveDataSource() { logger.info("create slave datasource..."); return DataSourceBuilder.create().build(); } @Bean @Primary DataSource primaryDataSource(@Autowired @Qualifier("masterDataSource")DataSource masterDataSource, @Autowired @Qualifier("masterDataSource")DataSource slaveDataSource){ logger.info("create routing datasource..."); Map<Object, Object> map = new HashMap<>(); map.put("masterDataSource", masterDataSource); map.put("slaveDataSource", slaveDataSource); RoutingDataSource routing = new RoutingDataSource(); routing.setTargetDataSources(map); routing.setDefaultTargetDataSource(masterDataSource); return routing; } }
第二步:编写RoutingDataSource
然后,我们用Spring内置的RoutingDataSource,把两个真实的数据源代理为一个动态数据源:
public class RoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return RoutingDataSourceContext.getDataSourceRoutingKey(); } }
第三步:编写RoutingDataSourceContext
用于存储当前需要切换为哪个数据源
public class RoutingDataSourceContext { // holds data source key in thread local: static final ThreadLocal<String> threadLocalDataSourceKey = new ThreadLocal<>(); public static String getDataSourceRoutingKey() { String key = threadLocalDataSourceKey.get(); return key == null ? "masterDataSource" : key; } public RoutingDataSourceContext(String key) { threadLocalDataSourceKey.set(key); } public void close() { threadLocalDataSourceKey.remove(); } }
测试(一下代码为controller中代码)
@GetMapping("/findAllProductM") public String findAllProductM() { String key = "masterDataSource"; RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext(key); productService.findAllProductM(); return "master"; } @GetMapping("/findAllProductS") public String findAllProductS() { String key = "slaveDataSource"; RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext(key); productService.findAllProductS(); return "slave"; }
以上代码即可实现数据源动态切换
以上代码是可行的,但是,需要读数据库的地方,就需要加上一大段RoutingDataSourceContext
ctx = ...代码,使用起来十分不便。以下是优化方案
我们可以申明一个自定义注解,将以上RoutingDataSourceContext中的值,放在注解的value属性中,
然后定义一个切面类,当我们在方法上标注自定义注解的时候,执行切面逻辑,获取到注解中的值,set到RoutingDataSourceContext中,从而实现通过注解的方式,来动态切换数据源
以下是代码实现:
注解类
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RoutingWith { String value() default "master"; }
切面类:
@Aspect @Component public class RoutingAspect { @Around("@annotation(routingWith)") public Object routingWithDataSource(ProceedingJoinPoint joinPoint, RoutingWith routingWith) throws Throwable { String key = routingWith.value(); RoutingDataSourceContext ctx = new RoutingDataSourceContext(key); return joinPoint.proceed(); } }
改造Controller方法
@RoutingWith("masterDataSource") @GetMapping("/findAllProductM") public String findAllProductM() { productService.findAllProductM(); return "lagou"; } @RoutingWith("slaveDataSource") @GetMapping("/findAllProductS") public String findAllProductS() { productService.findAllProductS(); return "lagou"; }
感谢你能够认真阅读完这篇文章,希望小编分享的“SpringBoot+Mybatis如何实现动态数据源切换”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。