Hibernate 优化技术之调用视图(View)和存储过程(Procedure)

前言

上一篇blog记录了hibernate抓取策略的相关用法(http://blog.csdn.net/wlwlwlwl015/article/details/42705585),它主要是在对象导航时为我们进行HQL方面的优化。本篇blog将介绍一些通用性的优化方式,即在hibernate中使用视图和存储过程。在数据量比较大时(百万级),使用hibernate时不再推荐使用HQL,而是使用原生的SQL语句,而视图、索引、存储过程等数据库对象也都是基于底层数据库和原生的SQL派生出的优化方案,废话不多说,下面就开始通过代码介绍一下如何在hibernate中调用view、proc以及需要注意的一些关键点。

通过hibernate查询视图

数据库视图(View)的概念和优点等等就不说了,这个书上和网上都讲了很多,下面直接通过例子来看一下如何在hibernate中查询视图,依旧是上一篇中的例子,一对多的典型示例:班级→学生,先看一下数据表:

下面写一个简单的视图,例如需要查询以下几个字段:stu_id、sname、sex、birthday、cname,首先根据需求创建视图,

DELIMITER $$

USE `wltestdb`$$

DROP VIEW IF EXISTS `v_stuinfo`$$

CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v_stuinfo` AS (
SELECT
  `t1`.`stu_id`   AS `stu_id`,
  `t1`.`name`     AS `sname`,
  `t1`.`sex`      AS `sex`,
  `t1`.`birthday` AS `birthday`,
  `t2`.`name`     AS `cname`
FROM (`t_student` `t1`
   JOIN `t_classroom` `t2`
     ON ((`t1`.`cid` = `t2`.`cla_id`))))$$

DELIMITER ;

通过查询语句select * from v_stuinfo即可查询视图,

OK,视图没问题,接下来就是如何映射和查询了,首先是通过hibernate建立view的mapping。

1.映射视图

如果不知道如何去手写view的映射,我们可以通过MyEclipse的hibernate的反向工程来生成hbm文件或者Annotation,打开DB Browser视图,找到我们的view然后右键Hibernate Reverse Engineering,视情况选择生成annotation或者xml文件,一路next之后我们可以发现多了三个文件,2个PO类一个hbm.xml文件,

我们可以看到生成了大写V打头+视图名的一个类,还有一个是在上一个类多加了Id结尾的类,一共视图生成2个PO类,这就是hibernate为我们映射的视图了,由于视图是没有主键的,所以hibernate在无法确定<id>的情况下自然也就无法映射成一个实体,hibernate的解决办法是:用两个PO来映射视图,一个PO封装查询视图返回的所有列,另一个PO将封装好的对象作为联合主键,这样就能完成映射了。下面看一下这两个类的代码和配置文件的代码,

VStuinfo:

package com.wl.entity;

/**
 * VStuinfo entity. @author MyEclipse Persistence Tools
 */

public class VStuinfo implements java.io.Serializable {

	// Fields

	private VStuinfoId id;

	// Constructors

	/** default constructor */
	public VStuinfo() {
	}

	/** full constructor */
	public VStuinfo(VStuinfoId id) {
		this.id = id;
	}

	// Property accessors

	public VStuinfoId getId() {
		return this.id;
	}

	public void setId(VStuinfoId id) {
		this.id = id;
	}

}

VStinfoId:

package com.wl.entity;

import java.util.Date;

/**
 * VStuinfoId entity. @author MyEclipse Persistence Tools
 */

public class VStuinfoId implements java.io.Serializable {

	// Fields

	private Integer stuId;
	private String sname;
	private String sex;
	private Date birthday;
	private String cname;

	// Constructors

	/** default constructor */
	public VStuinfoId() {
	}

	/** minimal constructor */
	public VStuinfoId(Integer stuId) {
		this.stuId = stuId;
	}

	/** full constructor */
	public VStuinfoId(Integer stuId, String sname, String sex, Date birthday,
			String cname) {
		this.stuId = stuId;
		this.sname = sname;
		this.sex = sex;
		this.birthday = birthday;
		this.cname = cname;
	}

	// Property accessors

	public Integer getStuId() {
		return this.stuId;
	}

	public void setStuId(Integer stuId) {
		this.stuId = stuId;
	}

	public String getSname() {
		return this.sname;
	}

	public void setSname(String sname) {
		this.sname = sname;
	}

	public String getSex() {
		return this.sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public Date getBirthday() {
		return this.birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public String getCname() {
		return this.cname;
	}

	public void setCname(String cname) {
		this.cname = cname;
	}

	public boolean equals(Object other) {
		if ((this == other))
			return true;
		if ((other == null))
			return false;
		if (!(other instanceof VStuinfoId))
			return false;
		VStuinfoId castOther = (VStuinfoId) other;

		return ((this.getStuId() == castOther.getStuId()) || (this.getStuId() != null
				&& castOther.getStuId() != null && this.getStuId().equals(
				castOther.getStuId())))
				&& ((this.getSname() == castOther.getSname()) || (this
						.getSname() != null && castOther.getSname() != null && this
						.getSname().equals(castOther.getSname())))
				&& ((this.getSex() == castOther.getSex()) || (this.getSex() != null
						&& castOther.getSex() != null && this.getSex().equals(
						castOther.getSex())))
				&& ((this.getBirthday() == castOther.getBirthday()) || (this
						.getBirthday() != null
						&& castOther.getBirthday() != null && this
						.getBirthday().equals(castOther.getBirthday())))
				&& ((this.getCname() == castOther.getCname()) || (this
						.getCname() != null && castOther.getCname() != null && this
						.getCname().equals(castOther.getCname())));
	}

	public int hashCode() {
		int result = 17;

		result = 37 * result
				+ (getStuId() == null ? 0 : this.getStuId().hashCode());
		result = 37 * result
				+ (getSname() == null ? 0 : this.getSname().hashCode());
		result = 37 * result
				+ (getSex() == null ? 0 : this.getSex().hashCode());
		result = 37 * result
				+ (getBirthday() == null ? 0 : this.getBirthday().hashCode());
		result = 37 * result
				+ (getCname() == null ? 0 : this.getCname().hashCode());
		return result;
	}

}

VStuinfo.hbm.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
    Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
    <class name="com.wl.entity.VStuinfo" table="v_stuinfo" catalog="wltestdb">
        <composite-id name="id" class="com.wl.entity.VStuinfoId">
            <key-property name="stuId" type="java.lang.Integer">
                <column name="stu_id" />
            </key-property>
            <key-property name="sname" type="java.lang.String">
                <column name="sname" />
            </key-property>
            <key-property name="sex" type="java.lang.String">
                <column name="sex" />
            </key-property>
            <key-property name="birthday" type="java.util.Date">
                <column name="birthday" length="10" />
            </key-property>
            <key-property name="cname" type="java.lang.String">
                <column name="cname" />
            </key-property>
        </composite-id>
    </class>
</hibernate-mapping>

2.查询视图

查询视图很简单,既然已经映射好了,那么就当成普通对象用HQL查询就可以了,下面看一下测试代码和运行结果,

没有问题,成功打印出了视图返回的两项数据。但这样映射视图需要注意一点,就是视图返回的所有列不能有NULL值,一旦有NULL值的话,那么上图中第18行返回的List必然为NULL(之前做项目就遇到这个问题,控制台发了正确的SQL语句,可返回List对象的总是NULL)。解决办法也很简单,可以给可能为空的列添加默认值,或者是给视图指定一个主键等等,当然如果视图一定不会返回NULL值的话就可以忽略这个问题了。

通过hibernate调用存储过程

同样的存储过程的概念和优点在这里就不做介绍了,依旧是通过一个完整的例子来演示hibernate中如何调用存储过程。

1.创建存储过程

依然使用班级和学生举例说明,并且还是查询上面的那5个列,只不过是用存储过程来实现,首先是创建存储过程,

DELIMITER $$

USE `wltestdb`$$

DROP PROCEDURE IF EXISTS `proc_stuinfo`$$

CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_stuinfo`(
	IN pstuid INT
    )
BEGIN
	SELECT
		t1.stu_id AS stuid,t1.name AS sname,t1.sex,
		t1.birthday,t2.name AS cname
		FROM t_student t1 INNER JOIN t_classroom t2
		ON t1.cid=t2.cla_id
		WHERE t1.stu_id=pstuid;
    END$$

DELIMITER ;

可以看到提供了一个输入参数用于传stu_id,调用一下看看数据,

可以看到调用存储过程之后成功返回了数据,接下面就是如何通过hibernate去调用存储过程了。

2.在hibernate中调用存储过程

如何在hibernate中调用存储过程,这个问题我经常在面试中问别人,不理解的是这么常用的东西好像知道的人很少,不知道是存储过程用的少还是在jdbc的api不熟悉。其实通过hibernate调用比JDBC调用存储过程更简单,我们甚至不需要使用CallableStatement对象,直接通过creatSQLQuery("{call proc_name(param)}")就可以返回数据了。下面看一下测试代码和运行结果,

这里我封装了一个DTO对象用来保存proc返回的数据,可以看到console成功打印出了调用语句和数据结果。

我个人推荐用上面这种方式调用存储过程最简单,当然还有一些其它的方式,例如通过session得到Connection对象,再通过CallableStatement去调用存储过程,代码这样写结果也是一样的,

这里可以发现session.connection()方法已经过时,从hibernate3.2.2版本开始这个方法就不推荐使用了,而是通过spring提供的spring-orm包下的SessionFactoryUtils去获取Connection对象,形如:

try {
			Connection connection = SessionFactoryUtils.getDataSource(
					getSessionFactory()).getConnection();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

关于hibernate操作存储过程的内容暂时介绍到这里,以后如果有更深入的学习研究还会陆续更新本篇blog。

总结

本篇blog记录了持久层的另一种优化方式,就是引用视图和存储过程,当数据量很大时,比如达到百万级之后,我们用HQL语句可能就达不到我们对效率方面的要求了,所以这时应使用原生SQL,比如视图、存储过程等等,基本就可以满足我们性能方面的要求。好了,本篇blog到这里就告于段落,关于hibernate优化的总结可能会暂时放一放,准备继续学习Android和前端方面的东西,时间永远不够用,加油,Raito!

时间: 2024-10-25 05:24:19

Hibernate 优化技术之调用视图(View)和存储过程(Procedure)的相关文章

Hibernate 优化技术之抓取策略(Fetching strategies)

一.前言 转载请标明出处:http://blog.csdn.net/wlwlwlwl015/article/details/42705585 使用hibernate一年多了,一直觉得他是一个很好用的持久层框架,在处理含有多个复杂的关联关系的数据表时,hibernate提供的各种关联映射可以让我们用少量的代码快速.便捷的去维护各种外键关系,当然他的核心还是允许我们以"面向对象"的方式去操作数据表,因为我们的Java语言就是面向对象的,所以我们使用ORM的持久层框架应该更容易理解和上手,他

MySQL视图view/存储过程和函数的使用

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Helvetica Neue"; color: #454545 } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 17.0px ".PingFang SC Semibold"; color: #454545 } p.p3 { margin: 0.0px 0.0px 2.0px 0.0px; font: 14.0px

【Unity技巧】Unity中的优化技术

写在前面 这一篇是在Digital Tutors的一个系列教程的基础上总结扩展而得的~Digital Tutors是一个非常棒的教程网站,包含了多媒体领域很多方面的资料,非常酷!除此之外,还参考了Unity Cookie中的一个教程.还有很多其他参考在下面的链接中. 这篇文章旨在简要地说明一下常见的各种优化策略.不过对每个基础有非常深入地讲解,需要的童鞋可以自行去相关资料. 还有一些我认为非常好的参考文章: Performance Optimization for Mobile Devices

(转)【Unity技巧】Unity中的优化技术

写在前面 这一篇是在Digital Tutors的一个系列教程的基础上总结扩展而得的~Digital Tutors是一个非常棒的教程网站,包含了多媒体领域很多方面的资料,非常酷!除此之外,还参考了Unity Cookie中的一个教程.还有很多其他参考在下面的链接中. 这篇文章旨在简要地说明一下常见的各种优化策略.不过对每个基础有非常深入地讲解,需要的童鞋可以自行去相关资料. 还有一些我认为非常好的参考文章: Performance Optimization for Mobile Devices

静态资源文件自动压缩并替换成压缩版本(大型网站优化技术)

原文:静态资源文件自动压缩并替换成压缩版本(大型网站优化技术) 这一次,我总结和分享一项大型网站优化技术,那就是在项目中自动压缩静态资源文件(css.js),并让网站自动加载压缩后的资源文件.当然,这项技术在雅虎35条前端优化建议里也有记载,但它那只是给出一个理论的方案而已,并且采用的是外部压缩工具去压缩,而在我的项目中,是直接通过自己的程序自动化去压缩所有css.js文件,然后让页面直接加载所压缩后的资源,接下来直接进入主题. 本次实验使用的是PHP脚本语言,版本是PHP5.6,是在LINUX

Android中自定义视图View之---进阶篇(Canvas的使用)

更多技术内容请移步:我的个人博客 一.前言 今天是周日,昨天刚刚写完了一篇关于如何搭建LNMP环境,让自己可以DIY有个性的个人主页: http://blog.csdn.net/jiangwei0910410003/article/details/50929955 那么今天,我们继续来看一篇关于Android中的UI篇,如何自定义视图View的进阶篇,关于前奏篇之前已经写过了,还没有了解的同学可以去看看:http://blog.csdn.net/jiangwei0910410003/articl

Unity内存优化技术测试案例

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解>电子工业出版社等. CSDN视频网址:http://edu.csdn.net/lecturer/144 Unity引擎优化技术,无非涉及到三点:CPU优化,GPU优化,内存优化. 先谈谈内存优化:大概可以分成三大部分: 1. Unity3D内部的内存 2. Mono的托管内存 3. 引入的DLL或者第

JVM优化技术

1.语言无关的经典优化技术之一:公共子表达式消除 公共子表达式消除是一个普遍应用于各种编译器的经典优化技术,它的含义是:如果一个表达式E已经被计算过了,并且从先前的计算到现在E中所有变量的值都没有发生变化,那么E的这次出现就成为了公共子表达式,对于这种表达式,没有必要花时间再对它进行计算,只需要直接用前面计算过的表达式结果替代E就可以了, 举个例子简单说明它的优化过程,假设有如下代码 int d = (c*b)*12 + a +(a+b*c) 如果这段代码交给javac编译器则不会进行任何优化,

[iOS]为什么不要在init初始化方法里调用self.view

首先.如果你调用self.view的时候,就会调用view的getter方法, 这个时候,view是空的,那么系统就会自动给你创建一个view,然后就会触发ViewDidLoad方法.那么这个时候,如果你init方法里有数组初始化.但是你还没走到那步,而直接就给数组赋值了,那么这个值赋值给了一个不存在的数组.这样就容易出现错误.所以,尽量不要在init方法里写可视化控件的语句. 附1: UIViewController的生命周期: 当一个视图控制器被创建,并在屏幕上显示的时候. 代码的执行顺序1