技术标签: 复杂查询 Spring Data JPA 动态查询
Spring Data JPA其诸多优点给我们的工作带来了很多便利,但对于接触不久的同学来说,有些情况让我们头疼。一些复杂的查询,比如涉及到聚合函数、动态多条件等,着实有些棘手。在不够了解的情况下,觉得Spring Data JPA在这方面不太人性化,有时候我们干脆使用原生sql粗暴的来解决这类查询问题。但这与Spring Data JPA的初衷是相悖的,在不断的学习中,慢慢发现Spring Data JPA的冰山一角,不禁感叹于它的强大。现将一些所得,与大家分享。
先看示例的数据表:
一张简单的学生信息表:包含住址、姓名、学校、电话、出生日期、分数等信息。
应用场景:查询某几个学校出生日期在某个范围内的学生信息,按学校分组统计,并计算各学校人数、总分数、第一名姓名及分数,另外还涉及到排序和筛选。
实现方式:
EntityManager:EntityManager是JPA中用于增删改查的接口,它的作用相当于一座桥梁,连接内存中的java对象和数据库的数据存储。可以用getCriteriaBuilder()的方式获取CriteriaBuilder对象。
CriteriaBuilder接口:用于构造标准查询、复合条件、表达式、排序等。可以通过createQuery的方式获取CriteriaQuery实例。
CriteriaQuery接口:代表一个specific的顶层查询对象,它包含着查询的各个部分,比如:select 、from、where、group by、order by。
Root接口:代表Criteria查询的根对象,定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似。
实现代码:
//通过注解@PersistenceContext注入的方式来获得EntityManager对象
@PersistenceContext
private EntityManager entityManager;
public void multiQueryStudent (StudentParam studentParam) { //studentParam:自定义的查询参数体
List<String> schoolList = studentParam.getSchoolList(); //查询条件:学校List
String startDate = studentParam.getStartDate(); //查询条件:出生日期-起
String endDate = studentParam.getEndDate(); //查询条件:出生日期-止
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
//StudentScoreSum指定了查询结果返回至自定义对象
CriteriaQuery<StudentScoreSum> query = cb.createQuery(StudentScoreSum.class);
Root<StudentEntity> root = query.from(StudentEntity.class);
Path<String> schoolPath = root.get("school");
Path<Integer> scorePath = root.get("score");
Path<String> namePath = root.get("name");
Path<String> birthdayPath = root.get("birthday");
//拼接where条件
List<Predicate> predicateList = new ArrayList<Predicate>();
if (schoolList != null && schoolList.size() > 0) {
CriteriaBuilder.In<String> in = cb.in(schoolPath);
for (String school : schoolList) {
in.value(school);
}
predicateList.add(in);
}
if (startDate != null && !"".equals(startDate)) {
predicateList.add(cb.greaterThan(birthdayPath, startDate));
}
if (endDate != null && !"".equals(endDate)) {
predicateList.add(cb.lessThan(birthdayPath, endDate));
}
Predicate[] predicates = new Predicate[predicateList.size()];
predicates = predicateList.toArray(predicates);
//加上where条件
query.where(predicates);
//指定查询项,select后面的东西
query.multiselect(schoolPath, cb.count(root).as(Integer.class), cb.sum(scorePath), namePath, cb.max(scorePath));
//按学校分组
query.groupBy(schoolPath);
//排序
query.orderBy(cb.desc(cb.max(scorePath)));
//筛选第一名成绩大于80分的
query.having(cb.greaterThan(cb.max(scorePath), 80));
TypedQuery<StudentScoreSum> q = entityManager.createQuery(query);
List<StudentScoreSum> result = q.getResultList();
for (StudentScoreSum studentScoreSum : result) {
//打印查询结果
System.out.println(studentScoreSum.toString());
}
}
自定义的查询参数体:
public class StudentParam {
private String name; //姓名
private String address; //住址
private List<String> schoolList; //学校
private String startDate; //出生日期-起
private String endDate; //出生日期-止
//省略get、set方法
}
自定义的查询结果实体类:
public class StudentScoreSum {
private String school;
private Integer count; //学生人数
private Long scoreSum; //总分数
private String name; //第一名姓名
private Integer score; //第一名分数
//省略了set、get方法
public StudentScoreSum(String school, Integer count, Long scoreSum, String name, Integer score) {
this.school = school;
this.count = count;
this.scoreSum = scoreSum;
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "StudentScoreSum{" +
"school='" + school + '\'' +
", count=" + count +
", scoreSum=" + scoreSum +
", name='" + name + '\'' +
", score=" + score +
'}';
}
}
Entity实体类:
@Entity
@Table(name = "t_student")
public class StudentEntity {
@Id
private String id;
private String name;
private String address;
private String tel;
private String school;
private String birthday;
private Integer score;
//省略set、get方法。
}
查询参数赋值及查询接口调用(本人用的Junit测试方法):
@Test
public void multiQueryStudentTest() {
StudentParam studentParam = new StudentParam();
List<String> schoolList = new ArrayList<String>();
schoolList.add("上大附中");
schoolList.add("实验中学");
schoolList.add("山大附中");
schoolList.add("育英中学");
schoolList.add("西城中学");
schoolList.add("青大附中");
studentParam.setSchoolList(schoolList);
studentParam.setStartDate("2001-01-01");
studentParam.setEndDate("2003-01-01");
studentService.multiQueryStudent(studentParam);
}
执行结果如下:
Hibernate:
select
studentent0_.school as col_0_0_,
cast(count(studentent0_.id) as signed) as col_1_0_,
sum(studentent0_.score) as col_2_0_,
studentent0_.name as col_3_0_,
max(studentent0_.score) as col_4_0_
from
t_student studentent0_
where
(
studentent0_.school in (
? , ? , ? , ? , ? , ?
)
)
and studentent0_.birthday>?
and studentent0_.birthday<?
group by
studentent0_.school
having
max(studentent0_.score)>80
order by
max(studentent0_.score) desc
StudentScoreSum{school='青大附中', count=4, scoreSum=286, name='李四', score=99}
StudentScoreSum{school='西城中学', count=2, scoreSum=196, name='郎芬', score=98}
StudentScoreSum{school='山大附中', count=3, scoreSum=254, name='杜吉祥', score=89}
StudentScoreSum{school='育英中学', count=1, scoreSum=89, name='陆大安', score=89}
StudentScoreSum{school='上大附中', count=1, scoreSum=81, name='李天山', score=81}
Process finished with exit code 0
上半部分是Hibernate输出的自动生成的sql语句,自己不需要写一句sql,都是自动生成的,下半部分是查询结果。越了解,越佩服Spring Data JPA的强大,另外,常用的,像:avg()、exists、distinct、between等等,都可以支持。
最后,希望我的分享能给需要的朋友带来一点参考或帮助,不足之处请斧正。
文章浏览阅读1.8k次。LCM支持多种语言,也有很多版本,这里只介绍C++的几个类以及方法。类型简介C++的API,LC提供了三个类以及两个结构体来封装LCM,他们分别是:class lcm::LCMstruct lcm::ReceiveBufferclass lcm::Subscriptionstruct lcm::LogEventclass lcm::LogFile两个struct是配合类来使用的。前..._lcm_eventlog_write_event
文章浏览阅读484次。Oracle安装需要注意的问题及卸载办法,数据库主页无法打开解决方案_ora-12505
文章浏览阅读5.7k次。`#include <stdio.h>#include <math.h>double TriArea(double a,double b,double c);int main(){double a,b,c;scanf("%lf%lf%lf",&a,&b,&c);printf("%lf\n",TriArea(a,b,c));}double TriArea(double a,double b,double c){double p}`@TOC欢迎_1. 已知三角形的三边,求三角形面积,将其编写成一个函数,在主函数中调用验证。 #in
文章浏览阅读1k次。Jetty和Tomcat性能方面差异不大(1)Jetty可以同时处理大量连接而且可以长时间保持连接,适合于web聊天应用等等。(2)Jetty的架构简单,因此作为服务器,Jetty可以按需加载组件,减少不需要的组件,减少了服务器内存开销,从而提高服务器性能。(3)Jetty默认采用NIO(非阻塞IO)结束在处理I/O请求上更占优势,在处理静态资源时,性能较高。Servlet规范支持方面(1)Jet..._server.jetty.connection-idle-timeout
文章浏览阅读1.4k次。anaconda安装nb_conda后,在打开jupyter notebook时报错,如下图:解决方案:卸载nb_conda_kernels后,重新安装,重启jupyter notebook命令如下:conda uninstall nb_conda_kernelsconda install nb_conda_kernelsjupyter notebook..._卸载nb_conda_kernels
文章浏览阅读600次。-- 创建测试库use mastergocreate database prodgouse prodgocreate table table1(ID int primary key ,name varchar(20),weight numeric(10,2));insert into table1 values(1,'union',70.2),(3,'mobil',75.1)go_sqlserver 开启审核
文章浏览阅读5.3k次。随着科技的不断进步,AI技术的发展正影响着世界各个领域。CSDN AI写作助手——InsCode AI创作助手,凭借其独特的对话式AI和高效创作支持,成为了一个值得期待的创新工具。在本文中,我们将探讨这类AI工具的使用体验、技巧以及未来的发展趋势与影响。_csdnai写手
文章浏览阅读1.7k次。目前App推广已经进入白热化状态,APP的推广越来越难,推广的APP渠道也是各APP运营人员关注的焦点,此文章根据笔者多年行业经验,整理而成,所以总结了这37个方法,为行业新人做指导。一、应用商店推广1.手机厂商应用商店:如小米商店,华为应用市场,三星应用商店,联想乐商店,HTC市场,oppo ,魅族市场,乐视应用商店等。渠道部门需要较多运营专员来跟手机厂商商店接触。2.手机运营商应用商店..._pr18社区
文章浏览阅读605次,点赞2次,收藏12次。●此为数据结构与算法实现的练习。0.退出1. 图书的创建和输出2. 新书入库(插入)3. 旧书出库(删除)4. 按书号查找图书5. 按价格升序排序图书6. 按价格区间查找图书7. 根据指定书名,进行图书价格的修改8. 图书价格普调9. 查找最贵图书10. 图书去重【注】:功能 8.图书价格普调的含义为:计算所有图书的平均价格,将所有低于平均价格的图书价格提高20%,所有高于或等于平均价格的图书价格提高10%,最后逐行输出价格修改后的全部图书信息。_线性表链表图书管理
文章浏览阅读2.7k次,点赞3次,收藏11次。博客引用处(以下内容在原有博客基础上进行补充或更改,谢谢这些大牛的博客指导):部署SpringBoot项目到windows server云服务器一、准备环境。首先考虑,你的项目正常运行需要哪些环境。MySQL,Java,Tomcat 这三种应该是大多人配置项目最基本的环境。安装教程,测试是否正确安装,请自行百度。二、SpringBoot项目的两种打包方式1)传统的war方式,将编译..._java+springboot项目部署到windowsserver云服务器
文章浏览阅读463次。近日,2018年新媒体新技术教学应用研讨会在广州落下帷幕,这是一次全国教育教学信息化交流展示活动,邀请国内有着大规模应用实践的教育局、学校专家、学者来分享实际教学经验,交流心得体会,探讨创新应用,展望智慧教育新未来。在本次会议上,IT168记者深入采访到了苏州星洲学校信息与总务处主任顾峰、华中师大一附中教研组组长孙俊峰、汕头市澄海广益小学副校长王楚亮以及锐捷网络云桌面产品经理袁野,并与上述专家共同..._教育信息化2.0下学生的学习方式
文章浏览阅读3.1k次。共享内存实际上是可受用户控制的一级缓存。每个SM中的一级缓存与共享内存共享一个64KB的内存段在开普勒架构的设备中,根据应用程序的需要,每个线程块可以配置为16KB的一级缓存或共享内存。而在费米架构的设备中,可以根据喜好选择16KB或者48KB的一级缓存或者共享内存。早期费米架构中只有固定的16KB共享内存而没有一级缓存。共享内存的延迟极低,大约有1.5TB/s的带宽,远远高于全局内存的190GB_cuda 向量点乘