【框架集成】多数据源 spring boot + mybatis + mybatis plus + druid

静态多数据源:每个数据源对应一套 mapper
动态多数据源:多个数据源通用一套 mapper

1. 静态多数据源

1.1 spring boot + mybaits

1.1.1 工程目录

1.1.2 添加 maven 依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
    <relativePath/>
</parent>

<properties>
    <mysql-connector.version>5.1.46</mysql-connector.version>
    <druid.version>1.1.10</druid.version>
    <mybatis.version>2.1.0</mybatis.version>
</properties>

<dependencies>
    <!-- spring boot -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- Mysql驱动s -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql-connector.version}</version>
    </dependency>
    <!-- mybatis -->
     <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>${mybatis.version}</version>
    </dependency>
    <!--阿里数据库连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>${druid.version}</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

1.1.3 配置文件

这里添加 source1 和 source2 两个数据源

spring:
  datasource:
    source1:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://10.20.32.201:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
      username: root
      password: root123
    source2:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://10.20.32.104:3306/smcc?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
      username: root
      password: root123

1.1.4 配置数据源

@Configuration
public class DataSourceConfig {

    @Bean("source1DataSource")
    @ConfigurationProperties("spring.datasource.source1")
    public DataSource source1DataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean("source2DataSource")
    @ConfigurationProperties("spring.datasource.source2")
    public DataSource source2DataSource(){
        return DruidDataSourceBuilder.create().build();
    }

}

1.1.5 配置 Mybatis

配置 mapper 和 xml 的扫描路径和使用的数据源
Source1MybatisConfig:

@Configuration
@MapperScan(basePackages = {Source1MybatisConfig.BASE_PACKAGE}, sqlSessionFactoryRef = "source1SqlSessionFactory")
public class Source1MybatisConfig {

    static final String BASE_PACKAGE = "com.ricky.learn.modules.source1.dao";

    private static final String MAPPING_LOCATION = "classpath:mapper/source1/**/*.xml";

    private DataSource amDataSource;

    @Autowired
    public void setAmDataSource(@Qualifier("source1DataSource") DataSource amDataSource) {
        this.amDataSource = amDataSource;
    }

    @Bean("source1SqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(amDataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPING_LOCATION));
        return factoryBean.getObject();

    }
}

Source2MybatisConfig:
复制一份,把 Source1MybatisConfig 中的 1 都改成 2

1.1.6 Mapper 和 xml

public interface Source1Mapper {
    int count();
}
public interface Source2Mapper {
    int count();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ricky.learn.modules.source1.dao.Source1Mapper">
    <select id="count" resultType="int">
      select 50 from dual
    </select>
</mapper>
<mapper namespace="com.ricky.learn.modules.source2.dao.Source2Mapper">
    <select id="count" resultType="int">
      select 100 from dual
    </select>
</mapper>

1.1.7 测试

@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class MapperTest {

    @Autowired
    private Source1Mapper source1Mapper;

    @Autowired
    private Source2Mapper source2Mapper;

    @Test
    public void sourceTest() {
        System.out.println(source1Mapper.count());
        System.out.println(source2Mapper.count());
    }
}

输出 50 和 100 就表示成功

1.2 spring boot + mybatis + mybatis plus

1.2.1 修改依赖

在 1.1 的基础上,使用 mybatis plus
去掉我们自己给的 mybaits 依赖,加上 mybaits plus 依赖,他会自动添加对应版本的 mybaits

<properties>
    <mybatis-plus.version>3.1.2</mybatis-plus.version>
</properties>

<!-- mybatis plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>${mybatis-plus.version}</version>
</dependency>

1.2.2 修改 MybatisConfig

将 mybatis 配置文件中的 SqlSessionFactory 改为 MybatisSqlSessionFactoryBean

@Configuration
@MapperScan(basePackages = {Source1MybatisConfig.BASE_PACKAGE}, sqlSessionFactoryRef = "source1SqlSessionFactory")
public class Source1MybatisConfig {

    static final String BASE_PACKAGE = "com.ricky.learn.modules.source1.dao";

    private static final String MAPPING_LOCATION = "classpath:mapper/source1/**/*.xml";

    private DataSource amDataSource;

    @Autowired
    public void setAmDataSource(@Qualifier("source1DataSource") DataSource amDataSource) {
        this.amDataSource = amDataSource;
    }

    @Bean("source1SqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean ();
        factoryBean.setDataSource(amDataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPING_LOCATION));
        return factoryBean.getObject();

    }

}

1.2.3 修改 mapper

将 mapper 继承 BaseMapper

public interface Source1Mapper extends BaseMapper<User> {
    int count();
}

1.2.4 测试

查询对应表中 id 为 0 的数据

@RunWith(SpringRunner.class)
@SpringBootTest
public class MapperTest {

