springboot多資料來源實現
阿新 • • 發佈:2019-06-26
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