1、创建一个数据源配置文件
// 假设该文件名称为datasource.properties
# druid setting (主库配置) jdbcm.type=com.alibaba.druid.pool.DruidDataSource jdbcm.driverClassName=com.mysql.jdbc.Driver jdbcm.url=jdbc:mysql://ip:port/database_name?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true jdbcm.username=xxx jdbcm.password=xxxx
# druid setting (从库配置)
jdbcs.type=com.alibaba.druid.pool.DruidDataSource
jdbcs.driverClassName=com.mysql.jdbc.Driver
jdbcs.url=jdbc:mysql://ip:port/database_name?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbcs.username=xxx
jdbcs.password=xxxx
2、创建一个数据源配置类
@Configuration
@PropertySource("classpath:datasource_prod.properties") @MapperScan(basePackages="xxx") // 根据实际的业务代码路径来配置 @Profile("prod") // 根据bootstrap.yml的active配置项来确定 public class MyBatisBaseProdConfig implements EnvironmentAware { private Environment env; @Override public void setEnvironment(final Environment environment) { this.env = environment; } /** * description:创建数据源(主库) * @return DataSource对象 */ @Bean public DataSource majorDbDataSource() throws Exception { StandardPBEStringEncryptor spe = new StandardPBEStringEncryptor(); spe.setPassword(env.getProperty("jasypt.encryptor.password")); Properties props = new Properties(); props.put("url", env.getProperty("jdbcm.url")); props.put("username", spe.decrypt(env.getProperty("jdbcm.username"))); props.put("password", spe.decrypt(env.getProperty("jdbcm.password"))); this.setCommonJDBCProperties(props); return DruidDataSourceFactory.createDataSource(props); }
/** * description:创建数据源(从库) * @return DataSource对象 */ @Bean public DataSource secondaryDbDataSource() throws Exception { StandardPBEStringEncryptor spe = new StandardPBEStringEncryptor(); spe.setPassword(env.getProperty("jasypt.encryptor.password")); Properties props = new Properties(); props.put("url", env.getProperty("jdbcs.url")); props.put("username", spe.decrypt(env.getProperty("jdbcs.username"))); props.put("password", spe.decrypt(env.getProperty("jdbcs.password"))); this.setCommonJDBCProperties(props); return DruidDataSourceFactory.createDataSource(props); }
/** * 设置动态数据源 * @param myTestDb2DataSource * @return DynamicDataSource对象 */ @Bean @Primary public DynamicDataSource dataSource(@Qualifier("majorDbDataSource") DataSource majorDbDataSource,
@Qualifier("secondaryDbDataSource") DataSource secondaryDbDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(16); targetDataSources.put("major", majorDbDataSource);
targetDtatSources.put("secondary", secondaryDbDataSource); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSources); // 设置默认数据源 dataSource.setDefaultTargetDataSource(majorDbDataSource); return dataSource; } /** * 设置xml路径 **/ @Bean(name = "sqlSessionFactory") public SqlSessionFactory sqlSessionFactoryBean(DynamicDataSource ds) { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(ds); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { bean.setMapperLocations(resolver.getResources("classpath:*.xml")); // 根据实际情况来设置xml路径 return bean.getObject(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 配置事务管理器 */ @Bean public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception { return new DataSourceTransactionManager(dataSource); } /** * 设置共用的jdbc属性 * @param props */ private void setCommonJDBCProperties(Properties props) { props.put("initialSize", env.getProperty("jdbc.initialSize")); props.put("minIdle", env.getProperty("jdbc.minIdle")); props.put("maxActive", env.getProperty("jdbc.maxActive")); props.put("maxWait", env.getProperty("jdbc.maxWait")); props.put("validationQuery", env.getProperty("jdbc.validationQuery")); props.put("testOnBorrow", env.getProperty("jdbc.testOnBorrow")); props.put("testOnReturn", env.getProperty("jdbc.testOnReturn")); props.put("testWhileIdle", env.getProperty("jdbc.testWhileIdle")); props.put("timeBetweenEvictionRunsMillis", env.getProperty("jdbc.timeBetweenEvictionRunsMillis")); props.put("minEvictableIdleTimeMillis", env.getProperty("jdbc.minEvictableIdleTimeMillis")); props.put("removeAbandoned", env.getProperty("jdbc.removeAbandoned")); props.put("removeAbandonedTimeout", env.getProperty("jdbc.removeAbandonedTimeout")); props.put("logAbandoned", env.getProperty("jdbc.logAbandoned")); props.put("poolPreparedStatements", env.getProperty("jdbc.poolPreparedStatements")); props.put("maxPoolPreparedStatementPerConnectionSize", env.getProperty("jdbc.maxPoolPreparedStatementPerConnectionSize")); props.put("filters", env.getProperty("jdbc.filters")); } }
3、创建一个数据源上下文保存类
public class DatabaseContextHolder { private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); public static String getDatabaseType(){ return CONTEXT_HOLDER.get(); } public static void setDatabaseType(String type) { CONTEXT_HOLDER.set(type); } public static void clear() { CONTEXT_HOLDER.remove(); }
4、创建一个动态数据类
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DatabaseContextHolder.getDatabaseType(); } }
5、创建一个AOP类,实现动态切换数据源
@Aspect @Component @Order(1) public class DataSourceAspect { /** * 对业务层方法前切换数据源 * @param point 切点 */ @Before("execution(*..service..*.*(..))") // 根据实际情况进行调整 public void setDataSourceKey(JoinPoint point){ if(point.getTarget().getClass().equals(XXClassImpl.class)){ DatabaseContextHolder.setDatabaseType("secondary"); } else { DatabaseContextHolder.setDatabaseType("major"); } } /** * 在业务层方法之后执行 * @param point 切点 */ @After("execution(*..service..*.*(..))") // 根据实际情况进行调整 public void after(JoinPoint point){ DatabaseContextHolder.clear(); } }