SpringBoot Mybatis之读写分离
2018-02-10 11:53
253 查看
原理
在执行不同的逻辑前,选择使用具体的DataSource;即将多个事先定义好的DataSource放在一个Map中,在需要DataSource的时候,使用具体的key来获取。先看一下javax.sql.DataSource接口:public interface DataSource extends CommonDataSource, Wrapper { Connection getConnection() throws SQLException; Connection getConnection(String username, String password) throws SQLException; }
再来看下实现类:
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean { private Map<Object, Object> targetDataSources; private Object defaultTargetDataSource; private boolean lenientFallback = true; private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup(); private Map<Object, DataSource> resolvedDataSources; private DataSource resolvedDefaultDataSource; @Override public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); } @Override public Connection getConnection(String username, String password) throws SQLException { return determineTargetDataSource().getConnection(username, password); } /** * Retrieve the current target DataSource. Determines the * {@link #determineCurrentLookupKey() current lookup key}, performs * a lookup in the {@link #setTargetDataSources targetDataSources} map, * falls back to the specified * {@link #setDefaultTargetDataSource default target DataSource} if necessary. * @see #determineCurrentLookupKey() */ protected DataSource determineTargetDataSource() { Assert.notNull(this.resolvedDataSources, "DataSource router not initialized"); Object lookupKey = determineCurrentLookupKey(); DataSource dataSource = this.resolvedDataSources.get(lookupKey); if (dataSource == null && (this.lenientFallback || lookupKey == null)) { dataSource = this.resolvedDefaultDataSource; } if (dataSource == null) { throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]"); } return dataSource; } /** * Determine the current lookup key. This will typically be * implemented to check a thread-bound transaction context. * <p>Allows for arbitrary keys. The returned key needs * to match the stored lookup key type, as resolved by the * {@link #resolveSpecifiedLookupKey} method. */ protected abstract Object determineCurrentLookupKey(); @Override public void afterPropertiesSet() { if (this.targetDataSources == null) { throw new IllegalArgumentException("Property 'targetDataSources' is required"); } this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size()); for (Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()) { Object lookupKey = resolveSpecifiedLookupKey(entry.getKey()); DataSource dataSource = resolveSpecifiedDataSource(entry.getValue()); this.resolvedDataSources.put(lookupKey, dataSource); } if (this.defaultTargetDataSource != null) { this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource); } } }
通过源码我们可以看到,创建DataSource的时候指定所有的targetDataSources(一个Map),然后需重写determineCurrentLookupKey()这个方法就可以拿到对应DataSource。
准备
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>1.5.4.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>6.0.4</version> </dependency>
实现
datasource.properties# 注:这个一定要加上,不然会报bean循环依赖? spring.datasource.initialize=false # master datasource master.datasource.driverClassName: com.mysql.cj.jdbc.Driver master.datasource.url: jdbc:mysql://localhost:3306/master?characterEncoding=utf8&serverTimezone=UTC&useSSL=false master.datasource.username: root master.datasource.password: 123456 # slave datasource slave.datasource.driverClassName: com.mysql.cj.jdbc.Driver slave.datasource.url: jdbc:mysql://localhost:3306/slave?characterEncoding=utf8&serverTimezone=UTC&useSSL=false slave.datasource.username: root slave.datasource.password: 123456
数据源配置
@Configuration @PropertySource(value = "classpath:datasource.properties") @MapperScan(value = "com.git.lee.spring.boot.example.mapper") @EnableTransactionManagement public class DataSourceConfig { @Bean(name = "masterDataSource") @ConfigurationProperties(prefix = "master.datasource") public DataSource masterDataSource() { return new org.apache.tomcat.jdbc.pool.DataSource(); } @Bean(name = "slaveDataSource") @ConfigurationProperties(prefix = "slave.datasource") public DataSource slaveDataSource() { return new org.apache.tomcat.jdbc.pool.DataSource(); } //动态数据源 @Bean(name = "dynamicDataSource") @Primary public DataSource getDataSource() { DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSources()); return dataSource; } private Map<Object, Object> targetDataSources() { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceType.MASTER.getType(), masterDataSource()); targetDataSources.put(DataSourceType.SLAVE.getType(), slaveDataSource()); return targetDataSources; } }
public class DynamicDataSource extends AbstractRoutingDataSource{ @Override protected Object determineCurrentLookupKey() { if (DataSourceHolder.getDataSource() != null) { return DataSourceHolder.getDataSource(); } return DataSourceType.MASTER.getType(); } }
public class DataSourceHolder { private static final ThreadLocal<String> holder = new ThreadLocal<>(); public static void putDataSource(DataSourceType dataSourceType) { holder.set(dataSourceType.getType()); } public static String getDataSource(){ return holder.get(); } }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented @Inherited public @interface DataSource { DataSourceType value() default DataSourceType.MASTER; }
切面植入
@Configuration @Aspect public class DataSourceAspect implements Ordered { //使用@DataSource注解的时候会设置具体使用的数据源 @Before(value = "@annotation(dataSource)") public void dataSourcePoint(JoinPoint jp, DataSource dataSource) { DataSourceHolder.putDataSource(dataSource.value()); } //order 越低,代表优先级越高 @Override public int getOrder() { return -1; } }
业务逻辑
@Service public class NodeService { @Autowired private NodeMapper nodeMapper; @DataSource(DataSourceType.SLAVE) @Transactional(rollbackFor = Exception.class) public void add(Node node) { nodeMapper.save(node); } @DataSource(DataSourceType.MASTER) public Node findById(int id) { return nodeMapper.findById(id); } }
这样就实现了一个简单的读写分离,当然如果想要完成更多的功能还需要做什么。
源码地址:https://github.com/shuaiweili/spring-boot-example
相关文章推荐
- spring-boot 速成(9) druid+mybatis 多数据源及读写分离的处理
- Spring Boot + Mybatis 多数据源配置实现读写分离
- Spring Boot + Mybatis 多数据源配置实现读写分离
- SpringBoot+MyBatis+MySQL读写分离
- SpringBoot+MyBatis+MySQL读写分离
- spring boot mybatis多数据源,后端进行数据库读写分离
- springboot+mybatis(读写分离)
- springboot+mybatis数据源的读写分离(mysql,后期连接设置,前期项目没做好的忽入会蒙的)
- spring-boot 速成(9) druid+mybatis 多数据源及读写分离的处理
- Spring Boot MyBatis 动态数据源切换、多数据源,读写分离
- springboot mybatis 读写分离集成
- spring boot + mybatis 多数据源,mysql服务主从读写分离
- springboot~mybatis里localdatetime序列化问题
- java鬼混笔记:springboot 9、springboot整合mybatis加上分页功能
- spring boot+mybatis+freemarker
- SpringBoot集成Mybatis(基于注解式开发)
- Spring Boot整合使用mybatis
- springboot+springmvc+mybatis 使用xml文件来写增查改的sql
- (二十二)SpringBoot之使用mybatis generator自动生成bean、mapper、mapper xml
- 从0开始学习SpringCould(5)--SpringBoot 集成Mybatis使用Druid数据源