在 Spring Data JPA 存储库中使用泛型 [英] Using generics in Spring Data JPA repositories

查看:30
本文介绍了在 Spring Data JPA 存储库中使用泛型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有许多简单的对象类型需要持久化到数据库中.我正在使用 Spring JPA 来管理这种持久性.对于每个对象类型,我需要构建以下内容:

I have a number of simple object types that need to be persisted to a database. I am using Spring JPA to manage this persistence. For each object type I need to build the following:

import org.springframework.data.jpa.repository.JpaRepository;

public interface FacilityRepository extends JpaRepository<Facility, Long> {
}


public interface FacilityService {
    public Facility create(Facility facility);
}

@Service
public class FacilityServiceImpl implements FacilityService {

    @Resource
    private FacilityRepository countryRepository;

    @Transactional
    public Facility create(Facility facility) {
        Facility created = facility;
        return facilityRepository.save(created);
    }
}

我突然想到可以用三个基于泛型的类替换每个对象类型的多个类,从而节省大量样板代码.我不确定如何去做,事实上它是否是个好主意?

It occurred to me that it may be possible to replace the multiple classes for each object type with three generics based classes, thus saving a lot of boilerplate coding. I am not exactly sure how to go about it and in fact if it is a good idea?

推荐答案

首先,我知道我们在这里提高了很多标准,但这已经比没有 Spring 帮助的情况下编写的代码要少得多数据 JPA.

First of all, I know we're raising the bar here quite a bit but this is already tremendously less code than you had to write without the help of Spring Data JPA.

第二,我认为您首先不需要服务类,如果您所做的只是将调用转发到存储库.如果您的业务逻辑需要在事务内协调不同的代码库或有其他业务逻辑要封装,我们建议在代码库前使用服务.

Second, I think you don't need the service class in the first place, if all you do is forward a call to the repository. We recommend using services in front of the repositories if you have business logic that needs orchestration of different repositories within a transaction or has other business logic to encapsulate.

一般来说,你当然可以这样做:

Generally speaking, you can of course do something like this:

interface ProductRepository<T extends Product> extends CrudRepository<T, Long> {

    @Query("select p from #{#entityName} p where ?1 member of p.categories")
    Iterable<T> findByCategory(String category);

    Iterable<T> findByName(String name);
}

这将允许您像这样在客户端使用存储库:

This will allow you to use the repository on the client side like this:

class MyClient {

  @Autowired
  public MyClient(ProductRepository<Car> carRepository, 
                  ProductRepository<Wine> wineRepository) { … }
}

它会按预期工作.不过有几点需要注意:

and it will work as expected. However there are a few things to notice:

这仅在域类使用单表继承时才有效.我们在引导时可以获得的域类的唯一信息是它将是 Product 对象.因此,对于像 findAll() 甚至 findByName(…) 这样的方法,相关查询将从 select p from Product p where… 开始.这是因为反射查找永远不会产生 WineCar 除非你为它创建一个专用的存储库接口捕获具体的类型信息.

This only works if the domain classes use single table inheritance. The only information about the domain class we can get at bootstrap time is that it will be Product objects. So for methods like findAll() and even findByName(…) the relevant queries will start with select p from Product p where…. This is due to the fact that the reflection lookup will never ever be able to produce Wine or Car unless you create a dedicated repository interface for it to capture the concrete type information.

一般来说,我们建议为每个聚合根创建存储库接口.这意味着您没有针对每个域类本身的存储库.更重要的是,通过存储库对服务进行 1:1 的抽象也完全没有意义.如果您构建服务,则不会为每个存储库构建一个(猴子可以做到,我们不是猴子,对吗?;).服务公开了更高级别的 API,更多是用例驱动,并且通常会协调对多个存储库的调用.

Generally speaking, we recommend creating repository interfaces per aggregate root. This means you don't have a repo for every domain class per se. Even more important, a 1:1 abstraction of a service over a repository is completely missing the point as well. If you build services, you don't build one for every repository (a monkey could do that, and we're no monkeys, are we? ;). A service is exposing a higher level API, is much more use-case drive and usually orchestrates calls to multiple repositories.

此外,如果您在存储库之上构建服务,您通常希望强制客户端使用该服务而不是存储库(这里的一个经典示例是用于用户管理的服务也会触发密码生成和加密,因此让开发人员直接使用存储库绝不是一个好主意,因为他们可以有效地解决加密问题).因此,您通常希望选择谁可以持久化哪些域对象,以免到处创建依赖项.

Also, if you build services on top of repositories, you usually want to enforce the clients to use the service instead of the repository (a classical example here is that a service for user management also triggers password generation and encryption, so that by no means it would be a good idea to let developers use the repository directly as they'd effectively work around the encryption). So you usually want to be selective about who can persist which domain objects to not create dependencies all over the place.

是的,您可以构建通用存储库并将它们用于多种域类型,但存在非常严格的技术限制.尽管如此,从架构的角度来看,您上面描述的场景甚至不应该出现,因为这意味着您无论如何都面临着设计的味道.

Yes, you can build generic repositories and use them with multiple domain types but there are quite strict technical limitations. Still, from an architectural point of view, the scenario you describe above shouldn't even pop up as this means you're facing a design smell anyway.

这篇关于在 Spring Data JPA 存储库中使用泛型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