1.pom
mybatis-starter版本只能选2开头的版本,选3开头的就报错
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
<scope>runtime</scope>
</dependency>
<!-- 切面依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.yml配置
server:
port: 9005
servlet:
context-path: /
# CREATE USER 'test1'@'localhost' IDENTIFIED BY 'test1'; 创建test1 用户
#FLUSH PRIVILEGES; 刷新
#grant all privileges on study.* to 'test1'@'localhost' with grant option; 把库study 授权给test1
spring:
datasource:
druid:
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/study?characterEncoding=utf-8
username: test1
password: test1
#type: com.alibaba.druid.pool.DruidDataSource
slave:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/studyslave?characterEncoding=utf-8
username: test1
password: test1
#type: com.alibaba.druid.pool.DruidDataSource
initial-size: 5 #初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
max-active: 10 #最大连接池数量
min-idle: 5 #最小连接池数量
stat-view-servlet:
enabled: true # 是否启用StatvViewServlet(监控页面),默认是false
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
mapper-locations: mapper/*Mapper.xml
configuration:
map-underscore-to-camel-case: true # 驼峰命名
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
type-aliases-package: study.entity
3. 初始化dataSource
package study.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 项目初始化的时候将各个数据源注册到DynamicDataSource 中,其中firstDataSource作为默认数据源
*/
@Configuration
public class DynamicDataSourceConfig {
private static String MASTER = "master";
private static String SLAVE = "slave";
/**
* 生成masterDataSource
* @return
*/
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
public DataSource slaveDataSource(){
return DruidDataSourceBuilder.create().build();
}
/**
* 在一个有多个实例的Bean中使用@Primary注解来标记主要实例
* @param masterDataSource
* @param slaveDataSource
* @return
*/
@Bean
@Primary
public DynamicRoutingDataSource dynamicDataSource(DataSource masterDataSource, DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(MASTER, masterDataSource);
targetDataSources.put(SLAVE, slaveDataSource);
return new DynamicRoutingDataSource(masterDataSource, targetDataSources);
}
}
4.继承AbstractRoutingDataSource
package study.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> DataSourceContextHolder = new ThreadLocal<>();
/***
*
* DynamicDataSourceConfig调用了这构造函数
* @param defaultTargetDataSource
* @param targetDataSources
*/
public DynamicRoutingDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
// 默认数据源
super.setDefaultTargetDataSource(defaultTargetDataSource);
// 所有的数据源
super.setTargetDataSources(targetDataSources);
// 初始化时,给resolvedDataSources 赋值
super.afterPropertiesSet();
}
public static void setDataSource(String dataSource) {
DataSourceContextHolder.set(dataSource);
}
public static String getDataSource() {
return DataSourceContextHolder.get();
}
public static void clearDataSource() {
DataSourceContextHolder.remove();
}
/**
* 该类中还有一个determineTargetDataSource方法,
* 是根据lookupkey从Map中获取对应的数据源,如果没有获取到,则使用默认的数据源
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
}
5.定义切换数据源的注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DS {
String value() default "";
}
/**
* 定义切面,获取注解对应的dataSource
*/
package study.config.annotation;
import com.alibaba.druid.util.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import study.config.DynamicRoutingDataSource;
import java.lang.reflect.Method;
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@annotation(study.config.annotation.DS)")
public void logPointCut() {}
@Before("logPointCut()")
public void doBefore(){
System.out.println("方法执行前。");
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature)point.getSignature();
Method method = signature.getMethod();
DS ds = method.getAnnotation(DS.class);
if(ds != null && !StringUtils.isEmpty(ds.value())){
DynamicRoutingDataSource.setDataSource(ds.value());
}
try {
return point.proceed();
} finally {
DynamicRoutingDataSource.clearDataSource();
}
}
}
6.mapper定义
@Mapper
public interface UserMapper {
@DS("master")
@Select("select id as id, user_name as userName, password as password,create_time as createTime from t_user where id = #{id}")
public UserInfo selectByIdFromMaster(@Param("id") Integer id);
@Select("select id as id, user_name as userName, password as password,create_time as createTime from t_user where id = #{id}")
public UserInfo selectByIdDefault(@Param("id") Integer id);
@DS("slave")
@Select("select id as id, user_name as userName, password as password,create_time as createTime from t_user where id = #{id}")
public UserInfo selectByIdFromSlave(@Param("id") Integer id);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
private Integer id;
private String userName;
private String password;
private Date createTime;
private Date updateTime;
public UserInfo(String userName, String password, Date createTime, Date updateTime) {
this.userName = userName;
this.password = password;
this.createTime = createTime;
this.updateTime = updateTime;
}
}
6.service类
@Slf4j
@Service
public class UserServiceImpl {
@Resource
private UserMapper userMapper;
public List<UserInfo> selectById(int id){
UserInfo userInfo1 = selectByIdDefault(2);
UserInfo userInfo2 = selectByIdFromMaster(1);
UserInfo userInfo3 = selectByIdFromSlave(5);
log.info("userInfo1={}, userInfo2={}, userInfo3={}", userInfo1, userInfo2, userInfo3);
return Arrays.asList(userInfo1, userInfo2);
}
/**
* 从master库查询
* @param id
* @return
*/
public UserInfo selectByIdFromMaster(int id) {
UserInfo userInfo = userMapper.selectByIdFromMaster(id);
return userInfo;
}
/**
* 从默认的库:master
* @param id
* @return
*/
public UserInfo selectByIdDefault(int id) {
UserInfo userInfo = userMapper.selectByIdDefault(id);
return userInfo;
}
/**
* 从slave库查询
* @param id
* @return
*/
public UserInfo selectByIdFromSlave(int id) {
return userMapper.selectByIdFromSlave(id);
}
}