Hibernate使用——数据查询示例

Criteria Query的使用

Criteria 查询表达式

Criteria 本身只是一个查询容器,具体的查询条件需要通过Criteria.add方法加到Criteria实例中。

示例1:

Criteria criteria = session.createCriteria(TPerson.class);
criteria.add(Restrictions.eq("name", "oham"));
criteria.add(Restrictions.eq("address", "earth"));	
criteria.list();

 执行sql:

select
        this_.id as id0_0_,
        this_.name as name0_0_,
        this_.address as address0_0_,
        this_.tel as tel0_0_,
        this_.zipcode as zipcode0_0_ 
    from
        t_person this_ 
    where
        this_.name=? 
        and this_.address=?

注意, 1)鄙人使用的Hibernate版本为4.1.4.Final,示例1的中Restrictions是先前版本的Expression的替代,在4.1.4.Final中,Expression已被标记为过期。

            2)针对SQL语法,Restrictions提供了对应的查询机制,可以去看看:http://docs.jboss.org/hibernate/orm/4.1/javadocs/

         3)Restriction中的各个方法的属性名参数是指POJO中的属性名,非真实数据库表的字段名。

Example类的使用

Example实现了Criteria接口,所以也可以用作Criteria的查询条件。Example的作用是,根据已有的对象,查找属性与之相符的其他对象。一般用于组合查询,如,界面上提供若干查询选项,然后根据用户所选返回符合条件的结果。

示例2:

Criteria criteria = session.createCriteria(TPerson.class);
		
Example example;
example = Example.create(examplePerson);
		
/**
 * 默认的情况下,Hibernate会过滤掉示例对象的Null值属性,可以通过调用
 * excludeNone/excludeZeroes方法进行调整,或则通过excludeProperty将
 * 某个属性排除在外。
 * 1.excludeNone —— 不过滤Null值的属性
 * 2.excludeZeroes —— 过滤值为0 的属性
 * 3.请测测example.excludeNone与example.excludeZeroes,若两者调用,貌似
 * 只用后者起作用。。。
*/
//	example.excludeZeroes();
//	example.excludeNone();
		
//	example.excludeProperty("address");
		
	criteria.add(example);
	criteria.list();

 执行上述的代码的sql:

    select
        this_.id as id0_0_,
        this_.name as name0_0_,
        this_.address as address0_0_,
        this_.tel as tel0_0_,
        this_.zipcode as zipcode0_0_,
        this_.age as age0_0_ 
    from
        t_person this_ 
    where
        (
            this_.name=? 
            and this_.address=? 
            and this_.age=?
        )

Criteria复合查询

 现在引入一个一对多的场景:


 执行下面的代码,打印出所有的person以及对应的skill:

Criteria criteria = session.createCriteria(TPerson.class);
List<TPerson> personList = criteria.list();
		
for(TPerson person : personList) {
	System.out.println(person.getName());
			
	Set<TSkill> set = person.getSkills();
	for(TSkill skill : set) {
		System.out.println("\t\t" + skill.getName());
	}
}

 结果:

Hibernate: 
    select
        this_.id as id0_0_,
        this_.name as name0_0_,
        this_.address as address0_0_,
        this_.tel as tel0_0_,
        this_.zipcode as zipcode0_0_,
        this_.age as age0_0_ 
    from
        t_person this_
Hibernate: 
    select
        skills0_.pid as pid0_1_,
        skills0_.id as id1_,
        skills0_.id as id1_0_,
        skills0_.name as name1_0_,
        skills0_.pid as pid1_0_ 
    from
        t_skill skills0_ 
    where
        skills0_.pid=?
Oham
		meditation
		kill

如果现在想查出所有具有kill技能的person,可以使用复合查询:

Criteria criteria = session.createCriteria(TPerson.class);
		
//在原有的查询基础上,针对TPerson对象的skills属性构造新的查询过滤条件
Criteria addCriteria = criteria.createCriteria("skills");
addCriteria.add(Restrictions.like("name", "%kill%"));
		
List<TPerson> personList = criteria.list();
		
for(TPerson person : personList) {
	System.out.println(person.getName());
			
	Set<TSkill> set = person.getSkills();
	for(TSkill skill : set) {
		System.out.println("\t\t" + skill.getName());
	}
}

