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

标签 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);
  }

...

现在,我创建了一个 Controller 来处理帐户的 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 中创建类,然后为创建的每个类调用保存方法吗:

@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) 在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)在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)感到困惑。

再次提问:

我应该/可以在服务中使用其他服务吗?或者我必须仅使用服务中的存储库?

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

最佳答案

服务并不总是必须是事务性的,但是当您使用 JPA 进行数据库工作时,事务非常重要,因为事务可确保您的更改可预测地提交,而不会受到同时进行的其他工作的干扰。 Spring 可以轻松地使您的服务具有事务性,请确保您了解事务,以便可以充分利用它们。

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

服务是放置业务逻辑的地方,尤其是需要事务性(全有或全无)的业务逻辑。根据功能将逻辑组织成服务是有意义的,这样服务方法就是扮演某个特定角色的用户所采取的操作。

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

使用特定于实体的服务时存在一个问题:在 Controller 中依次调用它们意味着每个服务都使用自己的事务。创建事务的速度很慢,并且单独的事务可能会导致数据不一致。将您的业务逻辑放在一个事务中,可以将您面临的一致性问题限制为仅与事务隔离级别相关的问题。

此外,我不同意服务应该在 dto 和实体之间进行转换的想法。有时您可能会发现需要 dto,但这不应该是例行公事。对于使用 JSP 或 thymeleaf 的 Web 应用程序,您可以愉快地添加实体作为请求属性,并让模板直接使用它们。如果您的 Controller 需要返回 JSON,您可以连接消息转换器以直接从实体生成 JSON,或者最好使用 dto。尝试将重点放在实现业务功能上,并避免将数据从一种持有者移动到另一种持有者,因为这种代码很脆弱且容易出错。

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

关于java - JPA:使用服务和多个存储库的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51976807/

相关文章:

java - 调试在 Oracle JVM 中运行的 Java 代码

java - Spring JUnit 测试测试类中的 Autowiring 变量

java - HSQLDB 和 Hibernate/JPA - 不持久化到磁盘?

java - 如何在 Oracle 中使用 JPA 存储日期和时间?

java - Hibernate 生成带有 JPA @Enumerated 注释的 VARBINARY 列

java - 如何创建可以修改的对象的副本?

java - 向 java maven 项目添加新组件

java - 如何逐行扫描输入整数?

java - Spring测试NoSuchBeanDefinitionException

java - org.apache.tiles.TilesException : Attribute 'console' not found