热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

解析JPA的视图查询问题

这篇文章主要是对JPA的视图查询问题进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助

昨天晚上遇到一个需求,每天早上要生成一份报告给各个部门的Leader。实现方式基本上确定为HTML格式的电子邮件。但是数据方面犯了难。原因在于数据库中存储的数据是跨表的,而且还要做count统计,这样得到的结果就不是原生的MySQL表,我用的又是JPA技术。我们知道,使用JPA第一步就是映射实体,每一张表就至少对应一个实体(力求严谨,因为联合主键时一张表会对应两个对象)。可是对于灵活的查询尤其是连接查询,并不存在一个真正的表与其对应,怎么样才能解决呢?来,我们来举个“栗子”

假设我们有两张表,一张学院表,一张学生表。学院表里存着学院ID和学院名称,学生表里存着学生的基本信息,包括学号、学院ID和学生姓名(其它较复杂的属性我们不看了),正如下面的建表语句所示:

代码如下:

-- ----------------------------
-- Table structure for `depts`
-- ----------------------------
DROP TABLE IF EXISTS `depts`;
CREATE TABLE `depts` (
  `deptId` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '学院ID',
  `deptName` varchar(50) NOT NULL COMMENT '学院名称',
  PRIMARY KEY (`deptId`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of depts
-- ----------------------------
INSERT INTO `depts` VALUES ('1', '哲学院');
INSERT INTO `depts` VALUES ('2', '经济学院');
INSERT INTO `depts` VALUES ('3', '法学院');
INSERT INTO `depts` VALUES ('4', '教育学院');
INSERT INTO `depts` VALUES ('5', '文学院');
INSERT INTO `depts` VALUES ('6', '历史学院');
INSERT INTO `depts` VALUES ('7', '理学院');
INSERT INTO `depts` VALUES ('8', '工学院');
INSERT INTO `depts` VALUES ('9', '农学院');
INSERT INTO `depts` VALUES ('10', '医学院');
INSERT INTO `depts` VALUES ('11', '军事学院');
INSERT INTO `depts` VALUES ('12', '管理学院');
INSERT INTO `depts` VALUES ('13', '艺术学院');


再建立一个学生表,再随便往里面插入点数据:
代码如下:

-- ----------------------------
-- Table structure for `students`
-- ----------------------------
DROP TABLE IF EXISTS `students`;
CREATE TABLE `students` (
  `stuNo` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '学号 从1000开始',
  `deptId` int(10) unsigned NOT NULL COMMENT '学院ID',
  `stuName` varchar(50) NOT NULL COMMENT '学生姓名',
  PRIMARY KEY (`stuNo`),
  KEY `FK_DEPTID` (`deptId`),
  CONSTRAINT `FK_DEPTID` FOREIGN KEY (`deptId`) REFERENCES `depts` (`deptId`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1006 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of students
-- ----------------------------
INSERT INTO `students` VALUES ('1000', '13', '鸟叔');
INSERT INTO `students` VALUES ('1001', '7', '乔布斯');
INSERT INTO `students` VALUES ('1002', '3', '阿汤哥');
INSERT INTO `students` VALUES ('1003', '3', '施瓦辛格');
INSERT INTO `students` VALUES ('1004', '2', '贝克汉姆');
INSERT INTO `students` VALUES ('1005', '3', '让雷诺');


现在我们想统计一下各个学院都有多少学生。这个题目在我们学习SQL的时候再简单不过了。两种实现方法:

使用Group By和不使用Group By:

代码如下:

SELECT b.deptId, b.deptName, count(*) as 'totalCount' FROM students a LEFT JOIN depts b ON a.deptId=b.deptId GROUP BY b.deptId ORDER BY b.deptId;

使用Group By之后,凡是没有对应学生记录的学院都没有显示出来(我不明白为什么。。。如果有人知道的话麻烦告诉我好吗?)
代码如下:

+--------+--------------+------------+
| deptId | deptName     | totalCount |
+--------+--------------+------------+
|      2 | 经济学院     |          1 |
|      3 | 法学院       |          3 |
|      7 | 理学院       |          1 |
|     13 | 艺术学院     |          1 |
+--------+--------------+------------+

再来一个不使用Group By的查询:
代码如下:

SELECT a.deptId, a.deptName, (SELECT count(*) FROM students b where b.deptId=a.deptId) as 'totalCount' FROM depts a;

这次就完全显示出来了:
代码如下:

+--------+--------------+------------+
| deptId | deptName     | totalCount |
+--------+--------------+------------+
|      1 | 哲学院       |          0 |
|      2 | 经济学院     |          1 |
|      3 | 法学院       |          3 |
|      4 | 教育学院     |          0 |
|      5 | 文学院       |          0 |
|      6 | 历史学院     |          0 |
|      7 | 理学院       |          1 |
|      8 | 工学院       |          0 |
|      9 | 农学院       |          0 |
|     10 | 医学院       |          0 |
|     11 | 军事学院     |          0 |
|     12 | 管理学院     |          0 |
|     13 | 艺术学院     |          1 |
+--------+--------------+------------+

至此,我们的SQL写通了。但是怎么才能使用JPA来查询出一样的视图呢?

我们按照往常编码那样,从一个主要的实体操作服务中暴露出EntityManager来:

代码如下:

package net.csdn.blog.chaijunkun.dao;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Service;

@Service
public class ObjectDaoServiceImpl implements ObjectDaoService {

 @PersistenceContext
 private EntityManager entityManager;

 @Override
 public EntityManager getEntityManager(){
  return this.entityManager;
 }

}


这样做的好处就是所有的数据操作都来源于同一个实体管理器。将来若部署发生变化,只改这一处注入就可以了。

然后我们还需要和以前一样构造两个表的实体类:

学院表的实体类:

代码如下:

package net.csdn.blog.chaijunkun.pojo;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="depts")
public class Depts implements Serializable {

 /**
  *
  */
 private static final long serialVersiOnUID= 3602227759878736655L;

 @Id
 @GeneratedValue(strategy= GenerationType.AUTO)
 @Column(name= "deptId")
 private Integer deptId;

 @Column(name= "deptName", length= 50, nullable= false)
 private String deptName;

 //getters and setters...
}


学生表的实体类:
代码如下:

package net.csdn.blog.chaijunkun.pojo;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name= "students")
public class Students implements Serializable {

 /**
  *
  */
 private static final long serialVersiOnUID= -5942212163629824609L;

 @Id
 @GeneratedValue(strategy= GenerationType.AUTO)
 @Column(name= "stuNo")
 private Long stuNo;

 @ManyToOne
 @JoinColumn(name= "deptId", nullable= false) 
 private Depts depts;

 @Column(name= "stuName", length= 50, nullable= false)
 private String stuName;

 //getters and setters...

}


两个实体类都构造好了,我们接下来还要弄一个视图类,属性的类型完全由你想要的结构来构造。例如这个例子中我们要学院编号,学院名称和总人数。那么我们就这么定义:
代码如下:

package net.csdn.blog.chaijunkun.pojo;

import java.io.Serializable;

public class Report implements Serializable {

 /**
  *
  */
 private static final long serialVersiOnUID= 4497500574990765498L;

 private Integer deptId;

 private String deptName;

 private Integer totalCount;

 public Report(){};

 public Report(Integer deptId, String deptName, Integer totalCount) {
  this.deptId = deptId;
  this.deptName = deptName;
  this.totalCount = totalCount;
 }

 //getters and setters...

}


可以说,视图对象的定义比实体定义还要简单,不需要注解,不需要映射(以上代码为了减少代码量均省去了各属性的get和set方法,请自行添加)。但是唯一不同的是我们需要额外构造一个带有字段初始化的构造函数。并且还不能覆盖默认的无参构造函数。然后我们就开始进入真正的查询了(作为视图来讲,SQL规范中是不允许修改数据的。因此,视图仅有SELECT特性。这也是为什么很多人使用JPA想通过实体映射数据库内建视图的方式进行查询,却始终映射不成功的症结所在。
代码如下:

package net.csdn.blog.chaijunkun.dao;

import java.util.List;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

import org.springframework.stereotype.Service;

import net.csdn.blog.chaijunkun.pojo.Depts;
import net.csdn.blog.chaijunkun.pojo.Report;
import net.csdn.blog.chaijunkun.pojo.Students;

@Service
public class ReportServiceImpl implements ReportService {

 @Resource
 private ObjectDaoService objectDaoService;

 @Override
 public List getReport() {
  String jpql= String.format("select new %3$s(a.deptId, a.deptName, (select count(*) from %2$s b where b.deptId= a.deptId) as totalCount) from %1$s a",
    Depts.class.getName(),
    Students.class.getName(),
    Report.class.getName());

  EntityManager entityManager= objectDaoService.getEntityManager();
  //建立有类型的查询
  TypedQuery reportTypedQuery= entityManager.createQuery(jpql, Report.class);
  //另外有详细查询条件的在jpql中留出参数位置来(?1 ?2 ?3....),然后在这设置
  //reportTypedQuery.setParameter(1, params);
  List reports= reportTypedQuery.getResultList();
  return reports;
 }

}


在上面的代码中我们构造了JPQL中的视图查询语句。最重要的就是要在最初的select后面new出新的对象。然后把我们查询到的结果通过视图对象的构造函数灌入各个属性。由统计生成的字段最好用as重命名结果以保持和视图对象属性名称相同。这样,我们就得到了视图数据。接下来就去尝试遍历这个List吧,操作非常方便。

另外,向大家推荐一本书——Apress出版社出版的《Pro JPA 2 Mastering the Java trade Persistence API》,这本书详细介绍了JPA的相关技术,非常实用。


推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 本文由编程笔记小编整理,介绍了PHP中的MySQL函数库及其常用函数,包括mysql_connect、mysql_error、mysql_select_db、mysql_query、mysql_affected_row、mysql_close等。希望对读者有一定的参考价值。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 数据库(外键及其约束理解)(https:www.cnblogs.comchenxiaoheip6909318.html)My ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • yum安装_Redis —yum安装全过程
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Redis—yum安装全过程相关的知识,希望对你有一定的参考价值。访问https://redi ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 解决VS写C#项目导入MySQL数据源报错“You have a usable connection already”问题的正确方法
    本文介绍了在VS写C#项目导入MySQL数据源时出现报错“You have a usable connection already”的问题,并给出了正确的解决方法。详细描述了问题的出现情况和报错信息,并提供了解决该问题的步骤和注意事项。 ... [详细]
  • 本文介绍了通过mysql命令查看mysql的安装路径的方法,提供了相应的sql语句,并希望对读者有参考价值。 ... [详细]
  • 本文详细介绍了MySQL表分区的创建、增加和删除方法,包括查看分区数据量和全库数据量的方法。欢迎大家阅读并给予点评。 ... [详细]
author-avatar
手机用户2502858307
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有