    @Autowired
    private Source1Mapper source1Mapper;

    @Test
    public void sourceTest() {
        System.out.println(source1Mapper.selectById("0"));
    }
}

能够打印出结果就成功了

2. 动态多数据源

2.1 spring boot + mybaits

基于 AOP 和 AbstractRoutingDataSource

2.1.1 添加 aop 依赖

在 1.1 的基础上添加 aop 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.1.2 数据库枚举

创建枚举来代表不同的数据库,也可以用字符串或其他形式表示

public enum DataSources {
    DS1, DS2;
}

2.1.3 数据库注解

因为我们使用同一套 mapper,所以用注解来区分使用的数据源

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
    DataSources value() default DataSources.DS1;
}

2.1.4 添加动态数据源

public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final ThreadLocal<DataSources> contextHolder = new ThreadLocal<>();

    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    public static void setDataSource(DataSources dataSource) {
        contextHolder.set(dataSource);
    }

    public static DataSources getDataSource() {
        return contextHolder.get();
    }

    public static void clearDataSource() {
        contextHolder.remove();
    }
}

2.1.5 添加切面处理类

@Aspect
@Component
public class DataSourceAspect implements Ordered {

    @Pointcut("@annotation(com.ricky.learn.framework.datasource.DataSource)")
    public void dataSourcePointCut() {

    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource ds = method.getAnnotation(DataSource.class);
        if (ds == null) {
            DynamicDataSource.setDataSource(DataSources.DS1);
        } else {
            DynamicDataSource.setDataSource(ds.value());
        }
        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

2.1.6 添加 mabatis 配置

@Configuration
@MapperScan(basePackages = {DynamicDataSourceConfig.BASE_PACKAGE}, sqlSessionFactoryRef = "dynamicSqlSessionFactory")
public class DynamicDataSourceConfig {

    static final String BASE_PACKAGE = "com.ricky.learn.modules.dynamicsource.dao";

    private static final String MAPPING_LOCATION = "classpath:mapper/dynamicsource/**/*.xml";

    @Primary
    @Bean("datasource1")
    @ConfigurationProperties("spring.datasource.source1")
    public DataSource source1DataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean("datasource2")
    @ConfigurationProperties("spring.datasource.source2")
    public DataSource source2DataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "dynamicDataSource")
    public DynamicDataSource DataSource(@Qualifier("datasource1") DataSource test1DataSource,
                                        @Qualifier("datasource2") DataSource test2DataSource) {
        Map<Object, Object> targetDataSource = new HashMap<>();
        targetDataSource.put(DataSources.DS1, test1DataSource);
        targetDataSource.put(DataSources.DS2, test2DataSource);
        DynamicDataSource dataSource = new DynamicDataSource ();
        dataSource.setTargetDataSources(targetDataSource);
        dataSource.setDefaultTargetDataSource(test1DataSource);
        return dataSource;
    }

    @Bean(name = "dynamicSqlSessionFactory")
    public SqlSessionFactory dynamicSqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dynamicDataSource);
        bean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources(MAPPING_LOCATION));
        return bean.getObject();
    }
}

2.1.7 添加 mapper 和 xml

public interface DynamicSourceMapper {
    User selectOne();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ricky.learn.modules.dynamicsource.dao.DynamicSourceMapper">

    <resultMap id="user" type="com.ricky.learn.modules.dynamicsource.User">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
    </resultMap>

    <select id="selectOne" resultMap="user">
      select * from user where id = 1
    </select>

</mapper>

2.1.8 添加 service

@Service
public class DynamicSourceService {

    private DynamicSourceMapper dynamicSourceMapper;

    @Autowired
    public DynamicSourceService(@Qualifier("dynamicSourceMapper") DynamicSourceMapper dynamicSourceMapper) {
        this.dynamicSourceMapper = dynamicSourceMapper;
    }

    @DataSource(DataSources.DS1)
    public User select1() {
        return dynamicSourceMapper.selectOne();
    }

    @DataSource(DataSources.DS2)
    public User select2() {
        return dynamicSourceMapper.selectOne();
    }
}

或者也可以手动设置

public User select2() {
    DynamicDataSource.setDataSource(DataSources.DS2);
    return dynamicSourceMapper.selectOne();
}

2.1.9 测试

@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class DynamicSourceServiceTest {

    @Autowired
    private DynamicSourceService service;

    @Test
    public void test(){
        System.out.println(service.select1());
        System.out.println(service.select2());
    }
}

分别从两个数据库查询出了 id = 1 的记录

2019-08-06 20:02:23.159  INFO 2192 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
Test{id='1', name='www', age=18}
2019-08-06 20:02:23.387  INFO 2192 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} inited
Test{id='1', name='1', age=1}

2.2 spring boot + mybaits + mybaits plus

参考 1.2

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容