Hibernate会在运行期构造以下的sql:

select
        this_.id as id0_1_,
        this_.name as name0_1_,
        this_.address as address0_1_,
        this_.tel as tel0_1_,
        this_.zipcode as zipcode0_1_,
        this_.age as age0_1_,
        tskill1_.id as id1_0_,
        tskill1_.name as name1_0_,
        tskill1_.pid as pid1_0_ 
    from
        t_person this_ 
    inner join
        t_skill tskill1_ 
            on this_.id=tskill1_.pid 
    where
        tskill1_.name like ?

  

DetachedCriteria

criteria对象生于session,所以其生命周期位于session之内,即虽不同生,必同死。。。这样就很大程度限制了Criteria的重用,对于相同的查询条件,在不同的session实例里需要再实例一个Criteria。

DetachedCriteria是可以脱离Session实例而独立存在的,于是我们可以使用其将某些通用的Criteria查询条件进行抽离,然后使用的时候将其再与当前的session实例绑定。

示例3:

//生成detachedCriteria的实例
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(TPerson.class);
		
		//detachedCriteria的用法与criteria基本类似
		DetachedCriteria addDetachedCriteria = detachedCriteria.createCriteria("skills");
		addDetachedCriteria.add(Restrictions.like("name", "%kill%"));
		
		
		SessionFactory factory = HibernateLocalUtil.getSessionFactory();
		Session session = factory.openSession();
		
		//调用getExecutableCriteria与当前的session实例绑定
		Criteria criteria = addDetachedCriteria.getExecutableCriteria(session);
		
		List<TPerson> personList = criteria.list();
		for(TPerson person : personList) {
			System.out.println(person.getName());
			
			Set<TSkill> set = person.getSkills();
			for(TSkill skill : set) {
				System.out.println("\t\t" + skill.getName());
			}
		}
		
		session.close();

 DetachedCriteria也可用于子查询,现在想查询大于平均年龄的所有person。

示例4:

DetachedCriteria avgAge = DetachedCriteria.forClass(TPerson.class);
avgAge.setProjection(Projections.avg("age"));
		
Criteria criteria = session.createCriteria(TPerson.class);
criteria.add(Subqueries.propertyGt("age", avgAge));
		
List<TPerson> list = criteria.list();

生成执行 sql:

    select
        this_.id as id0_0_,
        this_.name as name0_0_,
        this_.address as address0_0_,
        this_.tel as tel0_0_,
        this_.zipcode as zipcode0_0_,
        this_.age as age0_0_ 
    from
        t_person this_ 
    where
        this_.age > (
            select
                avg(this_.age) as y0_ 
            from
                t_person this_
        )

Criteria的其他特性辑录

   ——限定返回结果范围:

Criteria criteria = session.createCriteria(TPerson.class);
		
//从100条结果开始的20条记录
criteria.setFirstResult(100);
criteria.setMaxResults(20);

   ——排序:

	//按名字(顺序)和地址(逆序)排序
	criteria.addOrder(Order.asc("name"));
	criteria.addOrder(Order.desc("address"));

   ——分组与统计:

//对年龄age进行分组
criteria.setProjection(Projections.groupProperty("age"));

 生成sql:

    select
        this_.age as y0_ 
    from
        t_person this_ 
    group by
        this_.age

对于多条件组合的分组、统计功能,可以借助ProjectionList,现在统计各个年龄的person数量,

示例5:

	ProjectionList pList = Projections.projectionList();
	pList.add(Projections.groupProperty("age"));
	pList.add(Projections.rowCount());
		
	criteria.setProjection(pList);
	criteria.list();

 生成sql:

    select
        this_.age as y0_,
        count(*) as y1_ 
    from
        t_person this_ 
    group by
        this_.age

HQL(Hibernate Query Language)

 Hibernate提供的一套类SQL的查询语言。

具体语法:  [select / update / delete ...]  [from ...] [where ... ] [group by ...] [having ...] [order by ...],用起来与SQL十分类似,但注意好像HQL没有insert之用。

注意“

1.若查询或操作一个实体表的数据,from 后面跟的是实体类名,可以采用全路径类名,特别是同名不同包的情况。

2.HQL字句本身大小写无关,但其中出现的类名和属性名需要区分大小写。

