如何在Spring Data中使用@Transactional?
如何在Spring Data中使用@Transactional?
我刚开始做一个使用Spring-data、Hibernate、MySQL、JPA的项目。我转用spring-data是为了不必手动创建查询语句。
我注意到在使用spring-data时不需要使用@Transactional
注解,因为我也尝试了没有注解的查询。
是否使用@Transactional
注解有特定的原因吗?
可行:
@Transactional public List listStudentsBySchool(long id) { return repository.findByClasses_School_Id(id); }
同样可行:
public List listStudentsBySchool(long id) { return repository.findByClasses_School_Id(id); }
在Spring Data中如何使用@Transactional?
在你的示例中,这取决于你的repository是否有@Transactional注解。
如果有,那么在你的情况下,service应该不使用@Transactional(因为没有使用它的意义)。如果你计划在service中添加与其他表/仓库处理的逻辑,那么稍后可以添加@Transactional,那么这样做是有意义的。
如果没有,则如果要确保没有隔离问题,也就是说你不会读取尚未提交的内容,那么你的service应该使用@Transactional。
如果谈论一般的repository(作为crud的集合接口):
- 我会说:不应该使用@Transactional。
为什么不应该使用:如果我们相信repository是在业务上下文之外的,并且它不应该知道传播或隔离级别(锁的级别),它无法猜测它可能涉及的事务上下文。
repository是"无业务逻辑"的(如果你这样认为)。
假设你有一个repository:
class MyRepository void add(entity) {...} void findByName(name) {...}
还有一个业务逻辑,比如MyService:
class MyService() { (propagation=Propagation.REQUIRED, isolation=Isolation.SERIALIZABLE) void doIt() { var entity = myRepository.findByName("some-name"); if(record.field.equal("expected")) { ... myRepository.add(newEntity) } } }
也就是说,在这种情况下,MyService决定是否要将repository涉及其中。
在这种情况下,使用propagation="Required"可以确保findByName()和add()两个repository方法都涉及到同一个事务中,而isolation="Serializable"可以确保没有人可以干扰它。它将为get()和add()涉及到的表保持锁定。
但是其他的Service可能希望以不同的方式使用MyRepository,不涉及任何事务,比如它使用findByName()方法,对于此刻它可以找到的任何内容都不感兴趣。
- 如果你将repository视为始终返回有效实体的repository(没有脏读等),那么我会说是的,(防止用户使用它不正确)。也就是说,你的repository应该解决隔离问题(并发和数据一致性),就像下面的例子一样:
我们希望(repository)确保当我们add(newEntity)时,首先检查是否已经存在具有相同名称的实体,如果存在-插入,在一个锁定的工作单元中完成。(与我们在上面的service级别上做的相同,但现在将此责任转移到repository)
比如说,不能有两个具有相同名称“进行中”状态的任务(业务规则)
class TaskRepository (propagation=Propagation.REQUIRED, isolation=Isolation.SERIALIZABLE) void add(entity) { var name = entity.getName() var found = this.findFirstByName(name); if(found == null || found.getStatus().equal("in-progress")) { .. do insert } } void findFirstByName(name) {...}
第二种更像是DDD风格的repository。
我猜还有更多可以讨论的,如果:
class Service { (isolation=.., propagation=...) // 其中..与taskRepository()中定义的不同 void doStuff() { taskRepository.add(task); } }
如何在Spring Data中使用@Transactional?
在Spring Data中使用@Transactional的原因是为了确保数据库操作的一致性和完整性。当我们进行添加、编辑或删除操作时,需要在方法上添加@Transactional注解,以确保这些操作在一个事务中执行。而对于查询操作,我们可以在方法上添加(readOnly = true)注解,以提高查询性能并避免意外的写操作。
在Spring Data中,我们可以在接口上使用@Transactional注解来设置默认的事务属性。对于大部分的查询方法,我们可以使用(readOnly = true)注解来标记,以提高查询性能。而对于修改操作的方法,我们可以在方法上直接添加@Transactional注解。
同时,我们也需要注意一些性能优化方面的问题。例如,使用(readOnly = true)注解可以让Spring Data在查询时禁用JPA/Hibernate的缓存刷新操作,从而提高性能。另外,根据数据库的不同,该注解还可以避免表锁或拒绝写操作。
总之,我们应该根据具体情况在DAO方法中使用@Transactional注解。对于查询方法,我们可以使用(readOnly = true)注解来提高查询性能。而对于修改操作的方法,我们可以直接在方法上添加@Transactional注解。需要注意的是,如果使用(readOnly = true)注解,还可以考虑同时设置传播模式为SUPPORTS。
参考链接:
- [transaction-pit-falls](http://www.ibm.com/developerworks/java/library/j-ts1/index.html#listing8)
- [why do i need transaction in hibernate for read only operations](https://stackoverflow.com/questions/13539213)
- [Spring read-only transaction Hibernate optimization](https://vladmihalcea.com/spring-read-only-transaction-hibernate-optimization/)