返回介绍

4.2 扩展 JPA 功能

发布于 2025-04-26 13:26:33 字数 8228 浏览 0 评论 0 收藏

使用 JPA,在资源库接口定义中不但可以按照其规则约定的方法声明各种方法(像在第 2 章中介绍的那样),也可以使用注解 @Query 来定义一些简单的查询语句,如代码清单 4-4 所示。本节还将介绍如何在全局的范围中,扩展 JPA 接口的功能。

代码清单 4-4 使用 @Query 自定义查询

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    @Query("select t from User t where t.name =?1 and t.email =?2")
    User findByNameAndEmail(String name, String email);
    @Query("select t from User t where t.name like :name")
    Page<User> findByName(@Param("name") String name, Pageable pageRequest);
}

4.2.1 扩展 JPA 接口

首先创建一个接口,继承于 JpaRepository,并将接口标记为 @NoRepositoryBean,以防被当作一般的 Repository 调用,如代码清单 4-5 所示。接口 ExpandJpaRepository 不仅扩展了 JPA 原来的 findOne、findAll、count 等方法,而且增加了 deleteByIds、get-EntityClass、nativeQuery4Map 等方法,其中 nativeQuery4Map 用来执行原生的复杂的 SQL 查询语句。

代码清单 4-5 扩展 JPA 接口定义

@NoRepositoryBean
public interface ExpandJpaRepository<T, ID extends Serializable> extends JpaRepository<T,ID> {
    T findOne(String condition, Object... objects);
    List<T> findAll(String condition, Object... objects);
    List<T> findAll(Iterable<Predicate> predicates, Operator operator);
    List<T> findAll(Iterable<Predicate> predicates, Operator operator, Sort sort);
    Page<T> findAll(Iterable<Predicate> predicates, Operator operator, Pageable
pageable);
    long count(Iterable<Predicate> predicates, Operator operator);
    List<T> findAll(String condition, Sort sort, Object... objects);
    Page<T> findAll(String condition, Pageable pageable, Object... objects);
    long count(String condition, Object... objects);
    void deleteByIds(Iterable<ID> ids);
    Class<T> getEntityClass();
    List<Map<String,Object>> nativeQuery4Map(String sql);
    Page<Map> nativeQuery4Map(String sql, Pageable pageable);
Object nativeQuery4Object(String sql);
}

这一接口的所有声明方法,必须由我们来实现。为了节省篇幅,只列出实现的部分代码,如代码清单 4-6 所示。完整的代码可以通过检出实例工程查看。实现代码中使用了 JPQL 查询语言(Java Persistence Query Language),它是 JPA 的查询语句规范。

代码清单 4-6 扩展 JPA 接口实现

