Spring Data Composable Repositories – Spring Data可组合存储库

最后修改: 2018年 8月 18日

中文/混合/英文(键盘快捷键:t)

 

1. Introduction

1.绪论

When modeling a real-world system or process, domain-driven design (DDD) style repositories are a good option. For this very purpose, we can use Spring Data JPA as our data access abstraction layer.

当对现实世界的系统或流程进行建模时,领域驱动设计(DDD)风格的存储库是一个不错的选择。正是为了这个目的,我们可以使用Spring Data JPA作为我们的数据访问抽象层。

If you are new to this concept check out this introductory tutorial to help get you up to speed.

如果你是这个概念的新手,请查看这个介绍性的教程,以帮助你快速掌握。

In this tutorial, we’ll focus on the concept of creating custom as well as composable repositories which are created using smaller repositories called fragments.

在本教程中,我们将专注于创建自定义以及可组合的存储库的概念,这些存储库是通过称为片段的更小的存储库创建的。

2. Maven Dependencies

2.Maven的依赖性

The option to create composable repositories is available starting with Spring 5.

从Spring 5开始,创建可组合存储库的选项是可用的。

Let’s add the required dependency for Spring Data JPA:

让我们为Spring Data JPA添加必要的依赖关系。

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>2.2.2.RELEASE</version>
</dependency>

We’d also need to set up a data source in order for our data access layer to work. It’s a good idea to set up an in-memory database like H2 for development and quick testing.

我们还需要设置一个数据源,以便我们的数据访问层能够工作。设置一个像H2这样的内存数据库是一个好主意,用于开发和快速测试。

3. Background

3. 背景

3.1. Hibernate as JPA Implementation

3.1.Hibernate作为JPA的实现

Spring Data JPA, by default, uses Hibernate as the JPA implementation. We can easily confuse one with the other or compare them but they serve different purposes.

Spring Data JPA,默认使用Hibernate作为JPA的实现。我们很容易将两者混为一谈,或者将它们进行比较,但它们的目的是不同的。

Spring Data JPA is the data access abstraction layer below which we can use any implementation. We could, for instance, switch out Hibernate in favor of EclipseLink.

Spring Data JPA是数据访问抽象层,在它下面我们可以使用任何实现。例如,我们可以换掉Hibernate而使用EclipseLink

3.2. Default Repositories

3.2.默认存储库

In a lot of cases, we wouldn’t need to write any queries ourselves.

在很多情况下,我们不需要自己写任何查询。

Instead, we only need to create interfaces which in turn extend the generic Spring data repository interfaces:

相反,我们只需要创建一些接口,这些接口反过来又扩展了通用的Spring数据存储库接口。

public interface LocationRepository extends JpaRepository<Location, Long> {
}

And this, in itself, would allow us to do common operations – CRUD, paging, and sorting – on the Location object which has a primary key of type Long.

而这本身将允许我们对Location对象进行普通操作–CRUD、分页和排序,该对象有一个Long类型的主键。

Furthermore, Spring Data JPA comes equipped with a query builder mechanism which provides the ability to generate queries on our behalf using method name conventions:

此外,Spring Data JPA配备了一个查询生成器机制,它提供了使用方法名称约定代表我们生成查询的能力。

public interface StoreRepository extends JpaRepository<Store, Long> {
    List<Store> findStoreByLocationId(Long locationId);
}

3.3. Custom Repositories

3.3.自定义存储库

If required, we can enrich our model repository by writing a fragment interface and implementing the desired functionality. This can then be injected in our own JPA repository.

如果需要,我们可以通过编写一个片段接口和实现所需的功能来丰富我们的模型库。然后,这可以被注入到我们自己的JPA资源库中。

For example, here we are enriching our ItemTypeRepository by extending a fragment repository:

例如,这里我们通过扩展一个片段库来丰富我们的ItemTypeRepository

public interface ItemTypeRepository 
  extends JpaRepository<ItemType, Long>, CustomItemTypeRepository {
}

Here CustomItemTypeRepository is another interface:

这里CustomItemTypeRepository是另一个接口。

public interface CustomItemTypeRepository {
    void deleteCustomById(ItemType entity);
}

Its implementation can be a repository of any kind, not just JPA:

它的实现可以是任何类型的存储库,而不仅仅是JPA。

public class CustomItemTypeRepositoryImpl implements CustomItemTypeRepository {
 
    @Autowired
    private EntityManager entityManager;

    @Override
    public void deleteCustomById(ItemType itemType) {
        entityManager.remove(itemType);
    }
}

