@TargetDataSource与@Transactional共用导致切换数据源失效
前言
我真是吐了,这个问题搞了我一天,一开始我发现查询的时候并没有切换数据源,结果一直以为是@TargetDataSource这个注解方法出问题了,后来想着之前怎么没问题,就去看一下自己的提交记录,结果发现为了防止多张表插入数据时有一个出问题导致脏数据而加了个事务注解@Transactional,然后删掉之后就可以用@TargetDataSource注解方法正常切换数据源了。。。
具体情况描述
简单的写个和我问题代码结构一样的例子(以下的方法和类都是假设)
具体需求是从SQLServer数据库里拿数据出来存到MySQL数据库,不过这个是什么数据库也没关系,数据不在同一个数据库就行
class InfoServiceImpl extends InfoService {
@Autowired
private InfoService infoService;
@Override
@Transactional(rollbackFor = Exception.class)
public void saveSomething(Info info) {
boolean flag = infoService.checkExists(info);
if (flag) {
...
} else {
...
}
}
@Override
@TargetDataSource("db")
public boolean checkExists(Info info) {
Info info2 = 某个数据表的Mapper.selectOne(info);
if (info2 == null ) {
return true;
}
return false;
}
}
大致就和上面那样,带有**@Transactional注解的方法调用带有@TargetDataSource注解的方法时,带有@TargetDataSource**注解的方法就不会切换数据源,导致SQL报错,比如没有这个数据表或者没有这个字段,原因就是根本就没有切换到另一个数据库去找,肯定出问题啊。
解决办法
最直接的解决办法就是把@Transactional
删掉,但是这样又会出现新的问题,本来我是要防止脏数据的,删掉不就完了吗,所以最好的办法是把插入表的那部分内容再单独出来一个方法,在这个方法上加就不会影响到其他方法了,这样数据源也可以正常切换。
还有一点疑惑的就是为什么两个方法在同一个类里,调用的时候却需要注入Service接口再调用呢,这个和@TargetDataSource注解
有关系,如果不是通过Service调用的话就不会起作用,也不会切换数据源了,所以只能出现这种神奇的操作,不过我也不是很确定,但目前测试的情况确实是这样。
最后正确的代码就是这样的:
class InfoServiceImpl extends InfoService {
@Autowired
private InfoService infoService;
@Override
public void saveSomething(Info info) {
boolean flag = infoService.checkExists(info);
if (flag) {
save(info);
} else {
...
}
}
@Override
@TargetDataSource("db")
public boolean checkExists(Info info) {
Info info2 = [某个数据表的Mapper或Service].selectOne(info);
if (info2 == null ) {
return true;
}
return false;
}
@Transactional(rollbackFor = Exception.class)
public void save(Info info) {
...
}
}
结尾
再次吐槽这个问题,浪费了我一天的时间