概述

  ORM映射为我们带来便利的同时,也失去了较大灵活性,如果SQL较复杂,要进行动态查询,那必定是一件头疼的事情(也可能是lz还没发现好的方法),记录下自己用的三种复杂查询方式。

环境

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> <!--数据库连接-->
<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> <!-- 热启动等 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency> <!--Java bean 实体-->
<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>
<!--安全框架认证-->
<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>
<!--汉字转拼音-->
<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

当一个SQL较为复杂时,第一个想到的就是原生的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);

但如果需要进行动态查询,或更改,那这个value就变得复杂了。

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代码内发出JPQL查询,就需要利用到EntityManager的响应方法了。一般执行以下流程

  1. 获取一个EntityManager实例
  2. 调用实例的方法createQuery,创建一个Query实例,如果有需要可以指定检索的最大数量和起始位置
  3. 使用Query方法getResultList执行查询,当然更新和删除操作得使用executeUpdate执行

进行一个复杂的动态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,因为是从0开始的。

Criteria

这是一种规范查询是以元模型的概念为基础的,这个元模型可以是实体累,嵌入类,或者映射的父类,简单介绍几个里面用到接口。

CriteraQuery是一个特定的顶层查询对象,里面包含select,from,where,order by等各个部分,然而他只对实体类或嵌入类的标准查询起作用。

Root标准查询的根对象,根定义了实体类型,是你想要查询要获得的结果,也可以添加查询条件,结合实体管理对象得到查询的对象。

CriteriaBuilder接口用来构建CritiaQuery的构建器

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:学生对象
* @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
{
}

动态查询

    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 : 默认为 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必须是实体类属性与数据库对应,否则报ropertyReferenceException异常*/
PageRequest page = new PageRequest(pageNumber,pageSize,Sort.Direction.ASC,"studentId");
Page<StudentEntity> pages = studentDao.findAll(specification,page);
return pages;
}

因为这个项目应用比较简单,所以条件只有一个,如果条件较多,甚至可以定义一个专门的类去接收拼接参数,然后判断,成立就add进去。

转载一篇写得不错的文章:https://blog.csdn.net/u010775025/article/details/80497986

JPA多条件复杂SQL动态分页查询的更多相关文章

  1. SQL 数据分页查询

    最近学习了一下SQL的分页查询,总结了以下几种方法. 首先建立了一个表,随意插入的一些测试数据,表结构和数据如下图: 现在假设我们要做的是每页5条数据,而现在我们要取第三页的数据.(数据太少,就每页5 ...

  2. [.NET] SQL数据分页查询

    [.NET] SQL数据分页查询 程序下载 范例下载:点此下载 原始码下载:点此下载 NuGet封装:点此下载 数据查询 开发系统时,使用C#执行SQL查询指令,就可以从SQL数据库里查询所需数据. ...

  3. Oracle/MySql/SQL Sqlserver分页查询

    简述 简单概括一下Oracle,MySql,SQL Sqlserver这三个数据库的分页查询语句. Oracle分页查询 例:每页显示两条数据,现在要查询第二页,也就是第3-4条数据. 查询语句: s ...

  4. JPA或Hibernate中使用原生SQL实现分页查询、排序

    发生背景:前端展示的数据需要来自A表和D表拼接,A表和D表根据A表的主键进行关联,D表的非主键字段关联C表的主键,根据条件筛选出符合的数据,并且根据A表的主键关联B表的主键(多主键)的条件,过滤A表中 ...

  5. SQL Server分页查询存储过程

    --分页存储过程create PROCEDURE [dbo].[commonPagination]@columns varchar(500), --要显示的列名,用逗号隔开 @tableName va ...

  6. SQL Server分页查询方法整理

    SQL Server数据库分页查询一直是SQL Server的短板,闲来无事,想出几种方法,假设有表ARTICLE,字段ID.YEAR...(其他省略),数据53210条(客户真实数据,量不大),分页 ...

  7. Mybatis+MySQL动态分页查询

    https://blog.csdn.net/qq_34137397/article/details/63289621 mybatis有两种分页方法 1.内存分页,也就是假分页.本质是查出所有的数据然后 ...

  8. SQL Server分页查询进化史

    分页查询一直SQL Server的一个硬伤,就是是经过一些进化,比起MySql的limit还是有一些差距. 一.条件过滤(适应用所有版本) 条件过滤的方法有很多,而思路就是利用集合的差集选择出目标集合 ...

  9. SQL 存储过程 分页查询

    ALTER PROCEDURE [dbo].[gzProc_TablePage] @tablename varchar(MAX),--表名 @selcolumn varchar(MAX),--查询字段 ...

随机推荐

  1. python语言快捷注释

    1.注释单行 (1)方法1:直接在单行代码前边加 # (2)方法2:选中需要注释的代码,Ctrl+/ 即可注释 2.注释多行代码 选中想要注释的N行代码,直接Ctrl+/ 即可注释 3.取消注释多行代 ...

  2. 15. Java异常处理

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  3. Angular JS 中 ng-controller 值复制和引用复制

    我们知道在使用ng-app或者ng-controller指令的时候,都会创建一个新的作用域($rootScope或者是$scope),并且在使用ng-controller指令创建的作用域会继承父级作用 ...

  4. webapck小知识点1

    全局安装webpack webpack-cli npm install webapck webpack-cli -g 卸载全局安装的webpack webpack-cli npm unistall w ...

  5. Mysql无法启动情况下,如何恢复数据?

    本文适用于,mysql无法启动,但数据文件未丢失的情况. Mysql因意外情况,导致无法启动,数据库未做备份的情况下,如何将数据迁移至其他数据库中. 原数据库地址:192.168.1.100(以下简称 ...

  6. 在一个含有1-n的序列中,每次找到第Ki小的数,并把它删除(线段树)

    提交链接 Data structure is one of the basic skills for Computer Science students, which is a particular ...

  7. [译]使用golang每分钟处理百万请求

    [译]使用golang每分钟处理百万请求 在Malwarebytes,我们正在经历惊人的增长,自从我在1年前加入硅谷的这家公司以来,我的主要职责是为多个系统做架构和开发,为这家安全公司的快速发展以及百 ...

  8. springMVC(一) --前端控制器(DispatcherServlet)的作用

        SpringMVC是Spring中的模块,它实现了mvc设计模式的web框架,首先用户发出请求,请求到达SpringMVC的前端控制器(DispatcherServlet),前端控制器根据用户 ...

  9. 天气预报APP(1)

    一个天气预报APP至少应该具备以下功能: *可以罗列出全国所有的省.市.县: *可以查看全国任意城市的天气信息: *可以自由的切换城市,去查看其他城市的天气: *提供手动更新以及后台自动更新天气的功能 ...

  10. 深入理解ES6之——代理和反射(proxy)

    通过调用new proxy()你可以创建一个代理来替代另一个对象(被称为目标),这个代理对目标对象进行了虚拟,因此该代理与该目标对象表面上可以被当做同一个对象来对待. 创建一个简单的代理 当你使用Pr ...