Spring Boot + MyBatis + 多数据源

开发过程中需要用到多数据库,此文基于 Spring Boot + MyBatis

一、配置数据源:修改application.properties如下

spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8

mybatis.config-locations=classpath:mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/*.xml
mybatis.type-aliases-package=model

#mysql1  注意:数据库名为testdb1
spring.datasource.url=jdbc:mysql://localhost:3306/testdb1?allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=******
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.test-on-borrow=false
spring.datasource.test-while-idle=true
spring.datasource.time-between-eviction-runs-millis=3600000

#mysql2  注意:数据库名为testdb2  mysql版本大于5.6时,需增加&useSSL=false
sqls.datasource.url=jdbc:mysql://localhost:3308/testdb2?allowMultiQueries=true&useSSL=false
sqls.datasource.username=root
sqls.datasource.password=******
sqls.datasource.driver-class-name=com.mysql.jdbc.Driver
sqls.datasource.test-on-borrow=false
sqls.datasource.test-while-idle=true
sqls.datasource.time-between-eviction-runs-millis=3600000

二、列举数据源的key,在common包下创建DatabaseType.java

package common;
/**
 * 列出所有的数据源key(用数据库名称来命名)
 * 注意:
 * 1)这里数据源与数据库是一对一的
 * 2)DatabaseType中的变量名称就是数据库的名称
 */
public enum DatabaseType {
	testdb1,testdb2;
}

三、在common包下创建DynamicDataSource.java

package common;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource{
	
	private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();
    
    public static void setDatabaseType(DatabaseType type){
        contextHolder.set(type);
    }
    
    public static DatabaseType getDatabaseType(){
        return contextHolder.get();
    }
	
	protected Object determineCurrentLookupKey() {
        return getDatabaseType();
    }
}

四、在common包下创建DataSourceAspect.java

package common;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import service.TestService;

@Aspect
@Component
public class DataSourceAspect {
	 /**
     * 使用空方法定义切点表达式
     * 指定包含service包下的所有类
     */
    @Pointcut("execution(* service.*.*(..))")        
    public void declareJointPointExpression() {
    }

    /**
     * 使用定义切点表达式的方法进行切点表达式的引入
     */
    @Before("declareJointPointExpression()")
    public void setDataSourceKey(JoinPoint point) {
        // TestService是指要指定数据源的类,不这样指定则采用默认数据库testdb1
        if (point.getTarget() instanceof TestService) {
        	DynamicDataSource.setDatabaseType(DatabaseType.testdb2);//这里指明采用数据库testdb2
        } 
    }

}

五、配置Spring Boot的启动项

package Application;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import common.DatabaseType;
import common.DynamicDataSource;

@EnableAutoConfiguration  
@Configuration  
@EnableTransactionManagement
@EnableScheduling
@ComponentScan(basePackages={"controller","service","dao","common"}) 
@MapperScan({"dao"})
public class StartApplication  extends SpringBootServletInitializer {
	
	@Autowired
    private Environment env;
	
	@Override  
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {  
        return application.sources(StartApplication.class);  
    }  
	
	public static void main(String[] args) {
		SpringApplication.run(StartApplication.class, args);
	}
	
	//创建数据源   注意名称
	@Bean
    public DataSource testdb1DataSource() throws Exception {
        Properties props = new Properties();
        props.put("driverClassName", env.getProperty("spring.datasource.driver-class-name"));
        props.put("url", env.getProperty("spring.datasource.url"));
        props.put("username", env.getProperty("spring.datasource.username"));
        props.put("password", env.getProperty("spring.datasource.password"));
        return  DruidDataSourceFactory.createDataSource(props);
    }

    //创建数据源   注意名称
    @Bean
    public DataSource testdb2DataSource() throws Exception {
        Properties props = new Properties();
        props.put("driverClassName", env.getProperty("sqls.datasource.driver-class-name"));
        props.put("url", env.getProperty("sqls.datasource.url"));
        props.put("username", env.getProperty("sqls.datasource.username"));
        props.put("password", env.getProperty("sqls.datasource.password"));
        return DruidDataSourceFactory.createDataSource(props);
    }

    /**
     * @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
     * @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
     *  注意这里用到的名称:testdb1DataSource、testdb2DataSource、testdb1、testdb2 都是对应的,请根据
     */
    @Bean
    @Primary
    public DynamicDataSource dataSource(@Qualifier("testdb1DataSource") DataSource testdb1DataSource,
            @Qualifier("testdb2DataSource") DataSource testdb2DataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DatabaseType.testdb1, testdb1DataSource);
        targetDataSources.put(DatabaseType.testdb2, testdb2DataSource);

        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
        dataSource.setDefaultTargetDataSource(testdb1DataSource);// 默认的datasource设置为myTestDbDataSource

        return dataSource;
    }

    /**
     * 根据数据源创建SqlSessionFactory
     */    
    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("testdb1DataSource") DataSource testdb1DataSource,
                                               @Qualifier("testdb2DataSource") DataSource testdb2DataSource) throws Exception{
        SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
        fb.setDataSource(this.dataSource(testdb1DataSource, testdb2DataSource));
        // 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
        fb.setTypeAliasesPackage(env.getProperty("dao"));// 指定基包
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        fb.setMapperLocations(resolver.getResources("classpath:/mybatis/*.xml"));
        return fb.getObject();
    }

    /**
     * 配置事务管理器
     */
    @Bean
    public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
        return new DataSourceTransactionManager(dataSource);
    }
}

六、TestMapper 

package dao;

import org.springframework.stereotype.Component;

@Component
public interface TestMapper {

	//获取服务数
	int GetGoodsCount();	

	//获取店铺数
	int GetShopCount();
}

七、TestService

package service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import common.DatabaseType;
import common.DynamicDataSource;
import dao.TestMapper;
import dao.UsersMapper;

@Service("TestService")
public class TestService {

	@Autowired
    private TestMapper testMapper;
	
	//调用示例-返回int 获取服务总数
	public int GetGoodsCount() {	
        return testMapper.GetGoodsCount();		
    }
	
	//调用示例-返回int 获取店铺总数
	public int GetShopCount() {	
		DynamicDataSource.setDatabaseType(DatabaseType.testdb1);//写了这一句则按指定数据源,不会按照DataSourceAspect里面对整个类的指定
        return testMapper.GetShopCount();		
    }
}

本过程只给出了与多数据源相关的地方,请根据自己的项目适当调整。

本文是基于大神的文章,做了少许调整。

原创地址:

https://www.cnblogs.com/java-zhao/p/5413845.html

http://www.cnblogs.com/java-zhao/p/5415896.html

猜你喜欢

转载自blog.csdn.net/woai671/article/details/81409258