Spring Data JPA雖然大大的簡化了持久層的開發,但是在實際開發中,很多地方都需要高級動態查詢,在實現動態查詢時我們需要用到Criteria API,主要是以下三個:
1、Criteria 查詢是以元模型的概念為基礎的,元模型是為具體持久化單元的受管實體定義的,這些實體可以是實體類,嵌入類或者映射的父類。
2、CriteriaQuery接口:代表一個specific的頂層查詢對象,它包含着查詢的各個部分,比如:select 、from、where、group by、order by等注意:CriteriaQuery對象只對實體類型或嵌入式類型的Criteria查詢起作用
3、Root接口:代表Criteria查詢的根對象,Criteria查詢的查詢根定義了實體類型,能為將來導航獲得想要的結果,它與SQL查詢中的FROM子句類似
在JPA中,標准查詢是以元模型的概念為基礎的.元模型是為具體持久化單元的受管實體定義的.這些實體可以是實體類,嵌入類或者映射的父類.提供受管實體元信息的類就是元模型類。使用元模型類最大的優勢是憑借其實例化可以在編譯時訪問實體的持久屬性.該特性使得criteria 查詢更加類型安全。例如:Product實體類對應的元模型Product_:
package powerx.io.bean; import javax.annotation.Generated; import javax.persistence.metamodel.SingularAttribute; import javax.persistence.metamodel.StaticMetamodel; @Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor") @StaticMetamodel(Product.class) public abstract class Product_ { public static volatile SingularAttribute<Product, Double> price; public static volatile SingularAttribute<Product, String> name; public static volatile SingularAttribute<Product, Integer> id; }
這樣的元模型不用手動創建,有多種構建方式,本人使用的IDE是STS,具體操作如下:選中項目——鼠標右鍵Properties進入配置界面——java compiler下Annotation Processing——勾選enable project specific settings——Annotation Processing下Factory path——add external JARs ——加入事先下載好的hibernate-jpamodelgen-5.2.10.Final.jar——apply后則會在項目根目錄下生成.apt_generated包,包內含有對應的實體類元模型(看不到的同學需要把resource過濾去掉),還有其它的方式,可參考http://docs.jboss.org/hibernate/jpamodelgen/1.0/reference/en-US/html/chapter-usage.html。
使用criteria 查詢簡單Demo
@Resource private EntityManager entityManager; public List<Product> findByConditions(String name, Double price) { //創建CriteriaBuilder安全查詢工廠 //CriteriaBuilder是一個工廠對象,安全查詢的開始.用於構建JPA安全查詢. CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); //創建CriteriaQuery安全查詢主語句 //CriteriaQuery對象必須在實體類型或嵌入式類型上的Criteria 查詢上起作用。 CriteriaQuery<Product> query = criteriaBuilder.createQuery(Product.class); //Root 定義查詢的From子句中能出現的類型 Root<Product> ProductRoot = query.from(Product.class); //Predicate 過濾條件 構建where字句可能的各種條件 //這里用List存放多種查詢條件,實現動態查詢 List<Predicate> predicatesList = new ArrayList<>(); //name模糊查詢 ,like語句 if (name != null) { predicatesList.add( criteriaBuilder.and( criteriaBuilder.like( ProductRoot.get(Product_.name), "%" + name + "%"))); } // ProductPrice 小於等於 <= 語句 if (price != null) { predicatesList.add( criteriaBuilder.and( criteriaBuilder.le( ProductRoot.get(Product_.price), price))); } query.where(predicatesList.toArray(new Predicate[predicatesList.size()])); TypedQuery<Product> typedQuery = entityManager.createQuery(query); List<Product> resultList = typedQuery.getResultList(); return resultList; }
criteriaBuilder中各方法對應的語句:
equle : filed = value
gt / greaterThan : filed > value
lt / lessThan : filed < value
ge / greaterThanOrEqualTo : filed >= value
le / lessThanOrEqualTo: filed <= value
notEqule : filed != value
like : filed like value
notLike : filed not like value
如果每個動態查詢的地方都這么寫,那就感覺太麻煩了。那實際上,在使用Spring Data JPA的時候,只要我們的Repo層接口繼承JpaSpecificationExecutor接口就可以使用Specification進行動態查詢,在這里因為findAll(Specification<T> var1, Pageable var2)
方法中參數 Specification<T>
是一個匿名內部類
那這里就可以直接用lambda表達式直接簡化代碼。
持久層接口類:
package powerx.io; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import powerx.io.bean.Product; public interface ProductDao extends JpaRepository<Product, Integer>,JpaSpecificationExecutor<Product> { }
service層:
@Autowired private ProductDao productDao; public List<Product> findByConditions2(String name, Double price) { return productDao.findAll((root, criteriaQuery, criteriaBuilder) -> { List<Predicate> predicatesList = new ArrayList<>(); if (name != null) { predicatesList.add( criteriaBuilder.and( criteriaBuilder.like( root.get(Product_.name), "%" + name + "%"))); } // itemPrice 小於等於 <= 語句 if (price != null) { predicatesList.add( criteriaBuilder.and( criteriaBuilder.le( root.get(Product_.price), price))); } return criteriaBuilder.and( predicatesList.toArray(new Predicate[predicatesList.size()])); }); }
控制器類:
package powerx.io; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ProductController { @Autowired private ProductServiceImpl productServiceImpl; @GetMapping("/find") public Object find(@RequestParam String name ,@RequestParam Double price) { return productServiceImpl.findByConditions2(name, price); } }
實體類:
package powerx.io.bean; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Product implements Serializable{ private static final long serialVersionUID = 5800591455207338888L; @Id @GeneratedValue private Integer id; private String name; private Double price; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } }
使用springboot構建項目,pom.xml 和application.yml如下
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 連接mysql數據庫驅動 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- 整合jpa --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
spring: datasource: url: jdbc:mysql://localhost:3306/springdatajpa username: root password: root jpa: show-sql: true hibernate: ddl-auto: update
啟動項目,會根據實體類自動創建對應的表,加入測試數據,訪問http://localhost:8080/find?name=apple&price=88,結果如下:
參考文章:https://www.jianshu.com/p/0939cec7e207