實現原理: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>