3.若查询的目标实体存在继承关系的判定,例如 TUser 有两个子类TSystemUser和TAdmin,分别做了实体映射, 那么 from TUser 这条HQL返回的记录将包含这两个子类的所有数据,而包含的字段是与父类公有的那些。

4.from java.lang.Object  这句将返回数据库所有表的数据。

以下示例基于如下表关系:


 

用法举例:

1.获取某个实体的几个属性时可以这样:select u.name, u.age from TUser as u,而此时,list结果集中每个条目是个Object[]类型,不是实体类型,依次包含了所获取的各个属性数据。可以让其返回实体类对象,这样就行了:select new TUser(u.id, u.name) from TUser u,前提是TUser必须有这个构造函数(不好用。。。)。

下面是测试代码:

String hql1 = "from TUser";
String hql2 = "select u.id, u.name from TUser u";
String hql3 = "select new TUser(u.id, u.name) from TUser u";
		
//	List list = session.createQuery(hql1).list();
//	List list = session.createQuery(hql2).list();
List list = session.createQuery(hql3).list();
		
Iterator iterator =  list.iterator();
		
while(iterator.hasNext()) {
			
	//for hql and hql3
	TUser u = (TUser)iterator.next();
	System.out.println(u.getName());
			
	// for hql2
	/*Object[] results = (Object[])iterator.next();
	System.out.println(results[1]);*/
			
}

 2.动态的参数绑定:

      (1)使用query接口方法:

String hql = "from TUser u where u.name = ?";
		
Query query = session.createQuery(hql);
query.setString(0, "lulu");
		
List list = query.list();

       (2)使用占位符:

String hql = "from TUser u where u.name = :name";
Query query = session.createQuery(hql);
query.setParameter("name", "Lulu");

 说明动态的参数绑定机制可以使得查询语法与具体的参数值相互独立,这样,对于参数不同,SQL语法相同的查询操作,数据库可以实施性能优化策略。同时避免了参数值对语法本身的影响,也就避免了SQL Injection。

3.HQL的引用查询,将HQL语句本身从代码中抽出,写在映射配置文件当中。

<?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">
  
<hibernate-mapping package="learnHibernate.bean">
	<class name="TUser" table="t_user">
		<id name="id" column="id" type="java.lang.Integer">
			<generator class="native"/>
		</id>
		
		<property name="name" column="name" type="java.lang.String"/>
	</class>
	
	<query name="TUser.getByName">
		<![CDATA[
			from TUser u where u.name = :name
		]]>
	</query>
	
</hibernate-mapping>

 然后代码中:

Query query = session.getNamedQuery("TUser.getByName");
query.setParameter("name", "Lulu");

 4.HQL的联合查询,就是跟SQL的inner join ,left outter, right outer join, full join 差不多的。

注意的是:

 1. 关键字fetch ,看示例代码:

	String hql = "from TUser u join fetch u.addrs";
	Query query = session.createQuery(hql);
		
	List list = query.list();
		
	Iterator it = list.iterator();
		
	while(it.hasNext()) {
		TUser u = (TUser)it.next();
		System.out.print(u.getName() + "----");
		System.out.println(u.getAddrs().size());
	}

上述的HQL对应SQL为: 

    select
       ...
    from
        t_user U
    inner join
        t_address addr
            on uid=addr.uid

(其中因为on uid=addr.uid所描述的对应关系已在映射文件中指定,所以HQL无对应的表现) 

这里的fetch表示查出address对象后立刻立刻填充到对应的TUser对象的address集合属性中。若不加fetch,则list当中每个条目是个Object[],其中Object[]包含了一个TUser对象和对应的一个TAddress对象,运行代码:

String hql = "from TUser u join u.addrs";
Query query = session.createQuery(hql);
		
List list = query.list();
		
Iterator it = list.iterator();
		
while(it.hasNext()) {
	Object[] arr = (Object[])it.next();
			
	for(int i=0; i< arr.length; i++) {
		if(arr[i] instanceof TUser) {
			System.out.println( ((TUser)arr[i]).getName() );
		}else if(arr[i] instanceof TAddress) {
			System.out.println( "---" + ((TAddress)arr[i]).getAddr() );
		}
	}
}

 输出结果:

