Question

What is the right way to implement a restful-controller returning Json-objects with Spring MVC and JPA?

Focus:

  • Where should occur the conversion from json to entities (entity loading)?
  • Where should occur the conversion from entities to json?
  • Where should be placed the @Transactional statements?
  • Important: entities contains lazy loaded collection!

A first naive design was:

@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);
}

Motivation was to:

  • perform the conversion from id to entity as soon as possible
  • let the controller remain responsible for the presentation, in this case convert from entities to json

But I get several issues with transactions boundaries combined with lazy-loading and entities relations, so that it seems to be a bad design.

What are your best practices?

Was it helpful?

Solution

Try structuring it like this, this is a very frequent solution, it's the Domain Driven Development approach described in the blue book, here is a free short version approved by the same author:

Controller

The controller is not transactional and does not have business logic, so it does not try to navigate the user object graph which could cause LazyInitializationExceptions.

If by any reason other than business logic this is needed, then either the controller calls a service that returns an eager fetched object graph or it first merges the object into a session.

This is meant to be the exception and not the rule, in general the role of the controller is to validate the input parameters to see if they have the correct type/mandatory parameters, call the business logic and prepare a DTO for the response if one is needed.

@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

The service contains the business logic written using the domain model, and defines the transaction scope.

@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

The repository is usually not transactional, as it cannot know the scope of the transaction it's in. It is responsible for retrieving data from the database and transforming it in domain objects, and for storing domain objects in the database:

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

    public void someDataRelatedMethod(...) {
        ... use entity manager ...
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top