在你的示例中,这取决于你的存储库是否具有
@Transactional
。
如果是的话,在你的情况下,服务应该不使用
@Transactional
(因为没有使用它的意义)。如果您计划添加处理其他表/存储库的更多逻辑到服务中,则可以稍后添加
@Transactional
- 那时使用它就有意义了。
如果没有,则如果您想确保您没有隔离问题,例如您没有读取尚未提交的内容,那么您的服务应该使用
@Transactional
。
- 如果谈论一般的存储库(作为CRUD集合接口):
1. 我会说:不,你应该不使用@Transactional。
为什么不呢?如果我们认为存储库是在业务上下文之外的,那么它就不应该了解传播或隔离级别(锁定级别)。它不能猜测自己会被卷入哪个事务上下文中。
如果您相信存储库是"无业务"的,那么如下所示,您有一个存储库:
class MyRepository
void add(entity) {...}
void findByName(name) {...}
同时还有一个业务逻辑,例如MyService
class MyService() {
@Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.SERIALIZABLE)
void doIt() {
var entity = myRepository.findByName("some-name");
if(record.field.equal("expected")) {
...
myRepository.add(newEntity)
}
}
}
即在此情况下:
MyService
决定将存储库涉及到什么。
在这种情况下,使用propagation="Required"将确保两个存储库方法-
findByName()
和
add()
在单个事务中运行,并且isolation="Serializable"将确保没有人可以干扰它。它将为涉及get()和add()的表保留锁定状态。
但是,其他一些服务可能希望以不同方式使用MyRepository,例如根本不涉及任何事务,比如使用
findByName()
方法,不关心读取此时找到的任何内容的限制。
我会说如果您将存储库视为始终返回有效实体(无脏读等)的存储库,则应该是的(保存用户免于使用不正确)。也就是说,您的存储库应该解决隔离问题(并发性和数据一致性),就像上面的示例一样:
我们希望(存储库)确保在添加新实体之前,它将首先检查是否已经存在具有相同名称的实体,如果存在,则插入所有锁定工作单元。 (与上述服务级别相同,但现在我们将此责任转移到存储库)
比方说,不能有两个处于“进行中”状态并带有相同名称的任务(业务规则)。
class TaskRepository
@Transactional(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
}
}
@Transactional
void findFirstByName(name) {...}
第二个更像DDD风格的仓库。
我猜如果有更多需要涵盖:
class Service {
@Transactional(isolation=.., propagation=...)
void doStuff() {
taskRepository.add(task);
}
}
@Transactional(readOnly = true)
(因为它通常包含大部分的查找方法),并使用普通的@Transactional
覆盖每个修改查询方法的设置。这实际上就是SimpleJpaRepository
中所采用的方式。 - Oliver Drotbohm@Configuration
类上使用@EnableTransactionManagement
,以便@Transactional
正常工作。 - hello_earth