> For the complete documentation index, see [llms.txt](https://potoyang.gitbook.io/spring-in-action-v4/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://potoyang.gitbook.io/spring-in-action-v4/untitled-5/untitled-1/untitled.md).

# 11.3.3　混合自定义的功能

有些时候，我们需要 Repository 所提供的功能是无法用 Spring Data 的方法命名约定来描述的，甚至无法用 @Query 注解设置查询来实现。尽管 Spring Data JPA 非常棒，但是它依然有其局限性，可能需要我们按照传统的方式来编写 Repository 方法：也就是直接使用 EntityManager。当遇到这种情况的时候，我们是不是要放弃 Spring Data JPA，重新按照 11.2.2 小节中的方式来编写 Repository 呢？

简单来说，是这样的。如果你需要做的事情无法通过 Spring Data JPA 来实现，那就必须要在一个比 Spring Data JPA 更低的层级上使用 JPA。好消息是我们没有必要完全放弃 Spring Data JPA。我们只需在必须使用较低层级 JPA 的方法上，才使用这种传统的方式即可，而对于 Spring Data JPA 知道该如何处理的功能，我们依然可以通过它来实现。

当 Spring Data JPA 为 Repository 接口生成实现的时候，它还会查找名字与接口相同，并且添加了 Impl 后缀的一个类。如果这个类存在的话，Spring Data JPA 将会把它的方法与 Spring Data JPA 所生成的方法合并在一起。对于 SpitterRepository 接口而言，要查找的类名为 SpitterRepositoryImpl。

为了阐述该功能，假设我们需要在 SpitterRepository 中添加一个方法，发表 Spittle 数量在 10,000 及以上的 Spitter 将会更新为 Elite 状态。使用 Spring Data JPA 的方法命名约定或使用 @Query 均没有办法声明这样的方法。最为可行的方案是使用如下的 eliteSweep() 方法。

{% code title="程序清单 11.6　将活跃的 Spitter 用户升级为 Elite 状态的 Repository 方法" %}

```java
public class SpitterRepositoryImpl implements SpitterSweeper {
  @PersistenceContext
  private EntityManager em;
  
  public int eliteSweep() {
    String update = 
      "UPDATE Spitter spitter " +
      "SET spitter.status = 'Elite' " +
      "WHERE spitter.status = 'Newbie' " +
      "AND spitter.id IN (" +
      "SELECT s FROM Spitter s WHERE (" +
      "  SELECT COUNT(spittles) FROM s.spittles spittles) > 10000" +
      ")";
    return em.createQuery(update).executeUpdate();
  }
}
```

{% endcode %}

我们可以看到，eliteSweep() 方法与之前在 11.2.2 小节中所创建的 Repository 方法并没有太大的差别。SpitterRepositoryImpl 没有什么特殊之处，它使用被注入的 EntityManager 来完成预期的任务。

注意，SpitterRepositoryImpl 并没有实现 SpitterRepository 接口。Spring Data JPA 负责实现这个接口。SpitterRepositoryImpl（将它与 Spring Data 的 Repository 关联起来的是它的名字）实现了 SpitterSweeper 接口，它如下所示：

```java
public interface SpitterSweeper {
  int eliteSweep();
}
```

我们还需要确保 eliteSweep() 方法会被声明在 SpitterRepository 接口中。要实现这一点，避免代码重复的简单方式就是修改 SpitterRepository，让它扩展 SpitterSweeper：

```java
public interface SpitterRepository extends JpaRepository<Spitter, Long>, SpitterSweeper {
  ...
}
```

如前所述，Spring Data JPA 将实现类与接口关联起来是基于接口的名称。但是，Impl 后缀只是默认的做法，如果你想使用其他后缀的话，只需在配置 @EnableJpa-Repositories 的时候，设置 repositoryImplementationPostfix 属性即可：

```java
@@EnableJpaRepositories(basePackage="com.habuma.spittr.db", repositoryImplementationPostfix="Helper")
```

如果在 XML 中使用 \<jpa:repository> 元素来配置 Spring Data JPA 的话，我们可以借助 repository-impl-postfix 属性指定后缀：&#x20;

```markup
<jpa:repository base-package="com.habuma.spittr.db" repository-impl-postfix="Helper" />
```

我们将后缀设置成了 Helper，Spring Data JPA 将会查找名为 SpitterRepository-Helper 的类，用它来匹配 SpitterRepository 接口。
