springboot+mysql读写分离
发布日期:2021-05-27 02:54:11 浏览次数:2 分类:技术文章

本文共 7582 字,大约阅读时间需要 25 分钟。

在javaweb开发中数据库表数据量会越来越大,为了减轻数据库读写压力,对数据库进行主从模式部署(一主一从,多主多从),在应用层面可以通过spring提供的路由数据源+注解+切面进行控制,在代码层面切换不同数据库源,不同操作选择不同数据库。

代码如下:

com.alibaba
druid-spring-boot-starter
1.1.9
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.1.1
mysql
mysql-connector-java
spring.datasource.master.url=jdbc:mysql://localhost:3306/master?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=falsespring.datasource.master.username=rootspring.datasource.master.password=123456spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.slave.url=jdbc:mysql://localhost:3306/slave?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=falsespring.datasource.slave.username=rootspring.datasource.slave.password=123456spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class})@MapperScan("com.*.mapper")public class AnnotationAopReflectionApplication {    public static void main(String[] args) {        SpringApplication.run(AnnotationAopReflectionApplication.class, args);    }}
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic @interface DataSourceAnnotation {    /**     * 默认数据源     */    DataSourceEnum value() default DataSourceEnum.MASTER;    /**     * 清除     */    boolean clear() default true;}public enum DataSourceEnum {    //数据源主库、从库    MASTER("1","master"),    SLAVE("2","slave");    private String code;    private String name;    private DataSourceEnum(String code,String name){        this.code = code;        this.name = name;    }    public static DataSourceEnum getByCode(String code) {        for (DataSourceEnum event : DataSourceEnum.values()) {            if (event.code.equals(code)) {                return event;            }        }        return null;    }    public String getCode() {        return code;    }    public void setCode(String code) {        this.code = code;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}public class DataSourceContextHolder {    private static final ThreadLocal
context = new ThreadLocal<>(); /** * 赋值 */ public static void set(String datasourceType) { context.set(datasourceType); } /** * 获取值 */ public static String get() { return context.get(); } /** * 清空 */ public static void clear() { context.remove(); }}public class RoutingDataSource extends AbstractRoutingDataSource { /** * 最终的determineCurrentLookupKey返回的是从DataSourceContextHolder中拿到的,因此在动态切换数据源的时候注解 * 应该给DataSourceContextHolder设值 */ @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.get(); }}@Configuration@MapperScan(basePackages = "com.", sqlSessionTemplateRef = "sqlSessionTemplate")public class DataSourceConfig { /** * 主库 */ @Bean @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource master() { return DruidDataSourceBuilder.create().build(); } /** * 从库 */ @Bean @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaver() { return DruidDataSourceBuilder.create().build(); } /** * 实例化数据源路由 */ @Bean public RoutingDataSource routingDataSource(@Qualifier("master") DataSource masterDataSource,@Autowired(required = false) @Qualifier("slaver") DataSource slaveDataSource) { RoutingDataSource routingDataSource = new RoutingDataSource(); Map
targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceEnum.MASTER.getName(), masterDataSource); if (slaveDataSource != null) { targetDataSources.put(DataSourceEnum.SLAVE.getName(), slaveDataSource); } routingDataSource.setTargetDataSources(targetDataSources); routingDataSource.setDefaultTargetDataSource(masterDataSource); return routingDataSource; } /** * 配置sessionFactory */ @Bean public SqlSessionFactory sqlSessionFactory(@Qualifier("routingDataSource") DataSource routingDataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml")); sqlSessionFactoryBean.setDataSource(routingDataSource); return sqlSessionFactoryBean.getObject(); } /** * 创建sqlSessionTemplate */ @Bean public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } /** * 事务配置 */ @Bean(name = "dataSourceTransactionManager") public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("routingDataSource") DataSource dynamicDataSource) { DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dynamicDataSource); return dataSourceTransactionManager; }}@Aspect@Order(value = 1)@Componentpublic class DataSourceAspect { @Around("@annotation(com.*.DataSourceAnnotation)") public Object setDynamicDataSource(ProceedingJoinPoint pjp) throws Throwable { boolean clear = false; try { Method method = this.getMethod(pjp); DataSourceAnnotation dataSourceAnnotation = method.getAnnotation(DataSourceAnnotation.class); clear = dataSourceAnnotation.clear(); DataSourceContextHolder.set(dataSourceAnnotation.value().getName()); System.out.println(String.format("数据源切换至:{%s}", dataSourceAnnotation.value().getName())); return pjp.proceed(); } finally { if (clear) { DataSourceContextHolder.clear(); } } } private Method getMethod(JoinPoint pjp) { MethodSignature signature = (MethodSignature) pjp.getSignature(); return signature.getMethod(); }}
@Servicepublic class DemoService {    @Autowired    private UserMapper userMapper;    @Autowired    private AuthUserMapper authUserMapper;    /**     * 读操作     */    @DataSourceAnnotation(DataSourceEnum.SLAVE)    public AuthUser getAuthUser() {        String userId = "001";        AuthUser authUser = authUserMapper.getAuthUserById(userId);        //User user = userMapper.getUserById(1);        return authUser;    }    /**     * 写操作     */    @DataSourceAnnotation(DataSourceEnum.MASTER)    public User updateUser() {        User user = userMapper.getUserById(1);        return user;    }}@RestControllerpublic class DemoController {        @Autowired    private DemoService demoService;    @GetMapping("/readSlave")    public Object readSlave() {        return demoService.getAuthUser();    }    @GetMapping("/writeMaster")    public Object writeMaster() {        return demoService.updateUser();    }}

前提是部署好mysql主从数据库或准备不同数据库。注意读写分离的核心点就是数据路由,需要继承AbstractRountingDataSource,覆写它的determinCurrentLookupKey方法,同时需要注意全局的上下文管理器DataSourceContextHolder,它是保存数据源上下文的主要类,也是路由方法中寻找的数据源取值,相当于数据源的中转站,此例配置了3个数据源。再结合jdbc底层、事务等,数据库读写分离就实现了。

转载地址:https://blog.csdn.net/leijie0322/article/details/112589287 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Java之socket浅析
下一篇:oracle的rownum和mysql的limit浅析

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2023年11月26日 22时22分14秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

[云数据中心] 《云数据中心网络架构与技术》读书笔记第六章构建数据中心的逻辑网络(Overlay网络) 2019-03-25
[PythonCookBook][并发] 判断线程启动与否的方法 2019-03-25
[云数据中心] 《云数据中心网络架构与技术》读书笔记 第八章 构建云数据中心端到端安全 2019-03-25
【哲学向】《反脆弱》一书带给我的启示(5) 2019-03-25
【哲学向】《反脆弱》一书带给我的启示(5.1. 时间和脆弱性) 2019-03-25
【心理学向】《思考,快与慢》带给我的启示(1) 2019-03-25
[SRv6]《SRv6网络编程》SRv6网络的演进 2019-03-25
[云数据中心] 《云数据中心网络架构与技术》读书笔记 第七章 构建多数据中心网络(1/3) 2019-03-25
[云数据中心] 《云数据中心网络架构与技术》读书笔记 第七章 构建多数据中心网络(2/3) 2019-03-25
[云数据中心] 《云数据中心网络架构与技术》读书笔记 第七章 构建多数据中心网络(3/3) 2019-03-25
N1文法「第1-第5」 2019-03-25
【日语】【歌词】「ヤキモチ」--高橋優 2019-03-25
[SRv6]《SRv6网络编程》SRv6网络在电信云中的应用 2019-03-25
[SRv6]《SRv6网络编程》SRv6网络在SD-WAN中的应用 2019-03-25
[SRv6]《SRv6网络编程》SRv6 OAM与随路网络测量(1/2:OAM) 2019-03-25
[转载] 最深的云网融合:多接入边缘计算(MEC) 2019-03-25
[SRv6]《SRv6网络编程》SRv6 OAM与随路网络测量(2/2:IFIT) 2019-03-25
【日语】【歌词】「Lemon」--米津玄师 2019-03-25
[5GC]《5G核心网-赋能数字化时代》| 6.3用户面处理 2019-03-25
Clion安装配置(Windows) 2019-03-25