工作老系統添加新的功能 需要調用其他數據庫的資源,應用多數據源,老大隻給一晚上解決,哎,總算是磨出來(看了很多好的博客),我也記錄一篇,也給後人方便。
原理簡單說明,詳細請看代碼分析。
1. 配置多個數據源 =》 每個數據源一個key。
2. 將多個數據源注入到DynmicDataSource(自己定義,必須實現AbstractRoutingDataSource)。
3. 設置默認數據源,其他由業務手動獲取key後動態注入達到切換數據源的效果。
4. 授人以魚不如授人以漁,我看了很多同學博客,懂了原理才能進步,希望不要以完成任務爲目的,多多分析,其它同學還可以有aop實現動態注入數據源,在這裏我時間緊迫,且工作需求不大,在這裏就不再分析aop情況。
1.pom 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mybatis.datasources</groupId>
<artifactId>spring-boot-datasources</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-datasources</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</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.0.29</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.application.properties 配置 主從數據源基本數據
#主數據源
primary.datasource.driverClassName = com.mysql.jdbc.Driver
primary.datasource.url = jdbc:mysql://localhost:3306/mybatis
primary.datasource.username = root
primary.datasource.password = root
#從數據源
customer.datasource.driverClassName = com.mysql.jdbc.Driver
customer.datasource.url = jdbc:mysql://localhost:3306/lucene
customer.datasource.username = root
customer.datasource.password = root
3.DataSourceType (數據源key)
public enum DataSourceType {
mybatis , lucene
}
4.DynamicDataSource (動態數據源 )
package com.mybatis.datasources.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* Created by guoyao on 2017/4/18.
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<DataSourceType> contextHolder=new ThreadLocal<>();
/**
* 動態數據源(繼承AbstractRoutingDataSource)
*/
@Override
protected Object determineCurrentLookupKey() {
return getDatabaseType();
}
public static void setDatabaseType(DataSourceType type) {
contextHolder.set(type);
}
public static DataSourceType getDatabaseType() {
return contextHolder.get();
}
}
5.動態數據源源碼分析 AbstractRoutingDataSource
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = this.determineCurrentLookupKey();
DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
if(dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if(dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
} else {
return dataSource;
}
}
protected abstract Object determineCurrentLookupKey();
6.mybatis-config 配置類編寫
package com.mybatis.datasources.config;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* Created by guoyao on 2017/4/18.
*/
@Configuration
@MapperScan("com.mybatis.datasources.mapper")
public class MyBatisConfig {
@Autowired
private PrimaryDataSourceProperty primaryDataSourceProperty;
@Autowired
private CustomerDataSourceProperty customerDataSourceProperty;
/**
* 創建數據源(方法名即實例化對象名,可以由對象名指定注入@Qualifier )
*/
@Bean
public DataSource dbDataSource1() throws Exception {
Properties props=new Properties();
props.put("driverClassName", primaryDataSourceProperty.getDriverClassName());
props.put("url", primaryDataSourceProperty.getUrl());
props.put("username", primaryDataSourceProperty.getUsername());
props.put("password", primaryDataSourceProperty.getPassword());
return DruidDataSourceFactory.createDataSource(props);
}
@Bean
public DataSource dbDataSource2() throws Exception {
Properties props=new Properties();
props.put("driverClassName", customerDataSourceProperty.getDriverClassName());
props.put("url", customerDataSourceProperty.getUrl());
props.put("username", customerDataSourceProperty.getUsername());
props.put("password", customerDataSourceProperty.getPassword());
return DruidDataSourceFactory.createDataSource(props);
}
/**
* @Primary 該註解表示在同一個接口有多個實現類可以注入的時候,默認選擇哪一個,而不是讓@autowire註解報錯
* @Qualifier 根據名稱進行注入,通常是在具有相同的多個類型的實例的一個注入(例如有多個DataSource類型的實例)
*/
@Bean
@Primary
public DynamicDataSource ds(@Qualifier("dbDataSource1") DataSource dbDataSource1,
@Qualifier("dbDataSource2") DataSource dbDataSource2) {
Map<Object, Object> targetDataSources=new HashMap<>();
targetDataSources.put(DataSourceType.mybatis, dbDataSource1);
targetDataSources.put(DataSourceType.lucene, dbDataSource2);
DynamicDataSource dataSource=new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);
dataSource.setDefaultTargetDataSource(dbDataSource1);
return dataSource;
}
/**
* 根據數據源創建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {
SqlSessionFactoryBean fb=new SqlSessionFactoryBean();
fb.setDataSource(ds);
return fb.getObject();
}
/**
* 配置事務管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource ds) throws Exception {
return new DataSourceTransactionManager(ds);
}
}
7.Application ***一定要關閉datasource 自動配置
package com.mybatis.datasources
import com.mybatis.datasources.config.CustomerDataSourceProperty
import com.mybatis.datasources.config.PrimaryDataSourceProperty
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
import org.springframework.boot.context.properties.EnableConfigurationProperties
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) //關閉datasource的自動注入
@EnableConfigurationProperties({CustomerDataSourceProperty.class, PrimaryDataSourceProperty.class}) //注入配置文件
public class SpringBootDatasourcesApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDatasourcesApplication.class, args)
}
}
8.測試
package com.mybatis.datasources.mapper;
import com.mybatis.datasources.dataobject.Book;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* Created by guoyao on 2017/4/18.
*/
@Mapper
public interface BookMapper {
List<Book> findAll();
}
@Mapper
public interface UserMapper {
List<User> findAll();
}
@Service("bookService")
public class BookServiceImpl implements BookService {
@Autowired
private BookMapper bookDao ;
@Override
public List<Book> findAll() {
DynamicDataSource.setDatabaseType(DataSourceType.lucene);
return bookDao.findAll();
}
}
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userDao ;
@Override
public List<User> findAll() {
return userDao.findAll();
}
}
/**
* Created by guoyao on 2017/4/18.
*/
@RestController
public class TestController {
@Autowired
private UserService userService;
@Autowired
private BookService bookService;
@RequestMapping("/getUser")
public List<User> getUser() {
return userService.findAll();
}
@RequestMapping("/getBook")
public List<Book> getBook() {
return bookService.findAll();
}
}