Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA: Correct way to use Service and multiple Repositories

Tags:

I've made a system that use JPA and Spring. For example, If I need to handle Accounts, I use a repository:

@Repository
public interface AccountRepository extends JpaRepository<Account, Long>

Then I create a service that use the repository:

class AccountServiceImpl implements AccountService {

  @Autowired
  private AccountRepository repository;

  @Transactional
  public Account save(Account account){
    return repository.save(account);
  }

...

Now, I've created a controller that handle the POST method for Accounts:

@Controller
public class AccountController {

@Autowired    
private final accountService service;

@RequestMapping(value = "/account", method = RequestMethod.POST)
        public ModelAndView account(@Valid Account account, BindingResult bindingResult) {
    ...
service.save(account);

The same for Customer, Products, Contacts, etc.

Now, let suppose that I also have a class called "registration" that containts enough data to create a customer, with his accounts, contact data and products(a lot of data). The action "confirm" to a registration is the one dedicated to do that:

@RequestMapping(value = "/confirm", method = RequestMethod.POST)
    public ModelAndView confirmRegistration(@Valid Registration registration, BindingResult bindingResult) {

Now my question: What is the correct way to call the save method for every repository?

1) Should I create the classes in the controller and then call the save method for every class created:

@RequestMapping(value = "/confirm", method = RequestMethod.POST)
        public ModelAndView confirmRegistration(@Valid Registration registration, BindingResult bindingResult) {
...
customerService.save(customer);
accountService.save(account);
contactDataService.save(contactData);
productService.save(contactData);
...

2) Call the saves of each service in the RegistrationService:

class RegistrationServiceImpl implements RegistrationService {

  @Autowired
  private AccountService accountService;
  @Autowired
  private CustomerService customerService;
  ....

  @Transactional
  public void confirm(Registration registration){
  ... here I create the object
  customerService.save(customer);
  accountService.save(account);
  }

3) Call the saves of each repository in the RegistrationService:

class RegistrationServiceImpl implements RegistrationService {

  @Autowired
  private AccountRepository accountRepository;
  @Autowired
  private CustomerRepository customerRepository;
  ....

  @Transactional
  public void confirm(Registration registration){
  ... here I create the object
  customerRepository.save(customer);
  accountRepository.save(account);
  }

I undertand if I have to use (1). But confused about option (2) and (3).

The question again:

Should/Can I use other services in a service? Or I have to use only repositories in a service?

What is the correct way to google the explanation of this? Sorry English is not my native language and I can't find the correct way to ask about this kind of design.

like image 362
Mariano L Avatar asked Aug 23 '18 00:08

Mariano L


People also ask

Should I use JpaRepository or CrudRepository?

Crud Repository doesn't provide methods for implementing pagination and sorting. JpaRepository ties your repositories to the JPA persistence technology so it should be avoided. We should use CrudRepository or PagingAndSortingRepository depending on whether you need sorting and paging or not.

Can we have multiple Repository in Spring boot?

You can only have one repository per entity... however, you can have multiple entities per table; thus, having multiple repositories per table.


1 Answers

Services don’t always have to be transactional, but when you’re doing database work with JPA transactions are extremely important, because transactions make sure your changes get committed predictably without interference from other work going on concurrently. Spring makes it easy to make your services transactional, make sure you understand transactions so you can take full advantage of them.

You can use services within services, you can set up transaction propagation so they both use the same transaction or they can use separate transactions, there are valid cases for either of these. But I would suggest not doing what you’re doing here.

A service is a place you put business logic, especially business logic that you need to be transactional (all-or-nothing). It makes sense to organize your logic into services according to function, so that the service methods are actions taken by users playing some particular part.

But having a service for each type of entity isn’t really useful and I’d recommend against it. A service can have any number of repositories, you don’t have to wrap each one in its own service. (Tutorials show entity-specific services, or they may skip the service layer altogether, that’s because they want to show you framework features, and minimize the business logic. But real applications tend to have a lot of business logic.)

One problem with what you’re doing with entity-specific services: calling them in the controller one after the other means each one uses its own transaction. Creating transactions is slow and having separate ones opens you up to possible data inconsistencies. Having your business logic within one transaction limits your exposure to consistency issues to just those associated with your transaction isolation level.

Also I disagree with the idea that services should be converting between dtos and entities. Once in a while you may find a need for a dto but it shouldn’t be routine. For web applications that use JSP or thymeleaf you may be able to happily add entities as request attributes and let the template use them directly. If your controllers need to return JSON, you may be able to hook up a messageconverter to generate JSON directly from the entity or it may be better to have a dto. Try to keep the focus on implementing business functionality and avoid moving data from one kind of holder into a different kind of holder, because that kind of code is brittle and error-prone.

For differences between controllers and services I have answers here and here.

like image 110
Nathan Hughes Avatar answered Dec 04 '22 01:12

Nathan Hughes