事务是什么?事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
下面来看一个项目中遇到的问题:
有这么一个需求,我们要查询一些数据,但是在查询这个数据之前我们要往查询的表中插入一些数据,虽然这个需求有点愚蠢,但是难不到我们。
public class QueryStudentServiceImpl implements QueryStudentService {
public List queryStudent(){
Student student = new Student();
insertStudent(student); //查询之前先插入
return studentDao.queryStudent(); //查询数据库
}
@Transactional
public void insertStudent(Student student) {
studentDao.insertStudent(student);
}
}
上面的代码看上去很完美也很简洁,但是经过测试发现查询出来的数据并没有把插入的数据一并查出来。那么问题到底出在哪里呢?难道是事务管理哪里出问题了?
事务又会分为 “读写事务”和“只读事务”,“只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。Oracle默认情况下是没有事务控制的,即只能查询出语句执行前状态的数据,那么如果当你在做报表查询,两次查询结果可能会不一致。这个时候就可以用到只读事务。如果两条查询语句被一个只读事务包围,则两条查询结构就能保证一致性。也就是说只读事务开启期间数据库的变化是不会被查询出来。
说了这么多,跟上面的问题有什么关系呢,我的查询又没有开启只读事务?其实Spring事务控制是包含注解控制和xml配置控制。在通过各种方法查询,最后在核心层项目中找到了这么一段配置。
也就是说我们的query*方法被配置为只读事务,并且insert操作是发生在事务开启之后,所以导致查询不出插入的数据。
但是如果我一定要这么操作怎么办呢?也有办法,我们可以在queryStudent 方法前面 通过@Transactional(readOnly = false)来改变该方法的事务控制。并且建议将 insertStudent方法放到其他的bean中。因为调用同一个类中事务控制方法,会导致该事务失效。具体可以参考这篇文章