Hibernate: 
    select
        tuser0_.id as id0_0_,
        addrs1_.id as id1_1_,
        tuser0_.name as name0_0_,
        addrs1_.uid as uid1_1_,
        addrs1_.addr as addr1_1_ 
    from
        t_user tuser0_ 
    inner join
        t_address addrs1_ 
            on tuser0_.id=addrs1_.uid
Cancan
---Mars
Oham
---Earth
Lulu
---Moon
Lulu
---Earth
Maomao
---Venus

2.fetch 只对inner join 和 left outer join 有效,这是因为想right outer join,full join之类,作为关联对象可能为空,因此Hibernate无法通过fecth 进行集合填充操作。

数据加载方式

 Hibernate中支持以下的数据加载方式:

1.即时加载(Immediate Loading)—— 当实体加载完毕,立即加载其关联的数据。

2.延迟加载(Lazy Loading)—— 实体加载时,其关联数据不是立刻获取,而是当关联数据第一次被访问时再进行读取。

3.预先加载(Eager Loading)—— 实体及其关联对象同时读取,这与即时加载类似,不过实体及关联数据是通过一条SQL语句(基于外连接[outer join])同时读取。

4.批量加载(Batch Loading)—— 对于即时加载和延迟加载,可以采用批量加载方式进行性能上的优化。

举例说明,这里引入TUser及其地址的一对多关联。

即时加载,TUser映射中描述关联关系的配置段:

<set name="addrs" 
	table="t_address"
	cascade="none"
	lazy="false">
	<key column="uid" />
	<one-to-many class="TAddress" />		
</set>
 注意lazy=“false”。运行代码:
	Criteria criteria = session.createCriteria(TUser.class);
        criteria.add(Restrictions.eq("name", "lulu"));
	List list = criteria.list();
 输出结果:
Hibernate: select this_.id as id0_0_, this_.name as name0_0_ from t_user this_ where this_.name=?
Hibernate: select addrs0_.uid as uid0_1_, addrs0_.id as id1_, addrs0_.id as id1_0_, addrs0_.uid as uid1_0_, addrs0_.addr as addr1_0_ from t_address addrs0_ where addrs0_.uid=?
 可以看出即时加载的时候,当加载关联主题TUser的时候,Hibernate会自动读取其关联的数据并完成关联属性的填充。

延迟加载,有时我们只想取得TUser的信息,无需取得其对应的地址信息,即使用延迟加载,相应的关联关系配置段:

		<set name="addrs" 
			 table="t_address"
			 lazy="true">
			 <key column="uid" />
			<one-to-many class="TAddress" />		
		</set>
 把lazy设置为“true”就行了。执行代码:
		Criteria criteria = session.createCriteria(TUser.class);
		criteria.add(Restrictions.eq("name", "lulu"));
		
		List list = criteria.list();
 输出结果:
Hibernate: select this_.id as id0_0_, this_.name as name0_0_ from t_user this_ where this_.name=?
 可以看出此时只加载了TUser的数据,没有相应的Address信息,若加上读取Address的代码:
Criteria criteria = session.createCriteria(TUser.class);
criteria.add(Restrictions.eq("name", "lulu"));
		
List list = criteria.list();
		
Iterator it = list.iterator();
while(it.hasNext()) {
	TUser user = (TUser)it.next();
	System.out.println(user.getAddrs().size());
	
}
 输出结果:
Hibernate: select this_.id as id0_0_, this_.name as name0_0_ from t_user this_ where this_.name=?
Hibernate: select addrs0_.uid as uid0_1_, addrs0_.id as id1_, addrs0_.id as id1_0_, addrs0_.uid as uid1_0_, addrs0_.addr as addr1_0_ from t_address addrs0_ where addrs0_.uid=?
2
 所谓的延迟加载就是当真正需要数据的时候才进行读取操作。

预先加载,所谓的预先加载就是通过outer-join的方式,以一条SQL完成实体及其关联数据的读取操作,相比即时加载时的若干条SQL读取操作而言,其性能更优。

		<set name="addrs" 
			 table="t_address"
			 lazy="true"
			 outer-join="true">
			 <key column="uid" />
			<one-to-many class="TAddress" />		
		</set>
配置中加outer-join=“true” 即可启用预先加载。

注意,对于集合类型的关联关系(一对多,多对一,多对多)一般不宜采用预先加载的方式,理由跟即时加载的一样;

