11.3 借助 Spring Data 实现自动化的 JPARepository

尽管程序清单 11.2 和 11.3 程序清单中的方法都很简单,但它们依然还会直接与 EntityManager 交互来查询数据库。并且,仔细看一下的话,这些代码多少还是样板式的。例如,让我们重新审视 addSpitter() 方法:

public void addSpitter(Spitter spitter) {
entityManager.persist(spitter);
}

在任何具有一定规模的应用中,你可能会以几乎完全相同的方式多次编写这种方法。实际上,除了所持久化的 Spitter 对象不同以外,我敢打赌你以前肯定写过类似的方法。其实,JpaSpitterRepository 中的其他方法也没有什么太大的创造性。领域对象会有所不同,但是所有 Repository 中的方法都是很通用的。

为什么我们需要一遍遍地编写相同的持久化方法呢,难道仅仅是因为要处理的领域类型不同吗?Spring Data JPA 能够终结这种样板式的愚蠢行为。我们不再需要一遍遍地编写相同的 Repository 实现,Spring Data 能够让我们只编写 Repository 接口就可以了。根本就不再需要实现类了。

例如,看一下 SpitterRepository 接口。

程序清单 11.4 借助 Spring Data,以接口定义的方式创建 Repository
public interface SpitterRepository extends JpaRepository<Spitter, Long> {
}

此时,SpitterRepository 看上去并没有什么作用。但是,它的功能远超出了表面上所看到的那样。 编写 Spring Data JPA Repository 的关键在于要从一组接口中挑选一个进行扩展。这里,SpitterRepository 扩展了 Spring Data JPA 的 JpaRepository(稍后,我会介绍几个其他的接口)。通过这种方式,JpaRepository 进行了参数化,所以它就能知道这是一个用来持久化 Spitter 对象的 Repository,并且 Spitter 的 ID 类型为 Long。另外,它还会继承 18 个执行持久化操作的通用方法,如保存 Spitter、删除 Spitter 以及根据 ID 查询 Spitter。

此时,你可能会想下一步就该编写一个类实现 SpitterRepository 和它的 18 个方法了。如果真的是这样的话,那本章就会变得乏味无聊了。其实,我们根本不需要编写 SpitterRepository 的任何实现类,相反,我们让 Spring Data 来为我们做这件事请。我们所需要做的就是对它提出要求。

为了要求 Spring Data 创建 SpitterRepository 的实现,我们需要在 Spring 配置中添加一个元素。如下的程序清单展现了在 XML 配置中启用 Spring Data JPA 所需要添加的内容:

程序清单 11.5 配置 Spring Data JPA
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd">
<jpa:repository base-package="com.habuma.spittr.db" />
...
</beans>

<jpa:repository> 元素掌握了 Spring Data JPA 的所有魔力。就像 <context:component-scan> 元素一 样,<jpa:repository> 元素也需要指定一个要进行扫描的 base-package。不过,<context:component-scan> 会扫描包(及其子包)来查找带有 @Component 注解的类,而会扫描它的基础包来查找扩展自 Spring Data JPA Repository 接口的所有接口。如果发现了扩展自 Repository 的接口,它会自动生成(在应用启动的时候)这个接口的实现。

如果要使用 Java 配置的话,那就不需要使用 <jpa:repository> 元素了,而是要在 Java 配置类上添加 @EnableJpaRepositories 注解。如下就是一个 Java 配置类,它使用了 @EnableJpaRepositories 注解,并且会扫描 com.habuma.spittr.db 包:

@Configuration
@EnableJpaRepositories(basePackages="com.habuma.spittr.db")
public class JpaConfiguration {
}

让我们回到 SpitterRepository 接口,它扩展自 JpaRepository,而 JpaRepository 又扩展自 Repository 标记接口 (虽然是间接的)。因此,SpitterRepository 就传递性地扩展了 Repository 接口,也就是 Repository 扫描时所要查找的接口。当 Spring Data 找到它后,就会创建 SpitterRepository 的实现类,其中包含了继承自 JpaRepository、PagingAndSortingRepository 和 CrudRepository 的 18 个方法。

很重要的一点在于 Repository 的实现类是在应用启动的时候生成的,也就是 Spring 的应用上下文创建的时候。它并不是在构建时通过代码生成技术产生的,也不是接口方法调用时才创建的。

很漂亮的技术,对吧?

Spring Data JPA 很棒的一点在于它能为 Spitter 对象提供 18 个便利的方法来进行通用的 JPA 操作,而无需你编写任何持久化代码。但是,如果你的需求超过了它所提供的这 18 个方法的话,该怎么办呢?幸好,Spring Data JPA 提供了几种方式来为 Repository 添加自定义的方 法。让我们看一下如何为 Spring Data JPA 编写自定义的查询方法。