spring-boot+mybatis 插件配置多个数据源实现读写分离

SpringBoot使用注解整合Mybatis配置多个数据源实现读写分离

  • 数据库【mysql】
  • 数据源【Druid】阿里开源
  1. maven引入依赖jar
    <dependency>
    			<groupId>mysql</groupId>
    			<artifactId>mysql-connector-java</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.mybatis.spring.boot</groupId>
    			<artifactId>mybatis-spring-boot-starter</artifactId>
    			<version>2.1.1</version>
    		</dependency>
    		<dependency>
    			<groupId>com.alibaba</groupId>
    			<artifactId>druid</artifactId>
    			<version>1.1.9</version>
    		</dependency>

     

  2. 多个数据源配置
    @Configuration
    public class StudyConfiguration {
    	
    	@Bean(name = "readDataSouce")
    	@ConfigurationProperties(prefix = "spring.primary.druid")
    	public DataSource readDataSouce() {
    		return new DruidDataSource();
    	}
    	
    	@Bean(name = "writeDataSouce")
    	@ConfigurationProperties(prefix = "spring.secondary.druid")
    	public DataSource writeDataSouce() {
    		return new DruidDataSource();
    	}
    	
    }

     

  3. 属性文件配置applicatin.properties
    spring.primary.druid.url=jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=utf8
    spring.primary.druid.driverClassName=com.mysql.jdbc.Driver
    spring.primary.druid.username=root
    spring.primary.druid.password=root
    
    spring.secondary.druid.url=jdbc:mysql://localhost:3306/database1?useUnicode=true&characterEncoding=utf8
    spring.secondary.druid.driverClassName=com.mysql.jdbc.Driver
    spring.secondary.druid.username=root
    spring.secondary.druid.password=root

     

  4. DynamicDataSource继承spring的AbstractRoutingDataSouce类用来切换数据源,在整合mybatis的框架最终通过determineCurrentLookupKey方法决定当前的执行计划选择哪一个
    package com.tao.study.dynamic;
    
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    import javax.sql.DataSource;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    import org.springframework.stereotype.Component;
    
    @Component
    public class DynamicDataSouce extends AbstractRoutingDataSource {
    	
    	@Autowired
    	@Qualifier("readDataSouce")
    	private DataSource readDataSource;
    	@Autowired
    	@Qualifier("writeDataSouce")
    	private DataSource writeDataSource;
    
    	@Override
    	public void afterPropertiesSet() {
    		if(writeDataSource==null) {
    			throw new IllegalArgumentException("write data souce can not be empty");
    		}
    		setDefaultTargetDataSource(writeDataSource);
    		Map<Object,Object> targetDataSources = new ConcurrentHashMap<Object, Object>();
    		targetDataSources.put(DynamicDataSouceType.WRITE, writeDataSource);
    		if(readDataSource!=null) {
    			targetDataSources.put(DynamicDataSouceType.READ, readDataSource);
    		}
    		setTargetDataSources(targetDataSources);
    		super.afterPropertiesSet();
    	}
    
    
    
    	@Override
    	protected Object determineCurrentLookupKey() {
    		DynamicDataSouceType type = DynamicDataSouceHolder.getDataSouceType();
    		if(type==null) {
    			return DynamicDataSouceType.WRITE;
    		}
    		return type;
    	}
    
    }
    

     

  5. DynamicDataSouceHolder类用来存储需要切换的数据源类型,使用ThreadLocal线程本地存储确保安全性
    package com.tao.study.dynamic;
    
    public class DynamicDataSouceHolder {
    
    	public final static ThreadLocal<DynamicDataSouceType> currentDynamicDataSouceType = new ThreadLocal<DynamicDataSouceType>();
    	
    	public static void putDataSouceType(DynamicDataSouceType type) {
    		currentDynamicDataSouceType.set(type);
    	}
    	
    	public static DynamicDataSouceType getDataSouceType() {
    		return currentDynamicDataSouceType.get();
    	}
    }
    

     

  6. DynamicDataSouceType枚举定义多个数据源类型
    package com.tao.study.dynamic;
    
    public enum DynamicDataSouceType {
    	READ,WRITE
    }
    

     

  7. DynamicPlugin类实现mybatis的Interceptor的接口,在执行操作数据库的业务流程中对Query,Update等类型进行处理,可以根据业务需求选择相应的数据源,实现多个数据源,读写分离
    package com.tao.study.dynamic;
    
    import org.apache.ibatis.executor.Executor;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.mapping.SqlCommandType;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.Intercepts;
    import org.apache.ibatis.plugin.Invocation;
    import org.apache.ibatis.plugin.Signature;
    import org.apache.ibatis.session.ResultHandler;
    import org.apache.ibatis.session.RowBounds;
    import org.springframework.stereotype.Component;
    
    @Component
    @Intercepts({
    	@Signature(
    		  type= Executor.class,
    		  method = "update",
    		  args = {MappedStatement.class,Object.class}),
    	@Signature(
    			  type= Executor.class,
    			  method = "query",
    			  args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})
    	})
    public class DynamicPlugin implements Interceptor {
    
    	public Object intercept(Invocation invocation) throws Throwable {
    		Object[] objs = invocation.getArgs();
    		MappedStatement ms =  (MappedStatement) objs[0];
    		DynamicDataSouceType type = null;
    		// 如果执行select语句使用READ数据源, 其它使用WRITE数据源
    		if(ms.getSqlCommandType().equals(SqlCommandType.SELECT)){
    			type = DynamicDataSouceType.READ;
    		}else {
    			type = DynamicDataSouceType.WRITE;
    		}
    		DynamicDataSouceHolder.putDataSouceType(type);
    		return invocation.proceed();
    	}
    
    }
    

     

  8. MybatisConfig配置SqlSessionFactory注入Plugins、DataSource
    package com.tao.study.dynamic;
    
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MybatisConfig {
    
    	@Autowired
    	private DynamicDataSouce dynamicDataSouce;
    
    	@Autowired
    	private DynamicPlugin dynamicPlugin;
    
    	@Bean
    	public SqlSessionFactory sqlSessionFactory() throws Exception {
    		System.out.println("SqlSessinFactory");
    		SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    		factoryBean.setDataSource(dynamicDataSouce);
    		factoryBean.setPlugins(dynamicPlugin);
    		factoryBean.setVfs(SpringBootVFS.class); // Sets the SpringBootVFS class into SqlSessionFactoryBean
    		// ...
    		
    		return factoryBean.getObject();
    	}
    }

     

结束语:以上就是使用SpringBoot实现配置多个数据源进行读写分离,如果有其它好的方法也请大家留言一起探讨

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章