基于SpringDataJpa的mysql动态分页多表查询

由于这篇文章预计篇幅会很长,关于Spring Data JPA的知识就简短的分享,更多的请自行度娘,JPA 封装了很多查询的接口,但今天要讲到的例子需涉及到的技术:①.jpa动态查询,即查询条件不定;②.分页查询,根据传入的页码,和每页的显示行进行分页;③.联表查询:根据需求获得联表查询的结果集;④.获得不相关结果集的笛卡尔积

1.jpa动态分页查询

框架封装的动态分页查询很简单,只需要Dao层继承接口,service层重写jpaSpecificationExecutor的findAll方法,判断查询条件即可,最后返回Page对象,由于后面会提及此对象,这里不做阐述。

@Entity
@Table(name="machine_user_info")
public class MachineGroupInfo extends IdEntity {
private String snCode;
private String userName;
private String telephone;
private String itemName;
private String prov;
private String city;
private String area;
private String address;

get、set方法

}

这里我们写了一个统一的ID管理类继承了它,如果你只有一个类请在id字段使用

@Id

@GeneratedValue

注解标注

service层重写findAll方法:

public Page<MachineGroupInfo> getMachineGroupInfo(Integer page, Integer size, String prov, String city, String area, String itemname,

String username, String tel, String snCode){
Map<String,Object> map=new HashMap<>();
Sort sort=new Sort(Sort.Direction.ASC,"id");
Pageable pageable=new PageRequest(page,size,sort);
Page<MachineGroupInfo> page2=machineGroupInfoRepository.findAll(new Specification<MachineGroupInfo>() {
@Override
public Predicate toPredicate(Root<MachineGroupInfo> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
CriteriaQuery<MachineGroupInfo> query = cb.createQuery(MachineGroupInfo.class);
Predicate p1=null;
if (prov != null ) {
Predicate p2 = cb.equal(root.get("prov").as(String.class), prov);
if (p1 != null) {
p1 = cb.and(p1, p2);
else {
p1 = p2;
}
~~~~~代码逻辑~~~~~~
}
query.where(p1);
return query.getRestriction();
}
},pageable);
return page2;

}

    我们判断条件参数是否非空,以此来判断是否将条件加入查询语句,话到这里,我们知道框架的模块化,高内聚性和低耦合性是的我们的开发过程得到了大大的简化,但随之而来的问题是当我们代码的逻辑出现问题时,问题的查找变得不再那么直观,这里介绍一个我们用的很少的数据库驱动--log4jdbc,通常数据库厂商会针对每种语言发布相对应的接口程序,如mysql的驱动com.mysql.jdbc.Driver。

     Log4JDBC的好处在于我们可以用它将框架产生的数据库执行计划或者sql以日志的形式输出到控制台,这使得我们的检工作更加的方便,框架使用效果更加直观,如hibernate的执行计划参数使用"?"占位符,当参数确定时由框架生成完整的sql:

Hibernate: select machine0_.sn_code as sn_code14_0_, machine0_.id as id1_0_,

machine0_.air_direction as air_dir15_0_, machine0_.auto_antifreeze_protect as auto_an16_0_,

machine0_.backwater_temp as backwat17_0_, machine0_.crankcase_status as crankca18_0_,

machine0_.cumulative_electricity as cumulat19_0_, machine0_.cur_operate_mode as cur_ope20_0_,

machine0_.device_type as device_21_0_, machine0_.effluent_temp as effluen22_0_,

machine0_.electric_current as electri23_0_, machine0_.exhaust_temp1 as exhaust24_0_,

machine0_.expansion_valve_step as expansi25_0_, machine0_.fault_effluent_temp as fault_ef2_0_,

machine0_.fault_exhaust_temp as fault_ex3_0_, machine0_.fault_high_voltage as fault_hi4_0_,

machine0_.fault_indish_temp as fault_in5_0_, machine0_.fault_indoor_temp as fault_in6_0_, machine0_.fault_press_temp

as fault_pr7_0_, machine0_.fault_room_temp as fault_ro8_0_, machine0_.fault_water_switch as fault_wa9_0_,

machine0_.fourway_valve_status1 as fourway26_0_, machine0_.indish_temp as indish_27_0_,

machine0_.indoor_fan_status as indoor_28_0_, machine0_.indoor_temp as indoor_29_0_,

machine0_.machine_power as machine30_0_, machine0_.machine_voltage as machine31_0_,

machine0_.outdoor_temp as outdoor32_0_, machine0_.permanent_fault_high_voltage as permane10_0_,

machine0_.permanent_fault_press_temp as permane11_0_, machine0_.permanent_fault_water_switch as

permane12_0_, machine0_.press_status1 as press_s33_0_, machine0_.report_time as report_13_0_,

machine0_.set_temp as set_tem34_0_, machine0_.sleep_mode as sleep_m35_0_, machine0_.switch_status as switch_36_0_,

machine0_.water_pump_status as water_p37_0_, machine0_.water_supply_temp as water_s38_0_,

machine0_.water_tank_temp as water_t39_0_, machine0_.wind_status as wind_st40_0_ from machine_report_info

machine0_ where (machine0_.fault_room_temp=? or machine0_.fault_indoor_temp=? or machine0_.fault_indish_temp=? or

machine0_.fault_effluent_temp=? or machine0_.fault_exhaust_temp=? or machine0_.permanent_fault_press_temp=? or machine0_.fault_press_temp=?

or machine0_.fault_water_switch=? or machine0_.permanent_fault_water_switch=? or machine0_.fault_high_voltage=?

or machine0_.permanent_fault_high_voltage=?) and (cast(machine0_.report_time as datetime) between ? and ?) order by machine0_.id asc limit ?

MVC log4jdbc:配置信息如下所示:

spring.jpa.show-sql = true

logging.level.org.springframework.data=DEBUG

spring.jpa.hibernate.ddl-auto=

spring.datasource.validationQuery = SELECT 1

#######db##########

#spring.datasource.url=jdbc:mysql://17*.17.123.1*:8096/geothermy?useUnicode=true&characterEncoding=utf-8

spring.datasource.url = jdbc:log4jdbc:mysql://1*.17.123.193:8096/geothermy?useUnicode=true&characterEncoding=utf-8

spring.datasource.username=geo

spring.datasource.password=******************

#spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.driverClassName = net.sf.log4jdbc.DriverSpy

spring.datasource.initialSize=5

spring.datasource.minIdle=5

spring.datasource.maxActive=20

# Naming strategy

spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy

我们做代码测试的时候这个会很有帮助,通过这个方法我们也能学习框架中对于各种需求处理的方式,下面内容中我们会提到。

2.分页查询

在web项目中分页查询是不可或缺的一项技术,一般有两种分页方式:①.前端分页:首先这是一种伪分页,我们将需要的数据一次性查询并发送给前端,由前端进行分页,显然这种方式的缺点不用细说,数据量巨大的时候从响应时间到硬件资源的占用都是非常大的。②.分页查询分页:我们只需在创建查询sql的时候确定分页参数,按指定页数,指定页行数进行查询并返回结果即可,优势不言而喻。

JPA分页查询:

Dao层接口继承如下接口:

public interface MachineGroupStatusRespository extends PagingAndSortingRepository<Machine,Long>,JpaSpecificationExecutor<Machine> {}

泛型是你需要查询的Entity类;

Service层:

public Page<Machine> getHistoriaclInfo(Integer page, Integer size, String sncode, Timestamp minTime, Timestamp curTime){
Sort sort=new Sort(Sort.Direction.DESC,"reportTime");
Pageable pageable=new PageRequest(page,size,sort);
Page<Machine> pageHistory=machineGroupStatusRespository.findAll(new Specification<Machine>() {
@Override
public Predicate toPredicate(Root<Machine> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
CriteriaQuery query=cb.createQuery(Machine.class);
Predicate p1=cb.equal(root.get("snCode").as(String.class),sncode);
Predicate p2=cb.and(p1,cb.between(root.get("reportTime").as(Timestamp.class),minTime,curTime));
query.where(p2);
return query.getRestriction();
}
},pageable);
return pageHistory;
}

返回Page对象,我们来看看Page对象的结构:

{"machineGroupInfo":{"content":[],"totalElements":0,"last":true,"totalPages":0,"size":10,"number":0,"sort":[{"direction":"ASC","property":"id","ignoreCase":false,"nullHandling":"NATIVE","ascending":true}],"first":true,"numberOfElements":0}} 包括了:元数据,排序方式,大小写敏感设置,查询记录的总条目数,当前页的数据条目数,总页数等信息;

3.联表查询和笛卡尔积

mysql作为关系型数据库因为表与表之间的联系组成了庞大的数据组织,而联表查询使我们使用的较多的数据库技术,不做多的阐述,但当两个表格或者查询结果集没有直接的关联而我们又需要将其合并在一起时怎么做呢?这时我们就需要用到mysql的笛卡尔积以合并无关的结果集或者表格,基本语法参见:mysql笛卡尔积

4.Spring Date JPA 动态分页联表查询的实现

在经过对JPA的一系列接口实验以实现上述需求之后,笔者发现它们似乎并不适合做这件事情,特别是当查询比较复杂时,使用封装的接方法会使得工作很难进行并且容易逻辑混乱,经过前辈推荐,我们在这里选择使用EntityManager 的nativeQuery方法进行查询,然后自己封装查询结果:

实体类:

public class UnionSearch extends IdEntity {

//(继承公共的Id管理类,如果你只有一个类请使用@Id @GeneratedValue)
private String sncode;
private String prov;
private String city;
private String area;
private String tel;
private String address;
private String itemname;
private String username;
private String switchstatus;
private String reporttime;
private String sum;

构造器

get、set方法

}

Dao层:

/**
* Created by BBSee rolltion.zhang@foxmail.com on 2017/7/20.
*/

@Transactional
@Repository
public class CommonDao {
@PersistenceContext
EntityManager entityManager;

/**
* * 查询数据集合
@param sql 查询sql sql中的参数用:name格式
@param params 查询参数map格式,key对应参数中的:name
@param clazz 实体类型为空则直接转换为map格式
@return
*/
@SuppressWarnings("unchecked")
public List<?> queryListEntity(String sql, Map<String, Object> params, Class<?> clazz){
Session session = entityManager.unwrap(org.hibernate.Session.class);
SQLQuery query = session.createSQLQuery(sql);
if (params != null) {
for (String key : params.keySet()) {
query.setParameter(key, params.get(key));
}
}
query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
List<Map<String, Object>> result = query.list();
if (clazz != null) {
List<Object> entityList = convert(clazz, result);
return entityList;
}
return result;
}

private List<Object> convert(Class<?> clazz, List<Map<String, Object>> list) {
List<Object> result;
if (CollectionUtils.isEmpty(list)) {
return null;
}
result = new ArrayList<Object>();
try {
PropertyDescriptor[] props = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
for (Map<String, Object> map : list) {
Object obj = clazz.newInstance();
for (String key:map.keySet()) {
String attrName = key.toLowerCase();
for (PropertyDescriptor prop : props) {
attrName = removeUnderLine(attrName);
if (!attrName.equals(prop.getName())) {
continue;
}
Method method = prop.getWriteMethod();
Object value = map.get(key);
if (value != null) {
value = ConvertUtils.convert(value,prop.getPropertyType());
}
method.invoke(obj,value);
}
}
result.add(obj);
}
catch (Exception e) {
throw new RuntimeException("数据转换错误");
}
return result;
}

private String removeUnderLine(String attrName) {
//去掉数据库字段的下划线
if(attrName.contains("_")) {
String[] names = attrName.split("_");
String firstPart = names[0];
String otherPart = "";
for (int i = 1; i < names.length; i++) {
String word = names[i].replaceFirst(names[i].substring(0, 1), names[i].substring(0, 1).toUpperCase());
otherPart += word;
}
attrName = firstPart + otherPart;
}
return attrName;
}

/**
* 获取记录条数
@param sql
@param params
@return
*/
public Integer getCountBy(String sql,Map<String, Object> params){
Query query = entityManager.createNativeQuery(sql);
if (params != null) {
for (String key : params.keySet()) {
query.setParameter(key, params.get(key));
}
}
BigInteger bigInteger = (BigInteger) query.getSingleResult();
return bigInteger.intValue();
}

/**
* 新增或者删除
@param sql
@param params
@return
*/
public Integer deleteOrUpDate(String sql,Map<String, Object> params){
Query query = entityManager.createNativeQuery(sql);
if (params != null) {
for (String key : params.keySet()) {
query.setParameter(key, params.get(key));
}
}
return query.executeUpdate();
}
}

* Created by BBSee rolltion.zhang@foxmail.com on 2017/7/19.

@SuppressWarnings({"JpaQlInspection""JpaQueryApiInspection"})
@Repository
public class UnionSearchDao extends CommonDao{

/**
* 按查询条件动态分页
@param queryCondition
@return
*/
public List<UnionSearch> getPageList(Integer currentPage,Integer size,Map<String,Object> queryCondition){
String sql="select * from (select \n" +
"t2.id as id, t2.sncode as sncode,\n" +
"t2.prov as prov, t2.city as city, \n" +
"t2.area as area, \n" +
"t2.tel as tel , \n" +
"t2.address as address , \n" +
"t2.itemname as itemname , \n" +
"t2.username as username ,\n" +
" case when t1.switchstatus is null then 'NO' end as switchstatus, \n" +
" case when t1.reporttime is null then 'NO' end as reporttime \n" +
" from (select id,sn_code as sncode,prov,city,area,telephone as tel,item_name as itemname, address,user_name as username from machine_user_info where ? )t2 \n" +
" left join (\n" +
" select t3.sncode as sncode,t3.switchstatus as switchstatus,max(t3.reporttime) as reporttime \n" +
" from (\n" +
" select sn_code as sncode,switch_status as switchstatus,report_time as reporttime from machine_report_info \n" +
" where report_time >= CURRENT_TIMESTAMP - INTERVAL 10 MINUTE)t3 group by t3.sncode)t1 on t2.sncode=t1.sncode group by t2.id limit "+currentPage*size+","+size+ ")s1,\n" +
" (select count(distinct m.id) as sum from (\n" +
" select id,sn_code as sncode,prov as prov,city as city,area as area,telephone as tel,item_name as itemname, address as address,user_name as username from machine_user_info where ?)m)s2\n" +
" order by s1.id,s2.sum";

String where="";
Set set=queryCondition.keySet();
Iterator iterator=set.iterator();
List<String> list=new ArrayList<>();
while(iterator.hasNext()){
list.add((String) iterator.next());
}
for(int i=0;i<list.size();i++){
if(i==0){
where+=list.get(i)+"= :"+list.get(i);
}else {
where+=" and "+list.get(i)+"= :"+list.get(i);
}
}
sql=sql.replace("?",where);
System.out.println(sql);
return (List<UnionSearch>) super.queryListEntity(sql,queryCondition,UnionSearch.class);
}
}

对象封装Bean

import java.util.List;

* Created by BBSee rolltion.zhang@foxmail.com on 2017/7/20.

public class UnionResponse {
private List result;
private String totalPage;
private String totalElements;
private String numberOfElements;
private String resultCode;
getset方法
}

看看我们的SQL查询结果:

service层计算分页参数:

List list= unionSearchDao.getPageList(page,size,queryCondition);

if(list==null){
response.setResultCode("00X");
response.setTotalElements("0");
}else {
response.setResultCode("0");
response.setResult(list);//设置元数据
JSONObject jsonObject=JSONObject.fromObject(list.get(0));
System.out.println(jsonObject.has("sum")+","+jsonObject.has("prov"));
Long sum= Long.parseLong(jsonObject.get("sum").toString());//与查询总数字段一致
response.setTotalElements(sum+"");
Long totalPage=(long)(Math.ceil(Double.parseDouble(sum/size+"")))+1;
response.setTotalPage(totalPage+"");
response.setNumberOfElements(list.size()+"");
}

最后对比一下Page对象与我们自己封装的对象:

Page对象:

{"machineGroupInfo":{"content":[],"totalElements":0,"last":true,"totalPages":0,"size":10,"number":0,"sort":[{"direction":"ASC","property":"id","ignoreCase":false,"nullHandling":"NATIVE","ascending":true}],"first":true,"numberOfElements":0},"resultCode":"00x"}

{"result":[{"id":87,"sncode":"D3A108167900000611110012","prov":"xx省","city":"xx市","area":"xx区","tel":"13xx015xx08x","address":"xxx地址","itemname":"xxx项目", "username":"BBSee","switchstatus":null,"reporttime":null,"sum":"27"}],"totalPage":"27","totalElements":"27","numberOfElements":"1","resultCode":"0"}

嗯,基本一致,至于排序方式,大小写设置这些就比较简单了,如果需要酌情添加,以上就是jpa的动态条件,动态分页联表查询,通过动态拼接执行计划,没有sql注入的风险,但代码耦合度有点高,待他日我将这个思路封装好再与大家做更多的分享。​​​​

基于SpringDataJpa的mysql动态分页多表查询相关推荐

  1. 在MySQL中实现交叉表查询2(动态交叉表)

    在MySQL中实现交叉表查询2(动态交叉表) 交叉表分为静态交叉表和动态交叉表.其中静态交叉表中的列是固定的,因此相对容易实现:而动态交叉表中的列需要动态生成. 一.静态交叉表的实现 参见上一篇文章: ...

  2. mysql修改工资字段_基于Linux的MySQL操作实例(修改表结构,MySQL索引,MySQL数据引擎)...

    基于Linux的MySQL操作实例(修改表结构,MySQL索引,MySQL数据引擎) 前言 本篇是基于Linux下针对MySQL表结构的修改,MySQL索引的操作以及MySQL数据引擎的配置和说明. ...

  3. 在MySQL中实现交叉表查询1(静态交叉表)

    在MySQL中实现交叉表查询1(静态交叉表) 一.什么是交叉表 交叉表查询是将来源于某个表中的字段进行分组,一组列在交叉表左侧,一组列在交叉表上部,并在交叉表行与列交叉处显示表中某个字段的各种计算值. ...

  4. MySQL数据库应用 多表查询_mysql数据库-多表查询

    今日任务 完成对MYSQL数据库的多表查询及建表的操作 教学目标 掌握MYSQL中多表的创建及多表的查询 掌握MYSQL中的表关系分析并能正确建表 昨天内容回顾: ​ 数据库的创建 : create ...

  5. MySQL 笔记5 -- 多表查询

    MySQL 笔记5 – 多表查询 MySQL 系列笔记是笔者学习.实践MySQL数据库的笔记 课程链接: MySQL 数据库基础入门教程 参考文档: MySQL 官方文档 一.表之间关系 1.一对一 ...

  6. Mysql动态数据多条件查询

    Mysql动态数据多条件查询 前言 有的时候,我们进行数据的数据查询的时候,我们会加入筛选条件. 例如: select * from user where username = ? // ? 指的是自 ...

  7. mybatis分页多表查询

    mybatis分页多表查询 我们常遇到一种情况,数据量比较大,如果使用前台分页,在前台如果有权限,有逻辑判断,都会大大降低页面加载的速度,我们需要分页,单表查询的网上方法很多,这里就不赘余了,多表关联 ...

  8. 记录:数据库(MySQL)之多表查询

    数据库(MySQL)之多表查询 1.创建表 -- 多表联合查询 -- 创建三个表,并进行插入数据 -- 创建emp表,并插入数据 CREATE TABLE emp ( eno int(5),-- 员工 ...

  9. 【mysql】基础-多表查询(下)

    上文 中描述了多表关系.内连接.外连接.自连接.联合查询 union, union all.标量子查询.列子查询.行子查询.表子查询 文章链接在[mysql]基础-多表查询(上)_無名_b的博客-CS ...

最新文章

  1. Ubuntu 18.04.1 搭建Java环境和HelloWorld
  2. Python地信专题 |基于geopandas的空间数据分析-深入浅出分层设色
  3. 学习笔记68—PhotoShop常见快捷键
  4. codeforces contest 1140(D~G)
  5. 酷派android是什么系统版本,酷派大神X7的手机系统是什么?能升级安卓5.0吗?
  6. 三菱模拟量输入与输出程序_初学PLC是学习西门子还是三菱?
  7. Hibernate一对一关联------主键关联(亲测成功)
  8. linux-关机与重启命令
  9. 华为海思Hi1620芯片发布在即 7nm制程ARM架构最高可达3.0GHz
  10. 软件工程基础之需求分析
  11. 机器学习基础(三十七) —— 处理类别特征
  12. 因果推断笔记——数据科学领域因果推断案例集锦(九)
  13. 开源面向对象数据库 db4o 之旅,第 1 部分: 初识 db4o
  14. 计算机频繁开机是什么原因,电脑频繁自动重启什么原因
  15. elasticsearch查询关键字slop
  16. 【图片resize】图片三种缩放方式/letterbox_image实现
  17. 突破体系结构的错误–当Dreamliner成为噩梦时
  18. 网络教育计算机和英语难度怎样,网络教育统考大学英语b难吗?
  19. MySQL(进阶篇)
  20. win7计算机管理无用户账户,win7系统控制面板中用户账户显示空白的解决方法

热门文章

  1. MOS管和IGBT区别,一看就懂
  2. 笔试强训48天——day23
  3. Linux网课笔记——Linux自有服务
  4. micropython入门(二)硬件入门及步进电机驱动(102+A4988板)
  5. 使用Python的Swampy程序包中的“乌龟”画图及有意思的习题
  6. eclipse的介绍
  7. 仿豆瓣网首页(HTML+CSS)
  8. 中国经济两三年内能恢复原状吗
  9. vostro3470装win7_戴尔(dell)成就3470台式机装win7系统及bios设置(支持8代/9代cpu)
  10. 嵌入式系统-硬件工程师