一、配置數據源,主從兩個數據源java
/** * 多數據源配置(讀寫主從分離) * @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); } }
二、繼承AbstractRoutingDataSource類,實現determineCurrentLookupKey方法動態選擇數據源datasourcemysql
/** * 多數據源路由 * @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); } }
三、自定義註解,經過在方法上添加註解來選擇須要的數據源sql
@Documented @Retention(RUNTIME) @Target(METHOD) public @interface SelectDataSource { String value() default DataSourceConfig.MASTER_KEY; }
四、經過Aspect切面來解析註解mybatis
/** * 數據源自動選擇切面 * @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(); } } }
五、使用,直接在service方法上添加註解@SelectDataSourceapp
@Service public class TestService{ @Autowired private UserInfoMapper userInfoMapper; @SelectDataSource(DataSourceConfig.SLAVE_KEY) public List<User> getUserInfo(String userid){ return userInfoMapper.get(userid); } }
附上ide
mybatis-config.xmlui
<?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.propertiesurl
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