如何合理的使用动态数据源

如何合理的使用动态数据源

        动态数据源在实现项目中用的是比较多的,比如在业务上做读写分离(主库负责写,从库负责读,主从同步可以直接使用mysql自带的),这里需要注意:写的时候要想保证事务就只能往一个数据源中写。

还有就是在做支付系统的时候,由于前几个月的订单记录客户是很少去查询的,在订单记录量比较大的时候(单表数据量大的情况下查询很慢)这个时候可以考虑将前几个月订单记录转移到新数据库中。

既然在实际项目中用的比较多,那就又学习的价值,接下来我们就一块去学习吧!少年

1.     要是还不知道如何搭建动态数据源可以参考我之前写的文章"基于自定义注解和Aop动态数据源配置"。

2.     完成动态数据源的搭建过后,我们就来分析一下在使用动态数据源会遇到的一些问题和一些注意事项。众所周知,Spring声明式事务是基于Aop实现的,动态数据源也是使用到Aop,这个时候我们应当先考虑多个Aop,它们是如何按排序执行?其实在Aop中,我们可以定义一个order属性,决定Aop的执行顺序,order越小越先执行,这个时候我们又应当考虑到底把哪个Aop放前面了?接下来我们就带着问题去测试一下呗,看看到底把哪个Aop放前面比较合适。

先将动态数据源Aop设置order=1,再将Spring事务Aop设置order=2(注意这里的配置和"基于自定义注解和Aop动态数据源配置"配置方式不是同一种,配置方式请参考文章:"Spring 声明式事务常用的二种配置方式 ")。

@Aspect
@Order(1)
@Component
public class DataSourceAspect {

	private static final Logger LOG = new Logger(DataSourceAspect.class);

	@Before("@annotation(ds)")
	public void changeDataSource(JoinPoint point, DataSource ds) throws Throwable {
		DynamicDataSourceHolder.setDataSourceType(ds.value().name());
	}
		
	@After("@annotation(ds)")
	public void restoreDataSource(JoinPoint point, DataSource ds) {
		DynamicDataSourceHolder.clearDataSourceType();

	}
}
<!-- 开启事务控制的注解支持 -->
<tx:annotation-driven transaction-manager="transactionManager" order="2"/>

测试代码如下:

@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
@DataSource(value = DataSourceType.SLAVE)
public void annotationNestedTransaction01(){
	SecurityAddition record = new SecurityAddition();
	record.setAmt(new Double(20));
	record.setCard(System.currentTimeMillis()+"");
	record.setOrder_no("Order-"+System.currentTimeMillis());
	record.setName("张三");
	securityAdditionMapper.insert(record);
}

日志对应如下:

认真分析日志我们能发现数据源的Aop是执行在事务Aop之前的,项目默认的数据源是Master,而上面代码重新设置了数据源为Slave,所以这个时候,下面事务Aop是在数据源为Slave下创建的事务,所以记录肯定是存在Slave表中的,那现在我们把Aop的执行顺序发过来看看实际的效果。

修改order顺序:

@Aspect
@Order(2)
@Component
public class DataSourceAspect {

	private static final Logger LOG = new Logger(DataSourceAspect.class);

	@Before("@annotation(ds)")
	public void changeDataSource(JoinPoint point, DataSource ds) throws Throwable {
		DynamicDataSourceHolder.setDataSourceType(ds.value().name());
	}
		
	@After("@annotation(ds)")
	public void restoreDataSource(JoinPoint point, DataSource ds) {
		DynamicDataSourceHolder.clearDataSourceType();

	}
}
<!-- 开启事务控制的注解支持 -->
<tx:annotation-driven transaction-manager="transactionManager" order="1"/>

这个时候测试代码不变,日志如下:

这个时候可以根据日志可以得出,事务Aop是执行在数据源Aop之前的,这个时候就不会达到切换数据源到Slave下目的,因为这个插入操作是在spring事务开启之后才去切换的,没有任何效果,这个时候在到Slave中操作新插入的记录是找不到的,事务aop会走默认的Master,可以得知在master中会有一条新记录。

总结:如何要在一个service方法中既要切换数据源又要保证这个方法的事务,这个时候我们就必须将数据源切换Aop放在事务Aop之前执行,还有就是千万要记住,如何在一个service方法上已经使用了spring的事务,就不要指望在方法中还去切换数据源了,如下面错误演示代码:

@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
@DataSource(value = DataSourceType.SLAVE)
public void annotationNestedTransaction01(){
	SecurityAddition record = new SecurityAddition();
	record.setAmt(new Double(20));
	record.setCard(System.currentTimeMillis()+"");
	record.setOrder_no("Order-"+System.currentTimeMillis());
	record.setName("张三");
	securityAdditionMapper.insert(record);
	
	Admin admin = new Admin();
	admin.setCreateTime(new Date());
	admin.setId(102);
	admin.setDoctorId(101);
	admin.setHospitalId(101);
	admin.setPassword("123456");
	admin.setPayName("张三");
	admin.setState(1);
	admin.setToken("88888888");
	admin.setAccount("zhangsan");
	admin.setType("USER");
	DynamicDataSourceHolder.setDataSourceType(DataSourceType.MASTER.name());  //手动切换数据到master
	adminMapper.insert(admin);
	DynamicDataSourceHolder.clearDataSourceType();
}

如上错误代码不会将admin插入到Master中,而是插入到Slave中了。

猜你喜欢

转载自my.oschina.net/qrmc/blog/1807293