一、AOP 实现
AOP 实现多数据源,可读写分离
1 配置文件
dynamic-db.master.driver-class-name = com.mysql.jdbc.Driver dynamic-db.master.jdbc-url = jdbc:mysql: dynamic-db.master.username = xxx dynamic-db.master.password = xxx
dynamic-db.slave.driver-class-name = com.mysql.jdbc.Driver dynamic-db.slave.jdbc-url = jdbc:mysql: dynamic-db.slave.username = xxx dynamic-db.slave.password = xxx
|
2 ContextHolder
管理 DataSource
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static List<Object> dataSourceKeys = new ArrayList<>();
public static void setDataSourceKey(String key) { CONTEXT_HOLDER.set(key); }
public static String getDataSourceKey() { String key = CONTEXT_HOLDER.get(); return key == null ? "master" : key; }
public static void clearDataSourceKey() { CONTEXT_HOLDER.remove(); }
public static boolean containDataSourceKey(String key) { return dataSourceKeys.contains(key); } }
|
3 注册动态配置
继承 AbstractRoutingDataSource
public class DynamicRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceKey(); } }
|
4 加载配置
@Slf4j @Configuration public class DataSourceConfigurer { @Bean("master") @Primary @ConfigurationProperties(prefix = "dynamic-db.master") public DataSource master() { return DataSourceBuilder.create().build(); } @Bean("slave") @ConfigurationProperties(prefix = "dynamic-db.slave") public DataSource slave() { return DataSourceBuilder.create().build(); }
@Bean("dynamicDataSource") public DataSource dynamicDataSource() { DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource(); Map<Object, Object> dataSourceMap = new HashMap<>(2); dataSourceMap.put("master", master()); dataSourceMap.put("slave", slave());
dynamicRoutingDataSource.setDefaultTargetDataSource(master()); dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
DynamicDataSourceContextHolder.dataSourceKeys.addAll(dataSourceMap.keySet()); return dynamicRoutingDataSource; }
@Bean @ConfigurationProperties(prefix = "mybatis") public SqlSessionFactoryBean sqlSessionFactoryBean() { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dynamicDataSource()); return sqlSessionFactoryBean; }
@Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dynamicDataSource()); } }
|
5 注解
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource { String value(); }
|
6 Aspect
要用 @Order(0) 注解让这个切面的优先级最高,以免被其他切面(如事务管理器)影响
@Aspect @Component @Order(0) public class DynamicDataSourceAspect { @Before("@annotation(targetDataSource))") public void switchDataSource(JoinPoint point, TargetDataSource targetDataSource) { if (DynamicDataSourceContextHolder.containDataSourceKey(targetDataSource.value())){ DynamicDataSourceContextHolder.setDataSourceKey(targetDataSource.value()); } } //还原 @After("@annotation(targetDataSource))") public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) { DynamicDataSourceContextHolder.clearDataSourceKey(); } }
|
7 实例
读写分离
@Repository public interface InterMsgMapper { @InsertProvider(type = InterMsgProvider.class, method = "insert") int insert(InterMsg interMsg);
@TargetDataSource("slave") @SelectProvider(type = InterMsgProvider.class, method = "findMessages") List<InterMsg> findMessages(Long userId, Integer limit, Long lastId, Long currentTime); }
|
二、普通配置
1 配置文件
dynamic-db.master.driver-class-name = com.mysql.jdbc.Driver dynamic-db.master.jdbc-url = jdbc:mysql: dynamic-db.master.username = xxx dynamic-db.master.password = xxx
dynamic-db.slave.driver-class-name = com.mysql.jdbc.Driver dynamic-db.slave.jdbc-url = jdbc:mysql: dynamic-db.slave.username = xxx dynamic-db.slave.password = xxx
|
2 DataSourceConfig
@Configuration public class DataSourceConfig { @Bean @Scope("prototype") @ConfigurationProperties("mybatis.configuration") public org.apache.ibatis.session.Configuration globalConfiguration(){ return new org.apache.ibatis.session.Configuration(); }
@Bean("masterDataSource") @Primary @ConfigurationProperties(prefix = "dynamic-db.master") public DataSource master() { return DataSourceBuilder.create().build(); } @Bean("slaveDataSource") @ConfigurationProperties(prefix = "dynamic-db.slave") public DataSource slave() { return DataSourceBuilder.create().build(); }
@Bean(name="tranMagMaster") public PlatformTransactionManager bfTransactionManager(@Qualifier("masterDataSource")DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name="tranMagSlave") public PlatformTransactionManager bfscrmTransactionManager(@Qualifier("slaveDataSource")DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
|
3 JdbcTemplatesConfig
@Configuration public class JdbcTemplatesConfig extends DataSourceConfig {
@Bean(name="masterJdbcTemplate") public JdbcTemplate masterJdbcTemplate(@Qualifier("masterDataSource") DataSource dataSource){ return new JdbcTemplate(dataSource); }
@Bean(name="slaveJdbcTemplate") public JdbcTemplate slaveJdbcTemplate(@Qualifier("slaveDataSource") DataSource dataSource){ return new JdbcTemplate(dataSource); } }
|
3 MybatisConfigMaster
@Configuration @MapperScan(basePackages = "com.xxx.mapper.master", sqlSessionTemplateRef = "masterSqlSessionTemplate" ) public class MybatisConfigMaster extends DataSourceConfig { @Bean SqlSessionFactory masterSqlSessionFactory(){
SqlSessionFactory sqlSessionFactory = null; try { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setConfiguration(globalConfiguration()); sqlSessionFactoryBean.setDataSource(masterDataSource()); sqlSessionFactory = sqlSessionFactoryBean.getObject(); } catch (Exception e) { e.printStackTrace(); } return sqlSessionFactory; }
@Bean SqlSessionTemplate masterSqlSessionTemplate(){ return new SqlSessionTemplate(masterSqlSessionFactory()); } }
|
4 MybatisConfigSlave
@Configuration @MapperScan(basePackages = "com.xxx.mapper.slave", sqlSessionTemplateRef = "slaveSqlSessionTemplate" ) public class MybatisConfigSlave extends DataSourceConfig { @Bean SqlSessionFactory slaveSqlSessionFactory(){
SqlSessionFactory sqlSessionFactory = null; try { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setConfiguration(globalConfiguration()); sqlSessionFactoryBean.setDataSource(slaveDataSource()); sqlSessionFactory = sqlSessionFactoryBean.getObject(); } catch (Exception e) { e.printStackTrace(); } return sqlSessionFactory; }
@Bean SqlSessionTemplate slaveSqlSessionTemplate(){ return new SqlSessionTemplate(slaveSqlSessionFactory()); } }
|
5 使用
在 @MapperScan 配置的目录下写 mapper 就行了