特别复杂的关联关系,如多层关联,这样生成的outer-joinSQL可能会过于复杂,因根据情况判断预先加载的可用性,可以通过全局变量(hibernate.max_fetch_depth)限定outer-join的层次(一般设定为5层)。

批量加载,即通过批量提交多个限定条件,一次完成多个数据的读取。如对于以下形式的SQL:

select  from t_user where id = 1;
select  from t_user where id = 2;
 合并为一条SQL来完成同样的功能:
select  from t_user where id = 1 or id = 2;
 若使用了批量加载的方式,Hibernate会在进行数据查询之前,自动在当前的Session中寻找是否还有同类型的待加载的数据,如果有则将其查询条件合并到当前的select语句中一并提交。

只需在实体映射文件的class节点设定一个batch-size,就行,一般batch-size要设定为合理数值(<10)。

示例说明,还是拿TUser跟TAddress来说,现在TUser的映射中关于TAddress的映射配置如下:

<set name="addrs" 
	table="t_address"
	lazy="true">
	<key column="uid" />
	<one-to-many class="TAddress" />		
</set>
 执行代码:
String hql = "from TUser";
		
Query query = session.createQuery(hql);
		
List<TUser> list = query.list();
		
Iterator<TUser> it = list.iterator();
		
while(it.hasNext()) {
	TUser u = it.next();
	Set<TAddress> addrs = u.getAddrs();
	Iterator<TAddress> it2 = addrs.iterator();
			
	while(it2.hasNext()) {
		System.out.println(it2.next().getAddr());
	}
}		
 输出结果:
Hibernate: select tuser0_.id as id0_, tuser0_.name as name0_ from t_user tuser0_  --查询TUser的

--下面是对应每个TUser的实体对象分别查一次TAddress对象
Hibernate: select addrs0_.uid as uid0_1_, addrs0_.id as id1_, addrs0_.id as id1_0_, addrs0_.uid as uid1_0_, addrs0_.addr as addr1_0_ from t_address addrs0_ where addrs0_.uid=? 
Mars
Hibernate: select addrs0_.uid as uid0_1_, addrs0_.id as id1_, addrs0_.id as id1_0_, addrs0_.uid as uid1_0_, addrs0_.addr as addr1_0_ from t_address addrs0_ where addrs0_.uid=?
Earth
Hibernate: select addrs0_.uid as uid0_1_, addrs0_.id as id1_, addrs0_.id as id1_0_, addrs0_.uid as uid1_0_, addrs0_.addr as addr1_0_ from t_address addrs0_ where addrs0_.uid=?
Moon
Earth
Hibernate: select addrs0_.uid as uid0_1_, addrs0_.id as id1_, addrs0_.id as id1_0_, addrs0_.uid as uid1_0_, addrs0_.addr as addr1_0_ from t_address addrs0_ where addrs0_.uid=?
Venus
 现在映射文件改为如下:
<set name="addrs" 
	table="t_address"
	lazy="true"
	batch-size="3">
	<key column="uid" />
	<one-to-many class="TAddress" />		
</set>
加了个batch-size=“3”,执行同样的代码,输出结果:
Hibernate: select tuser0_.id as id0_, tuser0_.name as name0_ from t_user tuser0_ -- 查询TUser对象

-- 对应每个TUser对象实例,查询TAddress对象
Hibernate: select addrs0_.uid as uid0_1_, addrs0_.id as id1_, addrs0_.id as id1_0_, addrs0_.uid as uid1_0_, addrs0_.addr as addr1_0_ from t_address addrs0_ where addrs0_.uid in (?, ?, ?)
Mars
Earth
Earth
Moon
Hibernate: select addrs0_.uid as uid0_1_, addrs0_.id as id1_, addrs0_.id as id1_0_, addrs0_.uid as uid1_0_, addrs0_.addr as addr1_0_ from t_address addrs0_ where addrs0_.uid=?
Venus
 可以看出,由于设置batch-size=“3”,Hibernate将具有相同类型的待加载数据每3个的合并为一条SQL;如果这里设置为4,则最终只合并为一条,这里我就不试了。   SQL查询

 Hibernate的SQL查询接口使用示例:

看代码:

String sql = "select * from t_user usr";
List list = session.createSQLQuery(sql).list();
		
Iterator it = list.iterator();
		
