Spring Data JPA雖然大大的簡化了持久層的開發,可是在實際開發中,不少地方都須要高級動態查詢,在實現動態查詢時咱們須要用到Criteria API,主要是如下三個:html
一、Criteria 查詢是以元模型的概念爲基礎的,元模型是爲具體持久化單元的受管實體定義的,這些實體能夠是實體類,嵌入類或者映射的父類。java
二、CriteriaQuery接口:表明一個specific的頂層查詢對象,它包含着查詢的各個部分,好比:select 、from、where、group by、order by等注意:CriteriaQuery對象只對實體類型或嵌入式類型的Criteria查詢起做用mysql
三、Root接口:表明Criteria查詢的根對象,Criteria查詢的查詢根定義了實體類型,能爲未來導航得到想要的結果,它與SQL查詢中的FROM子句相似web
在JPA中,標準查詢是以元模型的概念爲基礎的.元模型是爲具體持久化單元的受管實體定義的.這些實體能夠是實體類,嵌入類或者映射的父類.提供受管實體元信息的類就是元模型類。使用元模型類最大的優點是憑藉其實例化能夠在編譯時訪問實體的持久屬性.該特性使得criteria 查詢更加類型安全。例如:Product實體類對應的元模型Product_:spring
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。sql
使用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 = valuespringboot
gt / greaterThan : filed > valueapp
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