@Transactional 在 REST 层还是在 Service 层?哪个更好?
@Transactional in REST layer or in Service layer? Which is better?
在我的例子中,我想通过调用方法 "createTags()" 创建多个标签。为了测试方便,我在这个方法中只使用@GET,没有参数,请不要介意。如果其中一个操作抛出异常,我希望所有操作回滚,例如如果 t3 的 tagValue 为空,则 t1、t2 不应该存在于我的数据库中。显然,我应该在这里使用@Transactional,所以我的问题是我可以这样使用@Transactional吗?我的意思是在我的 REST 层中使用它。这是代码:
TestREST.class
@Path("tag/create")
@GET
@Transactional(rollbackOn = {NoTagException.class, TagAlreadyExistedException.class}))
public void createTags() throws NoTagException, TagAlreadyExistedException {
// create all categories
Tag t1 = new Tag(TagType.CATEGORY);
t1.setTagValue("Edu");
tagService.createTag(t1);
Tag t2 = new Tag(TagType.CATEGORY);
t2.setTagValue("ZJNU");
tagService.createTag(t2);
Tag t3 = new Tag(TagType.CATEGORY);
// the value is empty here so I hope all previous operations rollback
t3.setTagValue("");
tagService.createTag(t3);
}
TagService.class
public void createTag(Tag tag) throws NoTagException, TagAlreadyExistedException {
if (tag.getType() == null || !(tag.getType() instanceof TagType)) {
throw new NoTagException("tag's type must be set");
} else if (tag.getTagValue() == null || tag.getTagValue().equals("")) {
throw new NoTagException("tag's value must be set!");
} else {
Optional<Tag> existedTag = retrieveTagByTypeAndValue(tag.getType(), tag.getTagValue());
if (existedTag.isPresent()) {
throw new TagAlreadyExistedException("one or more tags are already existed!");
} else {
tagDAO.create(tag);
}
}
}
或者我是否应该始终在我的服务层中使用@Transactional?我把上面的方法改成这样:
TestREST.class
@Path("tag/create")
@GET
public void createTags() throws NoTagException, TagAlreadyExistedException {
// create all categories
Set<Tag> tags = new HashSet<>();
Tag t1 = new Tag(TagType.CATEGORY);
t1.setTagValue("Edu");
Tag t2 = new Tag(TagType.CATEGORY);
t2.setTagValue("ZJNU");
Tag t3 = new Tag(TagType.CATEGORY);
t3.setTagValue("");
tags.add(t1);
tags.add(t2);
tags.add(t3);
tagService.createTags(tags);
}
TagService.class
@Transactional(rollbackOn = {NoTagException.class, TagAlreadyExistedException.class}))
public void createTag(Tag tag) throws NoTagException, TagAlreadyExistedException {
// just the same as above
}
public void createTags(Set<Tag> tags) throws NoTagException, TagAlreadyExistedException {
if (tags.isEmpty()) {
throw new NoTagException("tag must be set");
} else {
for (Tag tag : tags) {
createTag(tag);
}
}
}
他们都能达到我的预期。那么,我应该选择哪种方法呢?为什么?有什么建议可以改进这些方法吗? 顺便说一句,我在 TestREST.class 和 TagService.class 中使用 CDI @RequestScoped 感谢您的帮助!
通常,您希望在 Service Layer
中找到 @Transactional
注释,因为它是了解其他作品单元的层,并且通常是您管理异常的层,例如RuntimeException
.
我觉得这个问题不是关于在哪里使用事务,更多的是关于代码的结构。
在示例中,将@Transactional 添加到其余层将实现您想要的确切目标(保存所有标签或none 基于抛出的错误)。问题变成了,"where should the business logic exist?".
如果 rest 端点只是一个数据收集方法,并且有一个获取数据并尝试保存数据的服务方法,那么事务注释应该存在于该级别。
我发现人们经常使用两个比我聪明得多的概念。将 Transactional 注释向下移动到它有意义的最精细层;并且,在具体方法上注释接口方法。后者本身在这里不适用(但我希望它在将来对您有所帮助),但前者应该是您的指南。
重申一下,问题不在于@Transactional 应该放在哪里。应该是,"where will the call to save the tags be performed"; @Transactional 将随之而来。
(希望对您有所帮助)
在为应用程序设计事务时,可以根据应用程序的 architecture/type 使用事务设计模式之一
Client Owner 交易设计模式
域服务所有者事务设计模式
服务器委托所有者事务设计模式
对于大多数 java 企业应用程序域服务所有者事务设计模式用于应用程序的域组件管理事务
当我们想将事务责任转移到表示层时,使用客户端所有者事务设计模式
当我们想在应用程序中利用命令模式时,使用服务器委托所有者事务设计模式
当应用程序没有聚合服务并且需要多个客户端远程调用来完成一项业务操作时,使用客户端所有者。在您的代码中,您多次调用同一服务以在 REST 层中创建标签,因此没有理由将事务责任放在表示层中。对于您的业务需求,最好使用领域服务所有者模式将交易责任转移到中间层
此外,使用客户端所有者模式将过多的基础架构责任放在客户端,这会增加服务器聊天。表示层应该只负责呈现数据,但 Client Owner 模式强制表示层维护服务器端责任也不好。
域服务所有者是大多数基于 java 的企业应用程序的常用模式。在这种模式中,您的应用程序的域组件,即项目、订单、库存,在您的情况下标签拥有交易责任。所以对于业务需求,这是应该使用的模式
在应用程序中使用命令模式时使用服务器委托所有者事务设计模式,它解决了客户端所有者模式的问题。在 command 中,Pattern 功能被放入 Command 并发送到服务器执行。服务器端的命令处理器组件管理事务。
在事务中有效管理异常的一个简单规则是 layer/Component 管理事务应该处理异常并针对该异常进行业务操作。管理事务包括启动事务、提交事务和回滚事务,所以这三个操作都应该放在拥有事务责任的组件中
我建议阅读 pdf 版本 Java 我在下面的 git 存储库中上传的交易设计策略,以深入了解交易设计模式。
在我的例子中,我想通过调用方法 "createTags()" 创建多个标签。为了测试方便,我在这个方法中只使用@GET,没有参数,请不要介意。如果其中一个操作抛出异常,我希望所有操作回滚,例如如果 t3 的 tagValue 为空,则 t1、t2 不应该存在于我的数据库中。显然,我应该在这里使用@Transactional,所以我的问题是我可以这样使用@Transactional吗?我的意思是在我的 REST 层中使用它。这是代码:
TestREST.class
@Path("tag/create")
@GET
@Transactional(rollbackOn = {NoTagException.class, TagAlreadyExistedException.class}))
public void createTags() throws NoTagException, TagAlreadyExistedException {
// create all categories
Tag t1 = new Tag(TagType.CATEGORY);
t1.setTagValue("Edu");
tagService.createTag(t1);
Tag t2 = new Tag(TagType.CATEGORY);
t2.setTagValue("ZJNU");
tagService.createTag(t2);
Tag t3 = new Tag(TagType.CATEGORY);
// the value is empty here so I hope all previous operations rollback
t3.setTagValue("");
tagService.createTag(t3);
}
TagService.class
public void createTag(Tag tag) throws NoTagException, TagAlreadyExistedException {
if (tag.getType() == null || !(tag.getType() instanceof TagType)) {
throw new NoTagException("tag's type must be set");
} else if (tag.getTagValue() == null || tag.getTagValue().equals("")) {
throw new NoTagException("tag's value must be set!");
} else {
Optional<Tag> existedTag = retrieveTagByTypeAndValue(tag.getType(), tag.getTagValue());
if (existedTag.isPresent()) {
throw new TagAlreadyExistedException("one or more tags are already existed!");
} else {
tagDAO.create(tag);
}
}
}
或者我是否应该始终在我的服务层中使用@Transactional?我把上面的方法改成这样:
TestREST.class
@Path("tag/create")
@GET
public void createTags() throws NoTagException, TagAlreadyExistedException {
// create all categories
Set<Tag> tags = new HashSet<>();
Tag t1 = new Tag(TagType.CATEGORY);
t1.setTagValue("Edu");
Tag t2 = new Tag(TagType.CATEGORY);
t2.setTagValue("ZJNU");
Tag t3 = new Tag(TagType.CATEGORY);
t3.setTagValue("");
tags.add(t1);
tags.add(t2);
tags.add(t3);
tagService.createTags(tags);
}
TagService.class
@Transactional(rollbackOn = {NoTagException.class, TagAlreadyExistedException.class}))
public void createTag(Tag tag) throws NoTagException, TagAlreadyExistedException {
// just the same as above
}
public void createTags(Set<Tag> tags) throws NoTagException, TagAlreadyExistedException {
if (tags.isEmpty()) {
throw new NoTagException("tag must be set");
} else {
for (Tag tag : tags) {
createTag(tag);
}
}
}
他们都能达到我的预期。那么,我应该选择哪种方法呢?为什么?有什么建议可以改进这些方法吗? 顺便说一句,我在 TestREST.class 和 TagService.class 中使用 CDI @RequestScoped 感谢您的帮助!
通常,您希望在 Service Layer
中找到 @Transactional
注释,因为它是了解其他作品单元的层,并且通常是您管理异常的层,例如RuntimeException
.
我觉得这个问题不是关于在哪里使用事务,更多的是关于代码的结构。
在示例中,将@Transactional 添加到其余层将实现您想要的确切目标(保存所有标签或none 基于抛出的错误)。问题变成了,"where should the business logic exist?".
如果 rest 端点只是一个数据收集方法,并且有一个获取数据并尝试保存数据的服务方法,那么事务注释应该存在于该级别。
我发现人们经常使用两个比我聪明得多的概念。将 Transactional 注释向下移动到它有意义的最精细层;并且,在具体方法上注释接口方法。后者本身在这里不适用(但我希望它在将来对您有所帮助),但前者应该是您的指南。
重申一下,问题不在于@Transactional 应该放在哪里。应该是,"where will the call to save the tags be performed"; @Transactional 将随之而来。
(希望对您有所帮助)
在为应用程序设计事务时,可以根据应用程序的 architecture/type 使用事务设计模式之一
Client Owner 交易设计模式 域服务所有者事务设计模式 服务器委托所有者事务设计模式
对于大多数 java 企业应用程序域服务所有者事务设计模式用于应用程序的域组件管理事务
当我们想将事务责任转移到表示层时,使用客户端所有者事务设计模式
当我们想在应用程序中利用命令模式时,使用服务器委托所有者事务设计模式
当应用程序没有聚合服务并且需要多个客户端远程调用来完成一项业务操作时,使用客户端所有者。在您的代码中,您多次调用同一服务以在 REST 层中创建标签,因此没有理由将事务责任放在表示层中。对于您的业务需求,最好使用领域服务所有者模式将交易责任转移到中间层 此外,使用客户端所有者模式将过多的基础架构责任放在客户端,这会增加服务器聊天。表示层应该只负责呈现数据,但 Client Owner 模式强制表示层维护服务器端责任也不好。
域服务所有者是大多数基于 java 的企业应用程序的常用模式。在这种模式中,您的应用程序的域组件,即项目、订单、库存,在您的情况下标签拥有交易责任。所以对于业务需求,这是应该使用的模式
在应用程序中使用命令模式时使用服务器委托所有者事务设计模式,它解决了客户端所有者模式的问题。在 command 中,Pattern 功能被放入 Command 并发送到服务器执行。服务器端的命令处理器组件管理事务。
在事务中有效管理异常的一个简单规则是 layer/Component 管理事务应该处理异常并针对该异常进行业务操作。管理事务包括启动事务、提交事务和回滚事务,所以这三个操作都应该放在拥有事务责任的组件中
我建议阅读 pdf 版本 Java 我在下面的 git 存储库中上传的交易设计策略,以深入了解交易设计模式。