1、pom中添加
<!--spring boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.29</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2、配置文件中添加
############ 主库datasource master ###########
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=nature
spring.datasource.password=#####
spring.datasource.url=jdbc:mysql://192.168.x.xxx:3306/nature_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
###########从库 datasource slave ############ #spring-boot2.0中datasource-slave不能写成datasourceSlave不然会报错
spring.datasource-slave.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource-slave.driver-class-name=com.mysql.jdbc.Driver
spring.datasource-slave.username=health
spring.datasource-slave.password=####
spring.datasource-slave.url=jdbc:mysql://192.168.x.xxx:3306/health_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
#################jpa###########################
spring.jpa.show-sql=false
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
3、定义支持的数据源key
package com.example.demo.constant;
/**
* @author zhuenbang
* @description 定义支持的数据源key
* @date 2018/8/13 11:26
*/
public interface DataSources {
/**
*主库
*/
String MASTER_DB = "masterDB";
/**
*从库
*/
String SLAVE_DB = "slaveDB";
}
4、自定义数据库注解
package com.example.demo.anno;
import com.example.demo.constant.DataSources;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author zhuenbang
* @description 自定义数据库注解
* @date 2018/8/13 11:43
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface RouteDataSource {
String value() default DataSources.MASTER_DB; //默认为主数据源
}
5、ThreadLocal安全的管理当前进程使用的数据源连接
package com.example.demo.config;
import com.example.demo.constant.DataSources;
import org.jboss.logging.Logger;
/**
* @author zhuenbang
* @description
* @date 2018/8/13 12:57
*/
public class DataSourceContextHolder {
private static Logger logger = Logger.getLogger(DataSourceContextHolder.class);
/**
* 默认数据源
*/
public static final String DEFAULT_DATASOURCE = DataSources.MASTER_DB;
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
// 设置数据源名
public static void setDB(String dbType) {
logger.debugv("切换到{}数据源", dbType);
contextHolder.set(dbType);
}
// 获取数据源名
public static String getDB() {
return (contextHolder.get());
}
// 清除数据源名
public static void clearDB() {
contextHolder.remove();
}
}
6、自定义切库注解的方法进行拦截,动态的选择数据源
package com.example.demo.apsect;
/**
* @author zhuenbang
* @description
* @date 2018/8/13 12:54
*/
import com.example.demo.anno.RouteDataSource;
import com.example.demo.config.DataSourceContextHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.jboss.logging.Logger;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class DynamicDataSourceAspect {
private static Logger logger = Logger.getLogger(DynamicDataSourceAspect.class);
@Before("@annotation(com.example.demo.anno.RouteDataSource)")
public void beforeSwitchDS(JoinPoint point){
//获得当前访问的class
Class<?> className = point.getTarget().getClass();
//获得访问的方法名
String methodName = point.getSignature().getName();
//得到方法的参数的类型
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DEFAULT_DATASOURCE;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
// 判断是否存在@DS注解
if (method.isAnnotationPresent(RouteDataSource.class)) {
RouteDataSource annotation = method.getAnnotation(RouteDataSource.class);
// 取出注解中的数据源名
dataSource = annotation.value();
}
} catch (Exception e) {
logger.error("routing datasource exception, " + methodName, e);
}
// 切换数据源
DataSourceContextHolder.setDB(dataSource);
}
@After("@annotation(com.example.demo.anno.RouteDataSource)")
public void afterSwitchDS(JoinPoint point){
DataSourceContextHolder.clearDB();
}
}
7、定义数据库实体类并配置为多数据源的形式
package com.example.demo.config;
import com.alibaba.druid.pool.DruidDataSource;
import javax.sql.DataSource;
import com.example.demo.constant.DataSources;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
/**
* @author zhuenbang
* @description 定义数据库实体类并配置为多数据源的形式
* @date 2018/8/13 11:28
*/
@Configuration
public class DatasourceConfig {
private final static String MASTER_DATASOURCE_KEY = DataSources.MASTER_DB;
private final static String SLAVE_DATASOURCE_KEY = DataSources.SLAVE_DB;
//destroy-method="close"的作用是当数据库连接不使用的时候,就把该连接重新放到数据池中,方便下次使用调用.
@Value("${spring.datasource-slave.type}")
private Class<? extends DataSource> dataSourceType;
@Primary
@Qualifier(MASTER_DATASOURCE_KEY)
@Bean(destroyMethod = "close", name = DataSources.MASTER_DB, initMethod="init")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
System.out.println("-------------------- primaryDataSource初始化 ---------------------");
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean(destroyMethod = "close", name = DataSources.SLAVE_DB,initMethod="init")
@Qualifier(SLAVE_DATASOURCE_KEY)
@ConfigurationProperties(prefix = "spring.datasource-slave")
public DataSource dataSourceSlave() {
System.out.println("-------------------- slaveDataSource初始化---------------------");
return DataSourceBuilder.create().type(dataSourceType).build();
}
}
8、取出切面里设置的数据源key
package com.example.demo.config;
import org.jboss.logging.Logger;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @author zhuenbang
* @description
* @date 2018/8/13 13:03
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static Logger logger = Logger.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
logger.debugv("数据源为{}", DataSourceContextHolder.getDB());
return DataSourceContextHolder.getDB();
}
}
9、动态数据源
package com.example.demo.config;
/**
* @author zhuenbang
* @description
* @date 2018/8/13 12:15
*/
import com.example.demo.constant.DataSources;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import javax.annotation.Resource;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Map;
@SuppressWarnings("all")
@Configuration
@EnableConfigurationProperties(JpaProperties.class)
@EnableJpaRepositories(value = "com.example.demo.db.repository",
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager")
public class MyjpaConfig {
@Autowired
private JpaProperties jpaProperties;
@Resource(name = DataSources.MASTER_DB)
private DataSource masterDB;
@Resource(name = DataSources.SLAVE_DB)
private DataSource slaveDB;
/**
* 动态数据源
*/
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(masterDB);
// 配置多数据源
Map<Object, Object> dsMap = Maps.newHashMap();
dsMap.put(DataSources.MASTER_DB, masterDB);
dsMap.put(DataSources.SLAVE_DB, slaveDB);
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
@Bean(name = "entityManagerFactoryBean")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
Map<String, String> properties = jpaProperties.getProperties();
properties.put("hibernate.physical_naming_strategy",
"org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
return builder
.dataSource(dynamicDataSource())
.properties(properties)
.packages("com.example.demo.db.entity") //设置实体类所在位置
.build();
}
@Primary
@Bean(name = "entityManagerFactory")
public EntityManagerFactory entityManagerFactory(EntityManagerFactoryBuilder builder) {
return this.entityManagerFactoryBean(builder).getObject();
}
@Primary
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactory(builder));
}
}
10、单元测试
@RouteDataSource 为空的话默认为主数据源