1、配置数据源,主从两个数据源
/**
* 多数据源配置(读写主从分离)
* @author liucc
*/
@Configuration
@EnableTransactionManagement
public class DataSourceConfig {
public static final String SLAVE_KEY = "slave";
public static final String MASTER_KEY = "master";
/**
* maste setting
*/
@Bean(initMethod = "init", destroyMethod = "close",name = "masterDataSource")
@ConfigurationProperties("user.master")
@Primary
public DruidDataSource masterDataSource() {
return new DruidDataSource();
}
/**
* slave setting
*/
@Bean(initMethod = "init", destroyMethod = "close",name = "slaveDataSource")
@ConfigurationProperties("user.slave")
public DruidDataSource slaveDataSource() {
return new DruidDataSource();
}
@Bean(name="dynamicDataSource")
public ReplicationRoutingDataSource dynamicDataSource(@Qualifier("masterDataSource") DruidDataSource master, @Qualifier("slaveDataSource") DruidDataSource slave) throws IOException {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceConfig.MASTER_KEY, master);
targetDataSources.put(DataSourceConfig.SLAVE_KEY, slave);
ReplicationRoutingDataSource dynamicDataSource = new ReplicationRoutingDataSource();
dynamicDataSource.setDefaultTargetDataSource(master);
dynamicDataSource.setTargetDataSources(targetDataSources);
return dynamicDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlFactory = new SqlSessionFactoryBean();
sqlFactory.setDataSource(dataSource);
sqlFactory.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
return sqlFactory.getObject();
}
@Bean
public DataSourceTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dataSource) throws IOException {
return new DataSourceTransactionManager(dataSource);
}
}
2、继承AbstractRoutingDataSource类,实现determineCurrentLookupKey方法动态选择数据源datasource
/**
* 多数据源路由
* @author liucc
*/
public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
private static final Logger logger = LoggerFactory.getLogger(ReplicationRoutingDataSource.class);
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
/**
* Slave if the current transaction is in read-only mode, or master.
*/
@Override
protected Object determineCurrentLookupKey() {
String lookupKey = contextHolder.get();
if(StringUtils.isBlank(lookupKey)){
lookupKey = DataSourceConfig.MASTER_KEY;
}
logger.info("connected DataSource :{}", lookupKey);
return lookupKey;
}
public static void selectSlave(){
contextHolder.set(DataSourceConfig.SLAVE_KEY);
}
public static void selectMaster(){
contextHolder.set(DataSourceConfig.MASTER_KEY);
}
}
3、自定义注解,通过在方法上添加注解来选择需要的数据源
@Documented
@Retention(RUNTIME)
@Target(METHOD)
public @interface SelectDataSource {
String value() default DataSourceConfig.MASTER_KEY;
}
4、通过Aspect切面来解析注解
/**
* 数据源自动选择切面
* @author liucc
*/
@Aspect
@Component
public class DataSourceSelectAspect {
private static final Logger logger = LoggerFactory.getLogger(DataSourceSelectAspect.class);
@Before("@annotation(selectDataSource)")
public void before(SelectDataSource selectDataSource) {
if(DataSourceConfig.SLAVE_KEY.equals(selectDataSource.value())){
ReplicationRoutingDataSource.selectSlave();
}
else{
ReplicationRoutingDataSource.selectMaster();
}
}
}
5、使用,直接在service方法上添加注解@SelectDataSource
@Service
public class TestService{
@Autowired
private UserInfoMapper userInfoMapper;
@SelectDataSource(DataSourceConfig.SLAVE_KEY)
public List<User> getUserInfo(String userid){
return userInfoMapper.get(userid);
}
}
附上
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="true"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="callSettersOnNulls" value="true"/>
<setting name="logImpl" value="LOG4J2" />
</settings>
</configuration>
application-db.properties
user.master.url=jdbc:127.0.0.1:3306/user_test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&failOverReadOnly=false&allowMultiQueries=true&useSSL=false
user.master.username=test
user.master.password=test
user.master.initialSize=3
user.master.minIdle=3
user.master.maxActive=5
user.master.maxWait=4
user.master.timeBetweenEvictionRunsMillis=60000
user.master.minEvictableIdleTimeMillis=300000
user.master.testWhileIdle=true
user.master.testOnBorrow=false
user.master.validationQuery=SELECT 1
user.master.driverClassName=com.mysql.jdbc.Driver
user.master.connectionInitSqls=set names utf8mb4
user.slave.url=jdbc:mysql://127.0.0.2:3306/user_test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&failOverReadOnly=false&allowMultiQueries=true&useSSL=false
user.slave.username=test
user.slave.password=test
user.slave.initialSize=3
user.slave.minIdle=3
user.slave.maxActive=5
user.slave.maxWait=4
user.slave.timeBetweenEvictionRunsMillis=60000
user.slave.minEvictableIdleTimeMillis=300000
user.slave.testWhileIdle=true
user.slave.testOnBorrow=false
user.slave.validationQuery=SELECT 1
user.slave.driverClassName=com.mysql.jdbc.Driver
user.slave.connectionInitSqls=set names utf8mb4