就我而言,我想通过调用方法" 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);
}
}
}
他们都可以达到我的预期。那么,我应该选择哪种方法?为什么?还有什么建议我可以改进这些方法吗? BTW,我在TestREST.class和TagService.class中使用CDI @RequestScoped 感谢您的帮助!
答案 0 :(得分:3)
我觉得这个问题不是关于在何处使用Transactional,而是关于如何构建代码的更多信息。
在该示例中,将@Transactional添加到其余图层将实现您想要的确切目标(根据抛出的错误保存所有标记或不保存)。问题变成了,&#34;业务逻辑应该存在于哪里?&#34;。
如果,其余端点只是一个数据收集方法,并且有一个服务方法来获取数据并尝试保存它,那么Transactional注释应该存在于该级别。
有两个概念,我发现人们比我经常使用更聪明。将Transactional注释向下移动到有意义的最精细层;并且,在具体方法上注释接口方法。后者本身并不适用于此(但我希望将来可以帮助你),但前者应该是你的指南。
重申一下,问题不在于应该放置@Transactional的位置..应该是,&#34;保存标签的调用将在何处执行&#34 ;;并且@Transactional将跟随。
(希望这有帮助)
答案 1 :(得分:1)
通常,您希望在@Transactional
中找到Service Layer
注释,因为它是了解其他作品单元的图层,而且通常是您在例如{...}}中管理例外情况的图层。 RuntimeException
。
答案 2 :(得分:1)
在为应用程序设计事务时,可以根据应用程序的体系结构/类型使用事务设计模式之一
客户所有者交易设计模式 域服务所有者事务设计模式 服务器代理所有者交易设计模式
对于大多数Java企业应用程序,域服务所有者事务设计模式用于应用程序的域组件管理事务
当我们想将交易责任转移到表示层时,使用客户所有者交易设计模式
当我们想要在应用程序中利用命令模式时,使用Sever委托所有者事务设计模式
当应用程序没有聚合服务且需要多个客户端进行远程调用以完成一个业务操作时,将使用客户端所有者。在您的代码中,您多次调用相同的服务来在REST层中创建标记,因此没有理由在表示层中放置事务责任。对于您的业务需求,最好使用域服务所有者模式在中间层上移动事务责任 同样使用客户所有者模式在客户端放置了太多基础设施责任,这增加了服务器聊天表示层应该只负责呈现数据,但客户所有者模式强制表示层维护服务器端责任也不好。
域服务所有者是大多数基于Java的企业应用程序的常用模式。在此模式中,您的应用程序的域组件,即项目,订单,库存以及您的案例中标签拥有交易责任。因此,对于业务需求,这是应该使用的模式
在应用程序中使用命令模式时使用服务器代理所有者事务设计模式,它解决了客户端所有者模式的问题。在命令模式中,功能放在命令中并发送到服务器以供执行。服务器端的命令处理器组件管理事务。
在事务中有效管理异常的一个简单规则是管理事务的层/组件应处理异常并对该异常执行业务操作。管理事务包括启动事务,提交事务和回滚事务,因此所有这三个操作都应该放在拥有事务责任的组件中
我建议通过我在git repo下面上传的pdf版Java Transaction Design Strategies来深入了解交易设计模式。