while(it.hasNext()) {
	Object[] props = (Object[])it.next();
			
	System.out.println(props[1]);
}

 如代码中所示,list()返回的是一个Object[],里面依次包含了SQL 中 * 代表的各个column的值,至于其顺序需自知。而关于column的类型问题,请看Hibernate的官网介绍,比我写的强多了:http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html/ch13.html

上述代码输出结果:

Hibernate: select * from t_user usr
Cancan
Oham
Lulu
Maomao

使用SQL查询返回实体类型示例

 示例代码:

String sql = "select * from t_user usr";
List list = session.createSQLQuery(sql).addEntity(TUser.class).list();
		
Iterator it = list.iterator();
		
while(it.hasNext()) {
	TUser u = (TUser)it.next();
	System.out.println(u.getName());
	System.out.println(u.getAddrs().size());
}

 调用一下addEntity(Class cls)指定实体类型就可以了。

 使用SQL查询返回多个实体类型示例

String sql = "select {u.*}, {addr.*} from t_user u, t_address addr where u.id = addr.uid";
Query query = session.createSQLQuery(sql).addEntity("u", TUser.class)
					.addEntity("addr", TAddress.class);	
		
List list = query.list();
		
Iterator it = list.iterator();
		
while(it.hasNext()) {
	Object[] entitys = (Object[])it.next();
	TUser u = (TUser)entitys[0];
	TAddress addr = (TAddress)entitys[1];
			
	System.out.println(u.getName() + " lives in " + addr.getAddr());
			
}

 使用SQL查询返回一个非映射实体bean示例

首先是一个非映射实体bean:

package learnHibernate.bean;

import java.io.Serializable;

public class UserAddrBean implements Serializable{
	private static final long serialVersionUID = -1156337693273177901L;
	
	private int userId;
	private String userName;
	private String address;
	public int getUserId() {
		return userId;
	}
	public void setUserId(int userId) {
		this.userId = userId;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	
	
}

 示例代码:

String sql = "select u.id as userId, u.name as userName, addr.addr as address " +
				"from t_user u, t_address addr where u.id = addr.uid";
Query query = session.createSQLQuery(sql)
		.setResultTransformer(Transformers.aliasToBean(UserAddrBean.class));
		
List list = query.list();
		
Iterator it = list.iterator();
		
while(it.hasNext()) {
	UserAddrBean ud = (UserAddrBean)it.next();
			
	System.out.println(ud.getUserName() + " lives in " + ud.getAddress());
			
}

动态参数绑定

与HQL的一样用法,示例:

String sql = "select u.id as userId, u.name as userName, addr.addr as address " +
			"from t_user u, t_address addr where u.id = addr.uid and u.name = ?";
Query query = session.createSQLQuery(sql)
		.setResultTransformer(Transformers.aliasToBean(UserAddrBean.class));
		
query.setString(0, "oham");






String sql = "select u.id as userId, u.name as userName, addr.addr as address " +
			"from t_user u, t_address addr where u.id = addr.uid and u.name = :name";
Query query = session.createSQLQuery(sql)
		.setResultTransformer(Transformers.aliasToBean(UserAddrBean.class));
		
query.setParameter("name", "oham");
 

引用查询与HQL的差不多用法,现在介绍一种类似于Ibatis风格的resultSet,在映射文件中的一段引用SQL:

<resultset name="userAddrRs">
	<return alias="u" class="TUser" />
	<return-join alias="addr" property="u.addrs" />
</resultset>
	
<sql-query name="userAddrSQL" resultset-ref="userAddrRs">
	SELECT
		{u.*}, {addr.*}
	FROM t_user u
	JOIN t_address addr ON addr.uid = u.id
</sql-query>
 注意的是resultSet节点必须写在sql-query,包括query节点之前,测试代码:
Query query = session.getNamedQuery("userAddrSQL");
		
List list = query.list();
		
Iterator it = list.iterator();
		
while(it.hasNext()) {
	Object[] arr = (Object[])it.next();
			
	for(int i=0; i< arr.length; i++) {
		if(arr[i] instanceof TUser) {
			System.out.println( ((TUser)arr[i]).getName() );
		}else if(arr[i] instanceof TAddress) {
			System.out.println( "---" + ((TAddress)arr[i]).getAddr() );
		}
	}
			
}
 可以看出,返回来的list的每个条目是个Object[]。

注意:resultSet中明确指定属性可以这样来:

<resultset name="userAddrRs">
	<return alias="u" class="TUser" >
		<return-property name="id" column="userId" />
		<return-property name="name" column="userName" />
	</return>
	<return-join alias="addr" property="u.addrs" />
</resultset>
	
<sql-query name="userAddrSQL" resultset-ref="userAddrRs">
	SELECT
		u.id AS userId,
		u.name  AS userName,
		addr.*
	FROM t_user u
	JOIN t_address addr ON addr.uid = u.id
</sql-query>
 为什么SQL在下用addr.* ?,阁下可以试试addr像u那样指定明确列名,然后在return-join里面也写上return-property,会报错,具体原因不知道,在下估计是TUser与TAddress的一对多映射关联没弄好。映射配置如下
<set name="addrs" 
			 table="t_address"
			 lazy="true"
			 batch-size="4">
			 <key column="uid" />
			<one-to-many class="TAddress" />		
		</set>
还有很多特性的,详情须看看官方文档。

调用存储过程

创建一个存储过程(在下用的是MySQL):

CREATE PROCEDURE test_pr (in p_name varchar(30))
BEGIN
  select id, name from t_user where name  = p_name;
END

