Spring--多数据源解决方案

整体流程如下;
这里写图片描述
分为XMl配置方案与SpringBoot集成两个方案

1 与Spring集成采用XML配置,没有了Controller层,但是手动设置当前线程访问的数据库名,
项目整体目录结构:
这里写图片描述
自定义的数据源DynamicDataSource:

public class DynamicDataSource extends AbstractRoutingDataSource {

	/**
	 * 取得当前使用哪个数据源
	 * 
	 * @return
	 */
	@Override
	protected Object determineCurrentLookupKey() {
		return DbContextHolder.getDbType();
	}
}

数据源枚举:

public enum DBTypeEnum {

	one("dataSource_one"), two("dataSource_two");
	private String value;

	DBTypeEnum(String value) {
	    this.value = value;
	}

	public String getValue() {
	    return value;
	}
}

数据源存储获取处理中心:

public class DbContextHolder {
	
	@SuppressWarnings("rawtypes")
	private static final ThreadLocal contextHolder = new ThreadLocal<String>();

	/**
	 * 设置数据源
	 * @param dbTypeEnum
	 */
	@SuppressWarnings("unchecked")
	public static void setDbType(DBTypeEnum dbTypeEnum) {
	    contextHolder.set(dbTypeEnum.getValue());
	}

	/**
	 * 取得当前数据源
	 * @return
	 */
	public static String getDbType() {
	    return contextHolder.get().toString();
	}

	/**
	 * 清除上下文数据
	 */
	public static void clearDbType() {
	    contextHolder.remove();
	}
}

配置中心:

	<!-- 扫描Service层Bean注入到容器中 -->
	<context:component-scan base-package="com.test.service" />

	<!-- 开启注解 -->
	<context:annotation-config/>
	
	<bean id="dataSource_one" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="username" value="root" />
		<property name="password" value="123123" />
		<property name="url" value="jdbc:mysql://127.0.0.1:3306/test01?characterEncoding=utf8&amp;useSSL=true"/>
	</bean>
	
	<bean id="dataSource_two" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="username" value="root" />
		<property name="password" value="123123" />
		<property name="url" value="jdbc:mysql://127.0.0.1:3306/test02?characterEncoding=utf8&amp;useSSL=true"/>
	</bean>

	<bean id="dataSource" class="com.test.datasource.DynamicDataSource">
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<entry key="dataSource_one" value-ref="dataSource_one" />
				<entry key="dataSource_two" value-ref="dataSource_two" />
			</map>
		</property>
		<property name="defaultTargetDataSource" ref="dataSource_two" />
	</bean>
	
	<!-- - - - - - - - sessionFactory - - - - - - - -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:sqlMapConfig.xml" />
	</bean>

	<!-- - - - - - - DAO - - - - - - - -->
	<!-- 新添加表时要添加 的位置 -->
	<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="sqlSessionFactory" ref="sqlSessionFactory" />
		<property name="mapperInterface" value="com.test.dao.user.UserMapper" />
	</bean>

MyBatis配置:
sqlMapConfig:

<configuration>
    <!-- - - - - - - 懒加载和缓存 - - - - - - - - - - -->
    <settings>
        <setting name="cacheEnabled" value="true"/>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
    <!-- - - - - - - - 物理分页拦截器 - - - - - - - -->
    
    <!-- - - - - - - -映射文件路径- - - - - - -->
    <!-- 新添加表时要添加 的位置 -->
    <mappers>
        <mapper resource="com/test/dao/mapper/user/UserMapper.xml"/>
    </mappers>
</configuration>

Service层与Dao层代码可具体看:

测试:

public class TestMain {
	
	public static void main(String[] args) {
		test();
	}
	
	public static void test() {
		ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
		DbContextHolder.setDbType(DBTypeEnum.two);//单前线程选择数据源
		UserService userService=(UserService)ac.getBean("userServiceImpl");
		User user = userService.getUserInfo(1);
		System.out.println(user);
	}
}

