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() 方法。

程序清单 11.6 将活跃的 Spitter 用户升级为 Elite 状态的 Repository 方法
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();
  }
}

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

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

public interface SpitterSweeper {
  int eliteSweep();
}

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

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

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

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

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

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

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

Last updated