JPA:使用服务和多个存储库的正确方法

时间:2018-08-23 00:25:18

标签: java spring jpa spring-data-jpa

我制作了一个使用JPA和Spring的系统。例如,如果我需要处理帐户,则可以使用存储库:

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

然后我创建一个使用存储库的服务:

class AccountServiceImpl implements AccountService {

  @Autowired
  private AccountRepository repository;

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

...

现在,我创建了一个控制器,用于处理Accounts的POST方法:

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

与客户,产品,联系人等相同。

现在,假设我还有一个名为“注册”的类,该类包含足够的数据以创建一个带有客户,客户数据和产品(很多数据)的客户。进行注册的“确认”操作是专门用于执行此操作的操作:

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

现在我的问题:为每个存储库调用 save 方法的正确方法是什么?

1)我应该在 controller 中创建类,然后为每个创建的类调用save方法:

@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)调用注册服务中的每个服务的保存

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

我必须知道是否必须使用(1)。但是对于选项(2)和(3)感到困惑。

问题再次出现:

应该/可以在服务中使用其他服务吗?还是只需要在服务中使用存储库?

谷歌对此的正确解释是什么?抱歉,英语不是我的母语,我找不到正确的方式询问这种设计。

2 个答案:

答案 0 :(得分:3)

控制器 理想情况下,控制器仅用于接受HTTP请求并提供响应。如果数据是保存在数据库或文件中,还是通过另一个HTTP调用发送到另一个服务,则控制器不必理会。 始终使控制器不受任何业务影响。

服务

服务层是您将数据传输对象转换为实体(反之亦然)的地方。控制器接收可能/可能不直接映射到数据库实体的数据对象。此转换由服务层完成。 在您的情况下,您的控制器会收到Registration

因此,最好的方法是让您的控制器调用RegistrationService并将接收到的数据对象传递给它以进行处理。

现在注册服务的工作是将数据对象转换为数据库实体并将其保存在事务中。


  

应该/可以在服务中使用其他服务吗?或者我只需要使用   服务中的存储库?

我希望将关注点分开。

例如,只有AccountsService应该知道帐户存储库的内外信息。帐户服务应像处理帐户的中间人一样工作。您要保存吗?把它给我。您要找到吗?我会为您找到。

简而言之,我更喜欢RegistrationService调用Other服务,而不是直接调用存储库。

答案 1 :(得分:2)

服务并不一定必须是事务性的,但是当您使用JPA进行数据库工作时,事务非常重要,因为事务可确保您的更改以可预测的方式提交,而不会受到同时进行的其他工作的干扰。 Spring使您的服务变得易于事务化,确保您了解事务,从而可以充分利用它们。

可以使用服务内的服务,您可以设置事务传播,以便它们都使用相同的事务或可以使用单独的事务,这两种情况都有有效的情况。但我建议您不要在这里做什么。

服务是放置业务逻辑的地方,特别是需要进行事务处理(全有或全无)的业务逻辑。根据功能将逻辑组织到服务中是有意义的,因此服务方法是用户扮演某些特定角色所采取的操作。

但是为每种类型的实体提供服务并不是真的有用,我建议您不要这样做。一个服务可以有任意数量的存储库,您不必将每个存储库都包装在自己的服务中。 (教程显示了特定于实体的服务,或者它们可能完全跳过了服务层,这是因为它们想要向您展示框架功能,并最小化业务逻辑。但是实际的应用程序往往具有很多业务逻辑。)

您在处理特定于实体的服务时遇到了一个问题:一个接一个地在控制器中调用它们意味着每个人都使用自己的交易。创建事务很慢,并且有单独的事务会使您容易出现数据不一致的情况。将您的业务逻辑包含在一个事务中可以将您遇到的一致性问题限制为与事务隔离级别相关的问题。

我也不同意服务应该在dto和实体之间转换的想法。偶尔,您可能会需要dto,但这并不是日常工作。对于使用JSP或thymeleaf的Web应用程序,您可以愉快地将实体添加为请求属性,并让模板直接使用它们。如果您的控制器需要返回JSON,则可以挂接messageconverter以直接从实体生成JSON,或者最好具有dto。尝试着重于实现业务功能,并避免将数据从一种持有人转移到另一种持有人,因为这种代码易碎且容易出错。

对于控制器和服务之间的差异,我有答案herehere