实现原理:java
关键点一 (去掉spring boot 默认的mybatis 自动配置)mysql
spring boot 集成 mybatis 内部默配置只能使用 单实例的数据源。可是在实现多数据源时。不能使用 默认的配置,首先要去掉spring boot 默认的mybatis 自动配置:web
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)spring
public class MybatisApplication {sql
public static void main(String[] args) {数据库
SpringApplication.run(MybatisApplication.class, args);服务器
}mybatis
}app
关键点二 (使用动态数据源)ide
若是咱们要实现多数据库读写分离,就须要查询或修改的数据的时候,调用对应的数据库,spring boot jdbc 提供了一个 AbstractRoutingDataSource,经过实现,咱们能够在操做数据库以前,动态的设置 数据源:
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
String dataSourceType = DatabaseContextHolder.getDataSourceType();
System.out.println("动态获取到的 数据源key == "+dataSourceType);
return dataSourceType;
}
}
关键点三 (使用 ThreadLoacl 实现信息传递)
DatabaseContextHolder 内部使用 ThreadLocal 类,经过ThreadLocal 能够给每一个线程设置和获取数据,起做用是在 AOP拦截到对应的 方法时,实现读写分离。
public class DatabaseContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal();
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
}
关键点四(配置多数据源)
多数据源 使用默认的 properties配置确定是不行的了,这里就须要咱们使用 自定义的配置。
在 resouce 目录下建立一个 multidatabase.properties 文件,内容以下。
#下面的main 和 read 数据将会以轮询的方式 被 访问。
#mian 循环main
#read 循环read 具体代码查看DataSourceAOP
#自定义多数据源配置
my.datasource.driver=com.mysql.jdbc.Driver
# main 表明主服务器 可读写 read = 只读
my.datasource[0].type=main
my.datasource[0].url=jdbc:mysql://rm-bp1jxj7m4egc7ce118o.mysql.rds.aliyuncs.com:3306/zllshop
my.datasource[0].username=lichuan
my.datasource[0].password=2018515
my.datasource[1].type=read
my.datasource[1].url=jdbc:mysql://rm-bp1jxj7m4egc7ce118o.mysql.rds.aliyuncs.com:3306/zllshop1
my.datasource[1].username=lichuan
my.datasource[1].password=2018515
my.datasource[2].type=read
my.datasource[2].url=jdbc:mysql://rm-bp1jxj7m4egc7ce118o.mysql.rds.aliyuncs.com:3306/zllshop
my.datasource[2].username=lichuan
my.datasource[2].password=2018515
my.datasource[3].type=main
my.datasource[3].url=jdbc:mysql://rm-bp1jxj7m4egc7ce118o.mysql.rds.aliyuncs.com:3306/zllshop1
my.datasource[3].username=lichuan
my.datasource[3].password=2018515
关键点五(读取配置文件)
@Configuration
@MapperScan(basePackages = "com.example.mybatis.mapper")
@PropertySource(value = "classpath:multidatabase.properties", encoding = "utf-8")
@ConfigurationProperties("my")
@Data
public class MultDataSource {
public static final String MAIN = "main";
public static final String READ = "read";
public List<String> mainKeys = new ArrayList<>();
public List<String> readKeys = new ArrayList<>();
@Value("${my.datasource.driver}")
private String driver;
/**
* 读取配置文件获取。
*/
private List<MyDatabase> datasource;
public DruidDataSource getDataSource(MyDatabase database) {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(database.getUrl());
druidDataSource.setUsername(database.getUsername());
druidDataSource.setDriverClassName(driver);
druidDataSource.setPassword(database.getPassword());
druidDataSource.setInitialSize(1);
druidDataSource.setMaxWait(6000);
druidDataSource.setMinIdle(8);
return druidDataSource;
}
@Bean
@Primary
public DynamicDataSource dataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
for (int i = 0; i < datasource.size(); i++) {
String type = datasource.get(i).getType();
DruidDataSource dataSource = getDataSource(datasource.get(i));
if (MAIN.equals(type)) {
mainKeys.add(MAIN+i);
targetDataSources.put(MAIN+i,dataSource);
} else {
readKeys.add(READ+i);
targetDataSources.put(READ+i,dataSource);
}
}
DynamicDataSource dataSource = new DynamicDataSource();
// 该方法是AbstractRoutingDataSource的方法
dataSource.setTargetDataSources(targetDataSources);
// 默认的datasource设置为myTestDbDataSource
dataSource.setDefaultTargetDataSource(targetDataSources.get(mainKeys.get(0)));
return dataSource;
}
}
关键点六(AOP 拦截代码)
UserMapper 类 这里没有贴出 mapper.xml
package com.example.mybatis.mapper;
@Component
@Mapper
public interface UserMapper {
int deleteByPrimaryKey(Long id);
int insert(User record);
User selectByPrimaryKey(Long id);
List<User> selectAll();
int updateByPrimaryKey(User record);
}
AOP 拦截类
@Aspect
@Component
public class DataSourceAop {
@Autowired
MultDataSource multDataSource;
@Before("execution(* com.example.mybatis.mapper..*.get*(..)) || " +
"execution(* com.example.mybatis.mapper..*.list*(..)) ||"+
"execution(* com.example.mybatis.mapper..*.select*(..))")
public void setReadDataSource(){
DatabaseContextHolder.setDataSourceType(getReadKey());
System.out.println("我是读");
}
@Before("execution(* com.example.mybatis.mapper..*.add*(..)) || " +
"execution(* com.example.mybatis.mapper..*.update*(..)) ||"+
"execution(* com.example.mybatis.mapper..*.insert*(..)) ||"+
"execution(* com.example.mybatis.mapper..*.delete*(..))")
public void setWriteDataSource(){
DatabaseContextHolder.setDataSourceType(getMainKey());
System.out.println("我是写");
}
/**
* 轮询方式
*/
int m = 0;
public String getMainKey(){
List<String> readKeys = multDataSource.getMainKeys();
m ++;
m = m%readKeys.size();
return readKeys.get( m );
}
int i = 0;
public String getReadKey(){
List<String> readKeys = multDataSource.getReadKeys();
i ++;
i = i%readKeys.size();
return readKeys.get( i );
}
}
须要的依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>