Java基础、中级、高级、架构面试资料

手把手教你利用 SpringBoot + Mybatis 实现一个读写分库项目

JAVA herman 2077浏览
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog2,发送下载链接帮助你免费下载!
本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog2,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领
腾讯云】1核2G5M轻量应用服务器50元首年,高性价比,助您轻松上云

昨天,我发了一篇关于 Spring 脑图的学习知识体系,包括原理,源码解析,结合设计模式等。没想到非常的火爆,微信通讯录里一下子多了很多好友。我预计在今年 5 月份左右通讯录会达到 5000 人的上限。目的还是希望大家相互交流,共同进步,相互鼓励!

在互联网技术飞速发展的今天,各种技术已经非常成熟,尤其是像读写分离这样的架构,以及这样的项目显得非常的常见。那么今天我就和大家一起来,手把手的教大家利用 SpringBoot + Mybatis 实现一个读写分库的 demo 项目。

首先,我们这个项目要使用 SpringBoot,因为 SpringBoot 是现阶段最火的框架之一了;第二,我们要使用 Mybatis;第三,我们还要使用自定义注解。

下面我介绍几个主要的类实现,需要源码的可以加我微信号:xmtxtt 为好友,我免费发给大家!

先创建一个 DataSources 数据源接口。比如,我们假设是一主一从。

public interface DataSources {
    String MASTER_DB = "masterDB";
    String SLAVE_DB = "slaveDB";
}

第二步,创建一个切换数据源的 RoutingDataSource 注解类。

@Retention(RetentionPolicy.RUNTIME)
@Target({
        ElementType.METHOD
})
public @interface RoutingDataSource {
    String value() default DataSources.MASTER_DB;
}

注解的使用我就不在过多的解释了,就当大家都会吧,如果不会加群交流,好了。

第三步,在 application.yml 中配置多个数据源。

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/xttblog
    username: xttblog
    password: xttblog
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
  datasourceSlave:
    url: jdbc:mysql://127.0.0.1:3306/xttblog
    username: xttblog
    password: xttblog
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

第四步,配置多个 Datasource。

@Configuration
public class DatasourceConfig {
    @Bean(destroyMethod =  "close", name = DataSources.MASTER_DB)
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }
    @Bean(destroyMethod =  "close", name = DataSources.SLAVE_DB)
    @ConfigurationProperties(prefix = "spring.datasourceSlave")
    public DataSource dataSourceSlave() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }
}

第五步,定义一个 DataSourceHolder,切换数据源。

public class DataSourceHolder {
    /**
     * 默认数据源
     */
    public static final String DEFAULT_DATASOURCE = DataSources.MASTER_DB;
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    // 设置数据源名
    public static void setDB(String dbType) {
        System.out.println("切换到" + dbType + "数据源");
        contextHolder.set(dbType);
    }
    // 获取数据源名
    public static String getDB() {
        return (contextHolder.get());
    }
    // 清除数据源名
    public static void clearDB() {
        contextHolder.remove();
    }
}

第六步,定义一个动态数据源。

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceHolder.getDB();
    }
}

第七步,Mybatis 整合动态数据源。

@Configuration
public class MybatisDynamicDataSourceConfig {
    @Autowired
    @Qualifier(DataSources.MASTER_DB)
    private DataSource masterDB;
    @Autowired
    @Qualifier(DataSources.SLAVE_DB)
    private DataSource slaveDB;
    /**
     * 动态数据源
     */
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(masterDB);

        // 配置多数据源
        Map<Object, Object> dsMap = Maps.newHashMap();
        dsMap.put(DataSources.MASTER_DB, masterDB);
        dsMap.put(DataSources.SLAVE_DB, slaveDB);
        dynamicDataSource.setTargetDataSources(dsMap);
        return dynamicDataSource;
    }
    @Bean
    @ConfigurationProperties(prefix = "mybatis")
    public SqlSessionFactoryBean sqlSessionFactoryBean() {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        // 配置数据源,此处配置为关键配置,如果没有将 dynamicDataSource 作为数据源则不能实现切换
        sqlSessionFactoryBean.setDataSource(dynamicDataSource());
        return sqlSessionFactoryBean;
    }
}

第八步,也是最重要的一步,拦截所有有 @RoutingDataSource 注解的方法。动态的给它指定数据源。

@Aspect
@Component
public class XttblogDynamicDataSourceAspect {
    @Before("@annotation(com.xttblog.sharding.RoutingDataSource)")
    public void doBeforeSwitchDataSource(ProceedingJoinPoint point){
        //获得当前访问的class
        Class<?> className = point.getTarget().getClass();
        //获得访问的方法名
        String methodName = point.getSignature().getName();
        Signature signature =point.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature;
        //得到方法的参数的类型
        Class[] argClass = methodSignature.getMethod().getParameterTypes();
        String dataSource = DataSourceHolder.DEFAULT_DATASOURCE;
        try {
            // 得到访问的方法对象
            Method method = className.getMethod(methodName, argClass);
            // 判断是否存在@DS注解
            if (method.isAnnotationPresent(RoutingDataSource.class)) {
                RoutingDataSource annotation = method.getAnnotation(RoutingDataSource.class);
                // 取出注解中的数据源名
                dataSource = annotation.value();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 切换数据源
        DataSourceHolder.setDB(dataSource);
    }
    @After("@annotation(com.xttblog.sharding.RoutingDataSource)")
    public void doAfterSwitchDataSource(JoinPoint point){
        DataSourceHolder.clearDB();
    }
}

第九步,要取消自动配置数据源,不要让 SpringBoot 自动的使用 DataSourceAutoConfiguration 了。

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class XttblogApplication {
    public static void main(String[] args) {
        SpringApplication.run(XttblogApplication.class,args);
    }
}

最后就是如何使用了,插入我们使用主库,查询我们使用从库。

@Service
public class XttblogService {
    @Autowired
    private XttblogMapper xttblogMapper;
    @RoutingDataSource(DataSources.MASTER_DB)
    public int insert(Xttblog xttblog) {
        return xttblogMapper.insertXttblog(xttblog.getName());
    }
    @RoutingDataSource(DataSources.SLAVE_DB)
    public Xttblog select(Long id) {
        return xttblogMapper.selectXttblogById(id);
    }
}

以上源码,需要的可以加我微信号:xmtxtt 为好友,我免费发给大家!

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号:xttblog2。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » 手把手教你利用 SpringBoot + Mybatis 实现一个读写分库项目