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==

  

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