Spring Data JPA 从入门到精通~自定义实现Repository

37 篇文章 10 订阅

EntityManager 的获取方式

我们既然要自定义,首先讲一下 EntityManager 的两种获取方式。

1. 通过 @PersistenceContext 注解。

通过将 @PersistenceContext 注解标注在 EntityManager 类型的字段上,这样得到的 EntityManager 就是容器管理的 EntityManager。由于是容器管理的,所以我们不需要也不应该显式关闭注入的 EntityManager 实例。

@Repository
@Transactional(readOnly = true)
public class UserRepositoryImpl implements UserRepositoryCustom {
    @PersistenceContext  //获得entityManager的实例
    EntityManager entityManager;
}

2. 继承 SimpleJpaRepository 成为子类,实现构造方法即可,这时候我们直接用父类里面的 EntityManager 即可。

public class BaseRepositoryCustom<T, ID> extends SimpleJpaRepository<T, ID> {
    public BaseRepositoryCustom(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
    }
    public BaseRepositoryCustom(Class<T> domainClass, EntityManager em) {
        super(domainClass, em);
    }
}

自定义 Repository 的两种场景

我们自定义实现 Repository,主要的应用场景有两种:

  • 个别特殊化场景,私有的。
  • 公用的通用的场景,替代默认的 SimpleJpaRepository 的场景,架构层面出发。

1. 自定义个别的特殊场景,私有的 Repository。

这种方法就是需要自己创建一个接口,和对应的接口实现类,若需要用到特殊化的实现方法的话,***Respository 只需要继承你自定义的接口即可,主要有两种能力:

  • 实现自定义接口
  • 可以直接覆盖 Spring Data JPA 给我们提供的默认 ***Respository 的接口里面的方法。

案例1:单个私有的 Repository 接口实现类

(1)创建自定义接口

/**
 * @author jack
 */
public interface UserRepositoryCustom {
    /**
     * 自定义一个查询方法,name的like查询,此处仅仅是演示例子,实际中直接用QueryMethod即可
     * @param firstName
     * @return
     */
    List<User> customerMethodNamesLike(String firstName);
}

(2)自定义存储库功能的实现

/**
 * 用@Repository 将此实现交个Spring bean加载
 * 咱们模仿SimpleJpaRepository 默认将所有方法都开启一个事务
 */
@Repository
@Transactional(readOnly = true)
public class UserRepositoryCustomImpl implements UserRepositoryCustom {
    @PersistenceContext
    EntityManager entityManager;
    /**
     * 自定义一个查询firstname的方法
     * @param firstName
     * @return
     */
    @Override
    public List<User> customerMethodNamesLike(String firstName) {
        Query query = entityManager.createNativeQuery("SELECT u.* FROM user as u " +
                "WHERE u.name LIKE ?", User.class);
        query.setParameter(1, firstName + "%");
        return query.getResultList();
    }
}

我们这里采用 entityManager,当然了也不排除自己通过最底层的 JdbcTemplate 来自己实现逻辑。

(3)由于这个接口是为 User 单独写的,但是同时也可以继承和 @Repository 的任何子类。

/**
 * 使用的时候直接继承 UserRepositoryCustom接口即可
 */
public interface UserRepository extends Repository<User, Long>,UserRepositoryCustom {
}

Controller 的调用方式如下:

/**
 * 调用我们自定义的实现方法
 *
 * @return
 */
@GetMapping(path = "/customer")
@ResponseBody
public Iterable<User> findCustomerMethodNamesLike() {
    return userRepository.customerMethodNamesLike("jack");
}

(4)其实通过上述方法我们可以实现多个自定义接口:

//如:我们自定义了HumanCustomerRepository, ContactCustomerRepository两个Repository
interface UserRepository extends CrudRepository<User, Long>, HumanCustomerRepository, ContactCustomerRepository {
  // 用的时候只需要继承多个自定义接口即可
}

(5)覆盖 JPA 里面的默认实现方法

Spring Data JPA 的底层实现里面,自定义的 Repositories 的实现类和方法要高于它帮我们提供的 Repositories,所以当我们有场景需要覆盖默认实现的时候其 demo 如下:

//假设我们要覆盖默认的save方法的逻辑
interface CustomizedSave<T> {
  <S extends T> S save(S entity);
}
class CustomizedSaveImpl<T> implements CustomizedSave<T> {
  public <S extends T> S save(S entity) {
    // Your custom implementation
  }
}
//用法保持不变,如下:
interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> {
}
//CustomizedSave通过泛化可以被多个Repository使用
interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> {
}

实际工作中应用于逻辑删除场景:

在实际工作的生产环境中,我们可能经常会用到逻辑删除,所以做法是一般自定义覆盖 Data JPA 帮我们提供 remove 方法,然后实现逻辑删除的逻辑即可。

2:公用的通用的场景,替代默认的 SimpleJpaRepository 的场景,从架构层面出发。

案例2:定义一个公用的 Repository 接口的实现类。

通过构造方法获得 EntityManager,需要用到 Java 的泛化技术。当你想将一个方法添加到所有的存储库接口时,上述方法是不可行的,要将自定义行为添加到所有存储库,首先添加一个中间接口来声明共享行为。

(1)声明定制共享行为的接口,用 @NoRepositoryBean:

//因为要公用,所以必须要通用,不能失去本身的Spring Data JPA给我们提供的默认方法,所有我们继承相关的Repository类
@NoRepositoryBean
public interface MyRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
  void sharedCustomMethod(ID id);
}

(2)继承 SimpleJpaRepository 扩展自己的方法实现逻辑:

public class MyRepositoryImpl<T, ID extends Serializable>
  extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {
  private final EntityManager entityManager;
  public MyRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
    super(entityInformation, entityManager);
    // Keep the EntityManager around to used from the newly introduced methods.
    this.entityManager = entityManager;
  }
  public void sharedCustomMethod(ID id) {
    // 通过entityManager实现自己的额外方法的实现逻辑。这里不多说了
  }
}

注意:该类需要具有专门的存储库工厂实现使用超级类的构造函数,如果存储库基类有多个构造函数,则覆盖一个 EntityInformation 加上特定于存储的基础架构对象(例如,一个 EntityManager 或一个模板类),也可以重写 SimpleJpaRepository 的任何逻辑。如逻辑删除放在这里面实现,就不要所有的 Repository 去关心实现哪个接口了。

(3)使用 JavaConfig 配置自定义 MyRepositoryImpl 作为其他接口的动态代理的实现基类。

具有全局的性质,即使没有继承它所有的动态代理类也会变成它。

@Configuration
@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }

(4)使用的时候就可以显示的选择用哪个接口,从而选择性的暴露 SimpleJpaRepository 的实现方法。

现在,各个存储库接口将扩展此中间接口,而不是扩展 Repository 接口以包含声明的功能。接下来,创建扩展了持久性技术特定的存储库基类的中间接口的实现。然后,该类将用作存储库代理的自定义基类。

//如果你要使用你自定义的全局MyRepositoryImpl只需要继承接口即可,如下:
interface PersonRepository extends MyRepositoryImpl<Person, Long>{
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值