交易环境中的Spring控制器指南

时间:2014-04-16 06:52:14

标签: hibernate spring-mvc jpa model-view-controller transactions

使用Spring MVC和JPA实现一个返回Json对象的restful-controller的正确方法是什么?

聚焦:

  • 从json到实体(实体加载)的转换应该在哪里?
  • 从实体到json的转换应该在哪里?
  • 应该在哪里放置@Transactional语句?
  • 重要:实体包含延迟加载的集合!

第一个天真的设计是:

@RequestMapping(value = "/{userId}", method = RequestMethod.GET)
@ResponseBody
public JsonUser doSomethingOnUser(@PathVariable("userId") Long userId) {

    // 1. load entities by ids
    User user = mUserRepository.findOne(userId);

    // 2. eventually validate

    // 3. perform changes on entities (create, update, delete)
    mUserService.DoSomething(user);

    // 4. convert back to json
    return mUserPresenter.convertToJsonUser(user);
}

动机是:

  • 尽快执行从identity的转换
  • 让控制器继续负责演示,在这种情况下,从实体转换为json

但是我遇到了几个与事务边界相结合的问题,加上了延迟加载和实体关系,所以它似乎是一个糟糕的设计。

您的最佳做法是什么?

1 个答案:

答案 0 :(得分:1)

尝试像这样构建它,这是一个非常频繁的解决方案,它是blue book中描述的域驱动开发方法,这里是同一作者批准的free short version

控制器

控制器不是事务性的,并且没有业务逻辑,因此它不会尝试导航可能导致LazyInitializationExceptions的用户对象图。

如果需要除业务逻辑以外的任何原因,则控制器调用返回急切获取的对象图的服务,或者首先将对象合并到会话中。

这是例外,而不是规则,通常控制器的作用是验证输入参数以查看它们是否具有正确的类型/强制参数,调用业务逻辑并为其准备DTO。如果需要则回复。

@RequestMapping(value = "/{userId}", method = RequestMethod.GET)
@ResponseBody
public JsonUser doSomethingOnUser(@PathVariable("userId") Long userId) {

    // all the business logic is in the service layer
    User user = mUserService.doSomething(userId);

    // conversion to DTO is handled in the controller layer, 
    // the domain does not know about DTOs
    return mUserPresenter.convertToJsonUser(user);
}

服务

该服务包含使用域模型编写的业务逻辑,并定义事务范围。

@Service
public class MyUserService {
    @Autowired
    private MyRepository repository;

    @Transactional
    public User doSomething(String userId) {

       //this object is attached due to @Transactional, no exceptions will be thrown
       User user = mUserRepository.findOne(userId);

       // do something with the attached object
       ....
}

存储库

存储库通常不是事务性的,因为它无法知道它所处的事务的范围。它负责从数据库中检索数据并将其转换为域对象,以及将域对象存储在数据库中:

@Repository
public class MyRepository {
    @PersistenceContext
    private EntityManager em;

    public void someDataRelatedMethod(...) {
        ... use entity manager ...
    }
}