 然后看映射配置文件:

<sql-query name="testProcedure" callable="true">
	<return alias="u" class="TUser">
		<return-property name="id" column="id" />
		<return-property name="name" column="name" />
	</return>
	{ call test_pr(:name) }
</sql-query>

 然后是测试代码:

	Query query = session.getNamedQuery("testProcedure");
	query.setParameter("name", "oham");
		
	List<TUser> list = query.list();
		
	Iterator<TUser> it = list.iterator();
		
	while(it.hasNext()) {
		TUser u = it.next();
		System.out.println(u.getName());
			
	}

 注意的是因为不同的DBMS对存储过程的规范不一样,因此Hibernate对这个的支持也不一样,可以google一下Hibernate如何调用Oracle的存储过程,跟MySQL的是不一样的。

自定义持久化实现

 简而言之,就是直接指定用于实体的insert, update, delete操作的SQL语句,而不是Hibernate自动生成的SQL,示例:

在TAddress的映射文件中加入:

<sql-insert>
	INSERT INTO t_address (uid, addr) VALUES (?,?)
</sql-insert>
	
<sql-update>
	UPDATE t_address SET addr = ?, uid = ? WHERE id = ?
</sql-update>
	
<sql-delete>
	DELETE FROM t_address WHERE id = ?
</sql-delete>
 然后执行代码:
	Transaction tx = session.beginTransaction();
	TAddress addr = (TAddress)session.get(TAddress.class, 4);
		
	addr.setAddr("Earth_2");
	addr.setuId(1);
		
	tx.commit();
 
 输出如下:
Hibernate: select taddress0_.id as id1_0_, taddress0_.uid as uid1_0_, taddress0_.addr as addr1_0_ from t_address taddress0_ where taddress0_.id=?
Hibernate: UPDATE t_address SET addr = ?, uid = ? WHERE id = ?
 可以看出,update的时候用了<sql-update>里面的update SQL。要注意的是代码里的set值顺序必须要与update SQL中的一样。

若把代码中的set值语句换换位置,运行,输出结果:

若把映射中的<sql-update>注释掉,看输出结果如下:

org.hibernate.exception.GenericJDBCException: Incorrect integer value: 'Earth_2' for column 'uid' at row 1
 在下的抛了这么个异常,说明Hibernate用上了自定义的update SQL,而代码的set值顺序有误导致动态参数绑定后出现数据类型错误。若此时把映射文件中的<sql-update>注释掉,在运行:
Hibernate: select taddress0_.id as id1_0_, taddress0_.uid as uid1_0_, taddress0_.addr as addr1_0_ from t_address taddress0_ where taddress0_.id=?
Hibernate: update t_address set uid=?, addr=? where id=?
 正常执行了,因为update 的SQL是用了Hibernate自动生成的SQL。 自定义持久化大概是这么个意思,具体可以翻翻官方文档,但其应用场景在下始终还是不明白。。。

猜你喜欢

转载自gwoham-163-com.iteye.com/blog/1923623