静态多数据源:每个数据源对应一套 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