We just need to make sure that it has the postfix Impl. However, we can set a custom postfix by using the following XML configuration:

我们只需要确保它有后缀Impl。然而,我们可以通过使用以下XML配置来设置一个自定义的后缀。

<repositories base-package="com.baeldung.repository" repository-impl-postfix="CustomImpl" />

or by using this annotation:

或通过使用该注释。

@EnableJpaRepositories(
  basePackages = "com.baeldung.repository", repositoryImplementationPostfix = "CustomImpl")

4. Composing Repositories Using Multiple Fragments

4.使用多个片段组成存储库

Up until a few releases ago, we could only extend our repository interfaces using a single custom implementation. This was a limitation because of which we’d have to bring all related functionality into a single object.

直到几个版本之前,我们只能用一个单一的自定义实现来扩展我们的存储库接口。这是一个限制,因为我们必须把所有相关的功能都纳入一个单一的对象。

Needless to say, for larger projects with complex domain models, this lead to bloated classes.

不用说,对于具有复杂领域模型的大型项目来说,这导致了臃肿的类。

Now with Spring 5, we have the option to enrich our JPA repository with multiple fragment repositories. Again, the requirement remains that we have these fragments as interface-implementation pairs.

现在有了Spring 5,我们可以选择用多个片段库来丰富我们的JPA库。同样,我们的要求仍然是将这些片段作为接口-实现对。

To demonstrate this, let’s create two fragments:

为了证明这一点,让我们创建两个片段。

public interface CustomItemTypeRepository {
    void deleteCustom(ItemType entity);
    void findThenDelete(Long id);
}

public interface CustomItemRepository {
    Item findItemById(Long id);
    void deleteCustom(Item entity);
    void findThenDelete(Long id);
}

Of course, we’d need to write their implementations. But instead of plugging these custom repositories – with related functionalities – in their own JPA repositories, we can extend the functionality of a single JPA repository:

当然,我们需要编写他们的实现。但是,与其把这些自定义的存储库–相关的功能–插在他们自己的JPA存储库中,我们可以扩展单个JPA存储库的功能。

public interface ItemTypeRepository 
  extends JpaRepository<ItemType, Long>, CustomItemTypeRepository, CustomItemRepository {
}

Now, we’d have all the linked functionality in one single repository.

现在,我们会在一个单一的资源库中拥有所有的链接功能。

5. Dealing with Ambiguity

5.处理含糊不清的问题

Since we’re inheriting from multiple repositories, we may have trouble figuring out which of our implementations would be used in case of a clash. For instance, in our example, both fragment repositories have a method, findThenDelete(), with the same signature.

由于我们是从多个资源库继承的,我们可能很难弄清楚在发生冲突的情况下,我们的哪个实现会被使用。例如,在我们的例子中,两个片段库都有一个方法,findThenDelete(),具有相同的签名。

In this scenario, the order of the declaration of the interfaces is used to resolve the ambiguity. Consequently, in our case, the method inside CustomItemTypeRepository will be used since it’s declared first.

在这种情况下,接口的声明顺序被用来解决模糊的问题。因此,在我们的例子中,CustomItemTypeRepository中的方法将被使用,因为它被首先声明。

We can test this by using this test case:

我们可以用这个测试案例来测试。

@Test
public void givenItemAndItemTypeWhenDeleteThenItemTypeDeleted() {
    Optional<ItemType> itemType = composedRepository.findById(1L);
    assertTrue(itemType.isPresent());

    Item item = composedRepository.findItemById(2L);
    assertNotNull(item);

    composedRepository.findThenDelete(1L);
    Optional<ItemType> sameItemType = composedRepository.findById(1L);
    assertFalse(sameItemType.isPresent());

    Item sameItem = composedRepository.findItemById(2L);
    assertNotNull(sameItem);
}

6. Conclusion

6.结语

In this article, we took a look at the different ways through which we can use Spring Data JPA repositories. We saw that Spring makes it simple to perform database operations on our domain objects without writing much code or even SQL queries.

在这篇文章中,我们看了一下我们可以通过哪些不同的方式来使用Spring Data JPA存储库。我们看到,Spring使得在我们的领域对象上执行数据库操作变得很简单,不需要写很多代码,甚至不需要SQL查询。

This support is considerably customizable through the use of composable repositories.

通过使用可组合的存储库,这种支持是相当可定制的。

The code snippets from this article are available as a Maven project here on GitHub.

本文的代码片段可作为GitHub上的Maven项目