2 与SpringBoot的集成与上中相似,完全按照流程图中的设计,在Service层中通过注解的方式来设置访问的数据源。定义一个切面,拦截所有Service的线程,根据注解的值来设置当前线程访问 的数据源。
Service

@Service
public class UserServiceImpl implements UserService{

	@Autowired
	private UserMapper userMapper;
	
	@DataSource(DataSourceType.READ)
	public User getUserInfoByRead(Integer userId) {
		// TODO Auto-generated method stub
		return userMapper.selectUserById(userId);
	}
	
	@DataSource(DataSourceType.WRITE)
	public User getUserInfoByWrite(Integer userId){
		return userMapper.selectUserById(userId);
	}
}

整合Mybatis:

@Configuration
@EnableConfigurationProperties({MasterDataSourceProperties.class,SlaveDataSourceProperties.class})
@MapperScan(basePackages = {"com.text.dao"})
public class MybatisPlusConfig {
	
	private static final Logger logger=LoggerFactory.getLogger(MybatisPlusConfig.class);
	
	@Autowired
	private MasterDataSourceProperties masterDataSourceProperties;
	
	@Autowired
	private SlaveDataSourceProperties slaveDataSourceProperties;
	
	@Bean("dataSource")
    public ChooseDataSource dataSource() {
		logger.info("-------------------数据源加载开始-------------");
		DruidDataSource masterDataSource = new DruidDataSource();
		masterDataSourceProperties.config(masterDataSource);
		DruidDataSource slaveDataSource = new DruidDataSource();
		slaveDataSourceProperties.config(slaveDataSource);
        try {
        	masterDataSource.init();
        	slaveDataSource.init();
		} catch (SQLException e) {
			logger.error("数据源加载失败");
			e.printStackTrace();
			
		}
        Map<Object,Object> map = new HashMap<Object,Object>();
        map.put("read", masterDataSource);
        map.put("write", slaveDataSource);
        ChooseDataSource chooseDataSource = new ChooseDataSource();
        chooseDataSource.setTargetDataSources(map);
        chooseDataSource.setDefaultTargetDataSource(masterDataSource);
        logger.info("-------------------数据源加载结束-------------");
        return chooseDataSource;
    }
}

切面拦截事件:

@Aspect
@Order(1)
@Component
public class DataSourceAspect {
	private static Logger log = LoggerFactory.getLogger(DataSourceAspect.class);

	@Pointcut(value="execution(* com.text.service.impl.*.*.*(..))")
	public void pointCut() {
	};

	@Before(value = "pointCut()")
	public void before(JoinPoint point) {
		DataSource dataSource = ((MethodSignature) point.getSignature()).getMethod().getAnnotation(DataSource.class);
		if (dataSource != null) {
			log.debug("设置数据源为{}" ,dataSource.value());
			HandleDataSource.putDataSource(dataSource.value());
		} else {
			log.error("设置数据源失败,将使用默认的数据源");
		}
	}

	@After(value = "pointCut()")
	public void after() {
		HandleDataSource.setDefault(DataSourceType.WRITE);
		log.debug("业务结束,设置默认数据源为write");
	}

	@AfterThrowing(value = "pointCut()")
	public void afterThrowing() {
		HandleDataSource.setDefault(DataSourceType.WRITE);
		log.debug("业务出现异常,设置数据源为为write") ;
	}
}

测试可通过:
http://localhost:8081/user/getUserInfoByWrite/1
http://localhost:8081/user/getUserInfoByRead/1
创建两个数据库,相同ID不同其他数据即可测试结果。
具体可查看代码。http://download.csdn.net/download/btwangzhi/10215520
http://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247486593&idx=1&sn=c98328061adc5621d1cac3bee3bfeacb&chksm=ebd633addca1babb10cc15c8ef6d7bef982c9ddf940246303c9ae06274574bff98ed7b76e841&mpshare=1&scene=24&srcid=1018vnMtwsrdQd3ZkMrssxuA#rd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值