- 前言
- 第一部分 基础应用开发
- 第 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);
}
}
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论