- 前言
- 第一部分 基础应用开发
- 第 1 章 Spring Boot 入门
- 第 2 章 在 Spring Boot 中使用数据库
- 第 3 章 Spring Boot 界面设计
- 第 4 章 提高数据库访问性能
- 第 5 章 Spring Boot 安全设计
- 第二部分 分布式应用开发
- 第 6 章 Spring Boot SSO
- 第 7 章 使用分布式文件系统
- 第 8 章 云应用开发
- 第 9 章 构建高性能的服务平台
- 第三部分 核心技术源代码分析
- 第 10 章 Spring Boot 自动配置实现原理
- 第 11 章 Spring Boot 数据访问实现原理
- 第 12 章 微服务核心技术实现原理
- 附录 A 安装 Neo4j
- 附录 B 安装 MongoDB
- 附录 C 安装 Redis
- 附录 D 安装 RabbitMQ
- 结束语
4.2 扩展 JPA 功能
使用 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); } }
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论