public class ExpandJpaRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID> implements ExpandJpaRepository<T,ID> {
    private final EntityManager entityManager;
    private final JpaEntityInformation<T, ?> entityInformation;
    public ExpandJpaRepositoryImpl(JpaEntityInformation<T, ?> entityInformation,
EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
        this.entityInformation = entityInformation;
    }
    @Override
    public T findOne(String condition, Object... values) {
        if(isEmpty(condition)){
            throw new NullPointerException("条件不能为空




!");
        }
        T result = null;
        try {
            result = (T) createQuery(condition, values).getSingleResult();
        } catch (NoResultException e) {
            e.printStackTrace();
        }
        return result;
    }
    @Override
    public List<T> findAll(Iterable<Predicate> predicates, Operator operator) {
        return new JpqlQueryHolder(predicates,operator).createQuery().getResult
List();
    }
    @Override
    public List<T> findAll(Iterable<Predicate> predicates, Operator operator, Sort sort) {
        return new JpqlQueryHolder(predicates,operator,sort).createQuery().getResult
List();
    }
    @Override
    public Page<T> findAll(Iterable<Predicate> predicates, Operator operator, Pageable
pageable) {
        if(pageable==null){
            return new PageImpl<T>((List<T>) findAll(predicates,operator));
        }
        Long total = count(predicates,operator);
        Query query = new JpqlQueryHolder(predicates,operator,pageable.getSort()).
createQuery();
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        List<T> content = total > pageable.getOffset() ? query.getResultList() :
Collections.<T> emptyList();
        return new PageImpl<T>(content, pageable, total);
    }
    ......
}

因为自定义的接口继承于 JpaRepository,所以不但具有自定义的一些功能,而且拥有 JPA 原来的所有功能,它的继承关系如图 4-3 所示。

图 4-3 扩展 JPA 接口的继承关系

4.2.2 装配自定义的扩展接口

自定义的接口必须在程序启动时装配,才能正常使用。首先,创建一个装配类 ExpandJpaRepository-FactoryBean,继承于 JpaRepositoryFactory-Bean,用来加载自定义的扩展接口,如代码清单 4-7 所示。其中 getTargetRepository 返回自定义的接口实现:ExpandJpaRepositoryImpl。

代码清单 4-7 JPA 扩展接口装配类

public class ExpandJpaRepositoryFactoryBean<R extends JpaRepository<T, ID>, T, ID extends Serializable>
        extends JpaRepositoryFactoryBean<R, T, ID> {
    protected RepositoryFactorySupport createRepositoryFactory(
            EntityManager entityManager) {
        return new ExpandJpaRepositoryFactory<T, ID>(entityManager);
    }
    private static class ExpandJpaRepositoryFactory<T, ID extends Serializable>
            extends JpaRepositoryFactory {
        private final EntityManager entityManager;
        public ExpandJpaRepositoryFactory(EntityManager entityManager) {
            super(entityManager);
            this.entityManager = entityManager;
        }
        protected Object getTargetRepository(RepositoryMetadata metadata) {
            JpaEntityInformation<T, Serializable> entityInformation = (JpaEntity
Information<T, Serializable>) getEntityInformation(metadata.getDomainType());
            return new ExpandJpaRepositoryImpl<T, ID>(entityInformation, entity
Manager);
        }
        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
            return ExpandJpaRepositoryImpl.class;
        }
    }
}

然后,在 JPA 配置类中,通过 @EnableJpaRepositories 加载定义的装配类 ExpandJpa-RepositoryFactoryBean,如代码清单 4-8 所示。其中,“com.**.repository”为定义接口的资源库路径,“com.**.entity”为实体模型的路径。

代码清单 4-8 JPA 配置类

@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@EnableJpaRepositories(basePackages = "com.**.repository",repositoryFactoryBeanClass = ExpandJpaRepositoryFactoryBean.class)
@EntityScan(basePackages = "com.**.entity")
public class JpaConfiguration {
    @Bean
    PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor(){
        return new PersistenceExceptionTranslationPostProcessor();
    }
}

4.2.3 使用扩展接口

现在来做实体的持久化,这样就可以直接使用自定义的扩展接口了。如代码清单 4-9 所示,资源库接口 UserRepository 继承的就是前面定义的接口 ExpandJpaRepository。

代码清单 4-9 使用扩展接口做持久化

@Repository
public interface UserRepository extends ExpandJpaRepository<User, Long> {
    @Query("select t from User t where t.name =?1 and t.email =?2")
    User findByNameAndEmail(String name, String email);
    @Query("select t from User t where t.name like :name")
    Page<User> findByName(@Param("name") String name, Pageable pageRequest);
}

使用 JPA 扩展接口与使用原来的 JPA 接口一样,调用方法基本相同,只不过有些方法被赋予更为丰富的功能,可以更加灵活地使用。代码清单 4-10 是一个使用扩展接口的分页查询,使用 PredicateBuilder 来构造一个查询参数的对象,它可以包含更多的查询参数。

代码清单 4-10 使用扩展 JPA 接口的分页查询

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    public Page<User> findPage(UserQo userQo){
       Pageable pageable = new PageRequest(userQo.getPage(), userQo.getSize(),
new Sort(Sort.Direction.ASC, "id"));
        PredicateBuilder pb  = new PredicateBuilder();
        if (!StringUtils.isEmpty(userQo.getName())) {
            pb.add("name","%" + userQo.getName() + "%", LinkEnum.LIKE);
        }
        if (!StringUtils.isEmpty(userQo.getCreatedateStart())) {
            pb.add("createdate",userQo.getCreatedateStart(), LinkEnum.GE);
        }
        if (!StringUtils.isEmpty(userQo.getCreatedateEnd())) {
            pb.add("createdate",userQo.getCreatedateEnd(), LinkEnum.LE);
        }
        return userRepository.findAll(pb.build(), Operator.AND, pageable);
    }
}

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。