Spring Data JPA專案實戰(下)
一 專案說明
結合Specification和自定義Repository實現來定製一個自動模糊查詢。即對於任意實體型別進行查詢,物件裡有幾個值就查幾個值,當值為字元型時,就通過like查詢,其餘的型別使用自動等於查詢,沒有值就查詢全部。
二 實戰
1 定義Specification
package com.wisely.specs;
import static com.google.common.collect.Iterables.toArray;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
public class CustomerSpecs {
//定義一個返回值為Specification的方法byAuto,這裡使用的是泛型T,所以這個Specification是可以用於任意的實體類
//它接受的引數是entityManager和當前包含值作為查詢條件的實體類物件
public static <T> Specification<T> byAuto(final EntityManager entityManager, final T example) {
final Class<T> type = (Class<T>) example.getClass();//當前實體類物件的型別
return new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<>(); //新建Predicate列表儲存構造的查詢條件
//獲得實體類的EntityType,可以從中獲得實體類的屬性
EntityType<T> entity = entityManager.getMetamodel().entity(type);
for (Attribute<T, ?> attr : entity.getDeclaredAttributes()) {//對實體類的所有屬性做迴圈
Object attrValue = getValue(example, attr); //獲得實體類物件某個屬性的值
if (attrValue != null) {
if (attr.getJavaType() == String.class) { //當前屬性值為字串的時候
if (!StringUtils.isEmpty(attrValue)) { //字串不為空
predicates.add(cb.like(root.get(attribute(entity, attr.getName(), String.class)),
pattern((String) attrValue))); //構造當前屬性like屬性值查詢條件,並新增到條件列表中
}
} else {
predicates.add(cb.equal(root.get(attribute(entity, attr.getName(), attrValue.getClass())),
attrValue)); //構造屬性和屬性值equal查詢條件,並新增到條件列表中
}
}
}
//將條件列表轉換成Predicate
return predicates.isEmpty() ? cb.conjunction() : cb.and(toArray(predicates, Predicate.class));
}
/**
* 通過反射獲得實體物件對應屬性的屬性值
*/
private <T> Object getValue(T example, Attribute<T, ?> attr) {
return ReflectionUtils.getField((Field) attr.getJavaMember(), example);
}
/**
* 獲得實體類的當前屬性的SingularAttribute,SingularAttribute包含的是實體類的某個單獨屬性。
*/
private <E, T> SingularAttribute<T, E> attribute(EntityType<T> entity, String fieldName,
Class<E> fieldClass) {
return entity.getDeclaredSingularAttribute(fieldName, fieldClass);
}
};
}
/**
* 構造like查詢條件模式,即前後新增%
*/
static private String pattern(String str) {
return "%" + str + "%";
}
}
2 定義介面
//此例中介面繼承了JpaRepository,具備了JpaRepository所提供的方法,繼承了JpaSpecificationExecutor,讓我們具備使用Specification的能力。
package com.wisely.support;
import java.io.Serializable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;
@NoRepositoryBean
public interface CustomRepository<T, ID extends Serializable>extends JpaRepository<T, ID> ,JpaSpecificationExecutor<T>{
Page<T> findByAuto(T example,Pageable pageable);
}
3 定義CustomRepositoryImpl實現
package com.wisely.support;
import java.io.Serializable;
import javax.persistence.EntityManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import static com.wisely.specs.CustomerSpecs.*;
//此類繼承JpaRepository的實現類SimpleJpaRepository,讓我們可以使用SimpleJpaRepository的方法
//此類當然還要實現我們自定義的介面CustomRepository
public class CustomRepositoryImpl <T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements CustomRepository<T,ID> {
private final EntityManager entityManager;
public CustomRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager);
this.entityManager = entityManager;
}
//findByAuto方法使用byAuto Specification構造的條件查詢,並提供分頁功能
@Override
public Page<T> findByAuto(T example, Pageable pageable) {
return findAll(byAuto(entityManager, example),pageable);
}
}
4 定義repositoryFactoryBean
package com.wisely.support;
import java.io.Serializable;
import javax.persistence.EntityManager;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
//自定義CustomRepositoryFactoryBean,繼承JpaRepositoryFactoryBean
public class CustomRepositoryFactoryBean<T extends JpaRepository<S, ID>, S, ID extends Serializable>
extends JpaRepositoryFactoryBean<T, S, ID> {
@Override
//重寫createRepositoryFactory方法,用當前的CustomRepositoryFactory建立例項
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new CustomRepositoryFactory(entityManager); //該類見下面的定義
}
//定義一個私有的靜態類,並繼承JpaRepositoryFactory
private static class CustomRepositoryFactory extends JpaRepositoryFactory {
//建構函式
public CustomRepositoryFactory(EntityManager entityManager) {
super(entityManager);
}
@Override
@SuppressWarnings({"unchecked"})
protected <T, ID extends Serializable> SimpleJpaRepository<?, ?> getTargetRepository(
RepositoryInformation information, EntityManager entityManager) {// 獲得當前自定義類的實現
return new CustomRepositoryImpl<T, ID>((Class<T>) information.getDomainType(), entityManager);
}
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {// 獲得當前自定義類的型別
return CustomRepositoryImpl.class;
}
}
}
5 定義PersonRepository
package com.wisely.dao;
import java.util.List;
import com.wisely.support.CustomRepository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import com.wisely.domain.Person;
//只需PersonRepository繼承我們自定義的Repository介面,即可使我們在自定義PersonRepository實現功能
public interface PersonRepository extends CustomRepository<Person, Long> {
List<Person> findByAddress(String address); //使用方法名查詢,接收一個name引數,返回值為列表
Person findByNameAndAddress(String name,String address); //使用方法名查詢,接受name和address,返回值為單個物件
//使用@Query查詢,引數按照名稱繫結
@Query("select p from Person p where p.name= :name and p.address= :address")
Person withNameAndAddressQuery(@Param("name")String name,@Param("address")String address);
//使用@NamedQuery查詢,該查詢在實體類中有定義
Person withNameAndAddressNamedQuery(String name,String address);
}
6 控制器
package com.wisely.web;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.wisely.dao.PersonRepository;
import com.wisely.domain.Person;
@RestController
public class DataController {
// Spring Data JPA已自動為你註冊bean,所以可自動注入
@Autowired
PersonRepository personRepository;
//控制器接受一個Person物件,當Person的name有值時,會自動對name進行like查詢;當age有值時,會進行等於查詢
//當Person中有多個值不為空時,會自動構造多個查詢條件;當Person所有值為空時,預設查詢出所有記錄
@RequestMapping("/auto")
public Page<Person> auto(Person person){
Page<Person> pagePeople = personRepository.findByAuto(person, new PageRequest(0, 10));
return pagePeople;
}
}
7 主類
package com.wisely;
import com.wisely.domain.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import com.wisely.dao.PersonRepository;
import com.wisely.support.CustomRepositoryFactoryBean;
import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import java.util.List;
@SpringBootApplication
//該註解指定repositoryFactoryBeanClass,並讓自定義的Repsitory實現生效。
@EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)
public class Ch82Application {
@Autowired
PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(Ch82Application.class, args);
}
}
三 執行
1 輸入http://localhost:8080/auto
2 輸入http://localhost:8080/auto?address=肥,構造address的like查詢
3 輸入http://localhost:8080/auto?address=肥&name=雲&age=32,構造address的like查詢,name的like查詢以及age的equal查詢
相關文章
- Spring Data JpaSpring
- Spring Data JPA之Spring Data JPA快速入門(三)Spring
- Spring Data Jpa APISpringAPI
- Spring Data JPA系列2:SpringBoot整合JPA詳細教程,快速在專案中熟練使用JPASpring Boot
- Spring Boot:整合Spring Data JPASpring Boot
- Spring Data JPA系列3:JPA專案中核心場景與進階用法介紹Spring
- Spring Data JPA中TransactionInterceptorSpring
- Spring Data JPA中ConfigurableTransactionManagerSpring
- spring data jpa查詢Spring
- SpringBoot整合Spring Data JPASpring Boot
- Spring Data JPA:解析CriteriaQuerySpring
- Spring Data JPA:解析SimpleJpaRepositorySpring
- Spring Data JPA:解析CriteriaBuilderSpringUI
- Spring Data JPA 的使用Spring
- Spring Data JPA 實現聯表查詢Spring
- Spring Data JPA原始碼案例Spring原始碼
- Spring Data JPA REST Query CriteriaSpringREST
- Spring Data JPA簡單使用Spring
- Spring Data JPA 實現多表關聯查詢Spring
- Spring Data JPA中事務ReactiveTransactionManagerSpringReact
- Spring Data JPA 參考文件三Spring
- 一文搞定 Spring Data JPASpring
- Spring Boot (五)Spring Data JPA 操作 MySQL 8Spring BootMySql
- 【Spring技術棧】初識Spring Data JPASpring
- Spring Data JPA中實現更新插入三種方法Spring
- Spring Data JPA的簡單入門Spring
- @Query註解的用法(Spring Data JPA)Spring
- 使用Spring Data JPA實現DDD聚合的動態投影Spring
- Spring Data JPA框架的Repository自定義實現詳解Spring框架
- SpringMVC+Spring Data JPA實現增刪改查操作SpringMVC
- Spring Data JPA + QueryDSL實現CRUD和複雜查詢案例Spring
- Spring Data JPA中事務超時TransactionTimedOutExceptionSpringException
- SpringBoot第九篇:整合Spring Data JPASpring Boot
- 使用Spring Data Jpa遇到問題彙總Spring
- 提高Spring Data JPA應用程式的效能Spring
- Spring Data JPA(二):SpringBoot整合H2Spring Boot
- Spring Boot整合Spring Data JPA進行資料庫操作Spring Boot資料庫
- Spring Data JPA系列5:讓IDEA自動幫你寫JPA實體定義程式碼SpringIdea