`

JPA的视图查询(转)

阅读更多
本文出处:http://blog.csdn.net/chaijunkun/article/details/8442406,转载请注明。由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。

昨天晚上遇到一个需求,每天早上要生成一份报告给各个部门的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<Report> 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<Report> reportTypedQuery= entityManager.createQuery(jpql, Report.class);
//另外有详细查询条件的在jpql中留出参数位置来(?1 ?2 ?3....),然后在这设置
//reportTypedQuery.setParameter(1, params);
List<Report> reports= reportTypedQuery.getResultList();
return reports;
}

}

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



2014年3月28日补充:

最近在查阅资料的时候发现了另外一种变通的方式——“返璞归真”,将JPA对查询的包装进行去包装,得到实现JPA底层框架的原生查询,然后再进行操作。

Query query= entityManager.createNativeQuery(sql);

此时得到了query对象,我们可以执行去包装操作:SQLQuery nativeQuery= query.unwrap(SQLQuery.class);

上面的语句是针对Hibernate的,使用其他实现框架时可以查阅手册,找找对应的Query实现类。

得到原生查询后,我们可以再设置查询参数和字段类型:

设置参数,诸如:nativeQuery.setParameter("id",123456);

设置字段类型,诸如:nativeQuery.addScalar("id", StandardBasicTypes.LONG);

这里多说一句,当字段声明类型为bigInt (mysql数据库) 时,Hibernate查询返回的默认类型是BigInteger,当我们使用Long类型时就会产生类型转换异常。因此这种情况必须进行addScalar操作,限定好具体类型。

当返回的结果无法进行实体映射时,我们还可以声明ORM,让其返回Map,我们自己去对象化数据。

EclipseLink的方法是:nativeQuery.setHint(QueryHints.RESULT_TYPE, ResultType.Map);

Hibernate的方法是:nativeQuery.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);

此时查询的结果就得写成:List<Map<String, Object>> retVal= nativeQuery.list();

然后就对retVal进行遍历取得数据行,遍历或者随机读取Map来取得对应字段。





另外,向大家推荐一本书——Apress出版社出版的《Pro JPA 2 Mastering the Java trade Persistence API》,这本书详细介绍了JPA的相关技术,非常实用。
---------------------
作者:Jack_Chai
来源:CSDN
原文:https://blog.csdn.net/chaijunkun/article/details/8442406
版权声明:本文为博主原创文章,转载请附上博文链接!
分享到:
评论

相关推荐

    解析JPA的视图查询问题

    在本篇文章中,我们将深入探讨如何在JPA中使用视图查询。 首先,让我们了解JPA的基础。JPA通过实体类与数据库表进行映射,实体类代表了数据库中的表,实体类的字段对应表中的列。然而,对于视图,由于它不是一个...

    springjpa 和 spring多视图配置实例

    本实例探讨的是如何在Spring环境中整合Spring MVC、Spring Data JPA以及使用Freemarker或JSON作为多视图配置。让我们深入理解这些技术及其相互作用。 首先,Spring MVC是Spring框架的一部分,专门用于构建Web应用...

    Spring Data JPA中文文档[1.4.3]_springdatajpa_erlang_waitxpf_

    9. **JPA Querydsl Support**:Spring Data JPA 还支持 Querydsl,这是一个强大的类型安全的查询语言,它可以进一步提高查询的可读性和可维护性。 10. **Elasticsearch 集成**:虽然不是 Spring Data JPA 的核心...

    jpa

    实体是JPA中的核心概念,它是Java类,代表数据库中的表或视图。实体管理器负责与数据库交互,执行CRUD(创建、读取、更新、删除)操作。实体管理工厂是单例模式的工厂类,用于创建实体管理器实例。JPQL(Java ...

    springboot jpa获取行政区域

    此外,可能还需要实现一些更复杂的查询,比如根据父级ID查找子区域,这可以通过自定义JPA的`@Query`注解实现。 视图层使用Thymeleaf,我们需要创建HTML模板来展示数据。例如,`region-list.html`用于显示所有区域,...

    spring struct + jpa

    1. **Repository接口**:Spring Data JPA允许开发者定义接口,其中的方法会自动转换为JPQL查询。无需编写任何DAO层代码,即可实现基本的CRUD操作。 2. **事务管理**:Spring的声明式事务管理可以自动处理事务的开始...

    springmvc+jpa 例子

    4. **JPQL(Java Persistence Query Language)**:JPA的查询语言,类似SQL,但更面向对象。 **Spring MVC与JPA整合** 在Spring MVC应用中整合JPA,可以实现高效的数据库操作。以下是一些关键步骤: 1. **配置JPA*...

    JPA(hibernate) Dao 和 DaoSupport

    Hibernate是一个流行的JPA实现,它扩展了JPA的功能,提供了更强大的查询能力和性能优化。 **DAO(Data Access Object)模式:** DAO模式是一种设计模式,用于隔离业务逻辑层和数据访问层,使得业务代码不会直接与...

    spring boot jpa security

    Spring Data JPA是Spring框架的一个模块,提供了对JPA的高级支持,包括自动配置、查询生成和仓库抽象层,使得数据库操作更加便捷。 Spring Security是一个功能强大的安全框架,用于保护基于Spring的应用程序。它...

    struts2 jpa project

    它提供了一种模型-视图-控制器(MVC)架构,使得开发者可以更有效地分离业务逻辑、数据处理和用户界面。JPA(Java Persistence API)是Java平台上的一个标准,用于管理关系数据库中的对象持久化。它简化了数据库操作...

    OpenJPA 2.2.1 API (CHM格式)

    OpenJPA 针对数据持久化提供多种层次、多方面的缓存支持,包括数据、查询、汇编查询的缓存等。这些缓存的应用可以大幅度的提高企业应用的运行效率。 从之前发布其他chm文件下载用户的反映看,有不少朋友反映下载后...

    springmvc+datajpa

    此外,Spring Data JPA 还支持自定义查询,通过在 Repository 接口中添加注解方法,可以执行更复杂的 SQL 或 HQL 查询。同时,它还提供了事务管理功能,确保数据的一致性和完整性。 总的来说,Spring MVC + Spring ...

    手动创建 SpringMvc +SpringDataJpa+Hibernate+ freemarker mavenProject+ 环境切换 webDemo

    此外,Spring Data JPA还支持自定义查询,允许开发者使用@Query注解编写JPQL语句。 Hibernate作为JPA的实现,是一个强大的对象关系映射(ORM)框架。它将Java对象与数据库表进行映射,使得开发者可以通过操作对象来...

    基于JSF+EJB3+JPA的竞价拍卖系统

    JPA提供了查询语言JPQL,类似于SQL,但更面向对象,方便对数据库进行复杂的查询操作。同时,JPA的事务管理保证了数据的一致性和完整性。 **服务器环境:JBOSS** JBOSS是一个开源的应用服务器,支持Java EE规范,...

    spring mvc整合jpa后项目

    - 可以添加自定义的方法,利用JPA的查询语言(如JPQL)或者`@Query`注解进行复杂查询。 5. **Service层**: - 创建业务逻辑服务类,使用@Autowired注入Repository,实现业务处理。 - 在Service层中,可以调用...

    SpringMVC +Hibernate JPA+Spring-data-jpa实例

    SpringMVC是Spring框架的一部分,主要负责处理Web应用中的模型-视图-控制器(MVC)架构。它提供了一个轻量级、灵活的Web开发解决方案,使开发者可以方便地创建功能丰富的Web应用程序。 **SpringMVC工作原理:** 1. ...

    maven+springmvc+springjpa+hibernate

    它可以与各种JPA供应商(如Hibernate、EclipseLink等)配合使用,提供事务管理、懒加载、查询构造等功能。 最后,**Hibernate**是JPA的一个流行实现,它提供了一套强大的ORM解决方案。Hibernate不仅实现了JPA规范,...

    springMvc data jpa example

    通过定义 Repository 接口,Spring Data JPA 可以自动生成实现,包括CRUD操作,甚至复杂的查询方法。 在 "springMvc-jpa-blank-master" 这个项目中,我们可以预期以下组件和配置: 1. **配置文件**:通常会有一个 ...

    Spring+Spring MVC+SpringData JPA整合完成增删改查,翻页实例.zip

    Spring Data JPA进一步抽象了JPA,提供了一种声明式的方式来执行CRUD(创建、读取、更新、删除)操作,以及复杂的查询。通过定义Repository接口,Spring Data JPA可以自动生成实现,大大减少了手动编写DAO层代码的...

    jsf基础教程+JPA教程

    通过JSF,用户界面的创建和更新变得简单,而JPA则负责数据的持久化和查询。开发者可以在JSF的视图层中直接绑定JPA实体,实现数据的动态显示和更新。这种方式极大地提高了开发效率,同时保持了代码的整洁和模块化。 ...

Global site tag (gtag.js) - Google Analytics