JPA多條件復(fù)雜SQL動(dòng)態(tài)分頁查詢功能
概述
ORM映射為我們帶來便利的同時(shí),也失去了較大靈活性,如果SQL較復(fù)雜,要進(jìn)行動(dòng)態(tài)查詢,那必定是一件頭疼的事情(也可能是lz還沒發(fā)現(xiàn)好的方法),記錄下自己用的三種復(fù)雜查詢方式。
環(huán)境
springBoot
IDEA2017.3.4
JDK8
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.xmlxy</groupId> <artifactId>seasgame</artifactId> <version>0.0.1-SNAPSHOT</version> <name>seasgame</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--數(shù)據(jù)庫連接--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- 熱啟動(dòng)等 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <!--Java bean 實(shí)體--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!--swagger2 API 測試工具 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!--安全框架認(rèn)證--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.2.2</version> <classifier>jdk15</classifier> </dependency> <!--漢字轉(zhuǎn)拼音--> <dependency> <groupId>com.belerweb</groupId> <artifactId>pinyin4j</artifactId> <version>2.5.1</version> </dependency> <!-- thymeleaf模板 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> 移除嵌入式tomcat插件 <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> </dependencies> <packaging>war</packaging> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> <finalName>seasgame</finalName> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <encoding>${project.build.sourceEncoding}</encoding> <source>1.7</source> <target>1.7</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <testFailureIgnore>true</testFailureIgnore> </configuration> </plugin> </plugins> </pluginManagement> </build> </project>
@Query
當(dāng)一個(gè)SQL較為復(fù)雜時(shí),第一個(gè)想到的就是原生的SQL語句。如果只是簡單的查詢,那情況還沒這么糟糕
@Query(value = " SELECT IFNULL(sum(right_num),0) sumRight FROM t_record WHERE record_owner_id = ?1 AND responder_no = ?2 ",nativeQuery = true) Map<String,Object> sumRightNum(int studentId,int responderNo);
但如果需要進(jìn)行動(dòng)態(tài)查詢,或更改,那這個(gè)value就變得復(fù)雜了。
package com.xmlxy.seasgame.dao; import com.xmlxy.seasgame.entity.ScoreEntity; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * * Description: * @author hwc * @date 2019/9/5 * @return */ public interface ScoreDao extends CrudRepository<ScoreEntity,Integer> { /** * * Description: *@param scoreEntity * @author hwc * @date 2019/9/6 */ @Transactional(rollbackFor = Exception.class) @Modifying @Query(value = "UPDATE t_score t SET " + "t.responder_no = CASE WHEN :#{#scoreEntity.responderNo} IS NULL THEN t.responder_no ELSE :#{#scoreEntity.responderNo} END," + "t.max_level = CASE WHEN :#{#scoreEntity.maxLevel} IS NULL THEN t.max_level ELSE :#{#scoreEntity.maxLevel} END," + "t.right_num = CASE WHEN :#{#scoreEntity.rightNum} IS NULL THEN t.right_num ELSE :#{#scoreEntity.rightNum} END," + "t.use_time = CASE WHEN :#{#scoreEntity.userTime} IS NULL THEN t.use_time ELSE :#{#scoreEntity.userTime} END WHERE student_id = :#{#scoreEntity.getStudentId()}",nativeQuery = true) void updateScore(@Param("scoreEntity") ScoreEntity scoreEntity); }
JPQL
如果Java代碼內(nèi)發(fā)出JPQL查詢,就需要利用到EntityManager的響應(yīng)方法了。一般執(zhí)行以下流程
獲取一個(gè)EntityManager實(shí)例
調(diào)用實(shí)例的方法createQuery,創(chuàng)建一個(gè)Query實(shí)例,如果有需要可以指定檢索的最大數(shù)量和起始位置
使用Query方法getResultList執(zhí)行查詢,當(dāng)然更新和刪除操作得使用executeUpdate執(zhí)行
進(jìn)行一個(gè)復(fù)雜的動(dòng)態(tài)SQL查詢
public Page<RankEntity> getScoreByRank(int gradeId,int classId,Pageable pageable) { StringBuilder countSelectSql = new StringBuilder(""); countSelectSql.append(" SELECT COUNT(*) "); countSelectSql.append(" FROM "); countSelectSql.append(" t_score s, "); countSelectSql.append(" t_student st "); countSelectSql.append(" WHERE "); countSelectSql.append(" s.student_id = st.student_id "); StringBuilder selectSql = new StringBuilder(); selectSql.append(" SELECT s.student_id,st.real_name,st.student_class,s.max_level,s.use_time,s.right_num "); selectSql.append(" FROM t_score s "); selectSql.append(" JOIN t_student st ON s.student_id = st.student_id "); selectSql.append(" WHERE 1 = 1 "); Map<String,Object> params = new HashMap<>(); StringBuilder whereSql = new StringBuilder(); if (gradeId != -1) { whereSql.append(" AND st.student_grade = :student_grade "); params.put("student_grade",gradeId); } /**班級ID*/ if (classId != -1) { whereSql.append(" AND st.student_class = :classId "); params.put("classId",classId); } String orderSql = " ORDER BY s.max_level DESC,s.use_time,s.right_num ASC "; String countSql = new StringBuilder().append(countSelectSql).append(whereSql).toString(); Query countQuery = entityManager.createNativeQuery(countSql); for (Map.Entry<String,Object> entry : params.entrySet()) { countQuery.setParameter(entry.getKey(),entry.getValue()); } BigInteger totalCount = (BigInteger)countQuery.getSingleResult(); String querySql = new StringBuilder().append(selectSql).append(whereSql).append(orderSql).toString(); Query query = entityManager.createNativeQuery(querySql,RankEntity.class); for (Map.Entry<String,Object> entry:params.entrySet()) { query.setParameter(entry.getKey(),entry.getValue()); } query.setFirstResult((int) pageable.getOffset()); query.setMaxResults(pageable.getPageSize()); List<RankEntity> rankEntities = query.getResultList(); Page<RankEntity> page = new PageImpl<>(rankEntities,pageable,totalCount.longValue()); return page; }
注意:如果沒有重新定義Pageable那么pageNumber必須減1,因?yàn)槭菑?開始的。
Criteria
這是一種規(guī)范查詢是以元模型的概念為基礎(chǔ)的,這個(gè)元模型可以是實(shí)體累,嵌入類,或者映射的父類,簡單介紹幾個(gè)里面用到接口。
CriteraQuery是一個(gè)特定的頂層查詢對象,里面包含select,from,where,order by等各個(gè)部分,然而他只對實(shí)體類或嵌入類的標(biāo)準(zhǔn)查詢起作用。
Root標(biāo)準(zhǔn)查詢的根對象,根定義了實(shí)體類型,是你想要查詢要獲得的結(jié)果,也可以添加查詢條件,結(jié)合實(shí)體管理對象得到查詢的對象。
CriteriaBuilder接口用來構(gòu)建CritiaQuery的構(gòu)建器
StudentEntity類
package com.xmlxy.seasgame.entity; import io.swagger.annotations.ApiModel; import lombok.Data; import javax.persistence.*; import javax.print.attribute.standard.MediaSize; import java.io.Serializable; /** * * Description:學(xué)生對象 * @param * @author hwc * @date 2019/8/8 */ @Entity @Table(name = "t_base_student") @ApiModel @Data public class StudentEntity implements Serializable { private static final long serialVersionUID = 546L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "student_id") private Integer studentId; @Column(name = "student_grade") private Integer studentGrade; @Column(name = "student_class") private Integer studentClass; @Column(name = "address") private String address; @Column(name = "telephone") private Integer telephone; @Column(name = "real_name") private String realName; @Column(name = "id_number") private String idNumber; @Column(name = "study_id") private String studyId; @Column(name = "is_delete") private int isDelete; @Column(name = "uuid") private String uuid; }
dao層
public interface StudentDao extends JpaRepository<StudentEntity,Integer>,JpaSpecificationExecutor { }
動(dòng)態(tài)查詢
public Page<StudentEntity> getTeacherClassStudent(int pageNumber,int pageSize,int gradeId, int classId,String keyword) { pageNumber = pageNumber < 0 ? 0 : pageNumber; pageSize = pageSize < 0 ? 10 : pageSize; Specification<StudentEntity> specification = new Specification<StudentEntity>() { @Override public Predicate toPredicate(Root<StudentEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { //page : 0 開始, limit : 默認(rèn)為 10 List<Predicate> predicates = new ArrayList<>(); predicates.add(criteriaBuilder.equal(root.get("studentGrade"),gradeId)); predicates.add(criteriaBuilder.equal(root.get("studentClass"),classId)); if (!Constant.isEmptyString(keyword)) { predicates.add(criteriaBuilder.like(root.get("realName").as(String.class),"%" + keyword + "%")); } return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()])); } }; /*studentId必須是實(shí)體類屬性與數(shù)據(jù)庫對應(yīng),否則報(bào)ropertyReferenceException異常*/ PageRequest page = new PageRequest(pageNumber,pageSize,Sort.Direction.ASC,"studentId"); Page<StudentEntity> pages = studentDao.findAll(specification,page); return pages; }
因?yàn)檫@個(gè)項(xiàng)目應(yīng)用比較簡單,所以條件只有一個(gè),如果條件較多,甚至可以定義一個(gè)專門的類去接收拼接參數(shù),然后判
斷,成立就add進(jìn)去。
總結(jié)
以上所述是小編給大家介紹的JPA多條件復(fù)雜SQL動(dòng)態(tài)分頁查詢功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉(zhuǎn)載,煩請注明出處,謝謝!
相關(guān)文章
java jackson 將對象轉(zhuǎn)json時(shí),忽略子對象的某個(gè)屬性操作
這篇文章主要介紹了java jackson 將對象轉(zhuǎn)json時(shí),忽略子對象的某個(gè)屬性操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10gRPC在Java中的實(shí)現(xiàn)與應(yīng)用詳解
gRPC是由Google開發(fā)的高性能、開源的通用遠(yuǎn)程過程調(diào)用(RPC)框架,本文將詳細(xì)介紹如何在Java中使用gRPC,包括服務(wù)定義、服務(wù)器端實(shí)現(xiàn)、客戶端調(diào)用以及一些高級特性,我們將通過代碼示例來幫助理解gRPC的工作原理,需要的朋友可以參考下2024-06-06Java線程中sleep和wait的區(qū)別詳細(xì)介紹
Java中的多線程是一種搶占式的機(jī)制,而不是分時(shí)機(jī)制。搶占式的機(jī)制是有多個(gè)線程處于可運(yùn)行狀態(tài),但是只有一個(gè)線程在運(yùn)行2012-11-11springMVC中基于token防止表單重復(fù)提交方法
本篇文章主要介紹了springMVC中基于token防止表單重復(fù)提交方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07MySQL text類型對應(yīng)mybatis jdbcType類型方式
這篇文章主要介紹了MySQL text類型對應(yīng)mybatis jdbcType類型方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07Java使用自定義注解+反射實(shí)現(xiàn)字典轉(zhuǎn)換代碼實(shí)例
這篇文章主要介紹了Java使用自定義注解+反射實(shí)現(xiàn)字典轉(zhuǎn)換代碼實(shí)例,注解是一種能被添加到j(luò)ava代碼中的元數(shù)據(jù),類、方法、變量、參數(shù)和包都可以用注解來修飾,注解對于它所修飾的代碼并沒有直接的影響,需要的朋友可以參考下2023-09-09