`

使用HIBERNATE的SQL查询并将结果集自动转换成POJO

阅读更多
在某些场合下,我们可能想使用HIBERNATE的框架提供的SQL查询接口,但是,由于实体没有做映射,HIBERNATE不能把结果集转换成你想要的List<POJO>,本文讨论如何在这种情况下让HIBERNATE直接返回你想的结果。下面是简单的查询封装方法

public List<?> findObjectBySql(String queryString,Class<?> pojoClass){
                  //使用SQL构造查询对象,此SQL是可以被JDBC接受的SQL,如SELECT * FROM XXX_TABLE
		Query query = this.getSession().createSQLQuery(queryString);
                  //设置结果集转换器,这是本文重点所在
		query.setResultTransformer(new EscColumnToBean(pojoClass));
                  //返回查询结果
		return query.list();



    上面这个方法很简单,如果查的是USER表,并且pojoClass参数传入的就是USER.class的话,那么返回结果集的就是List<User>。当然在我的应用中还有查询条件封装,分页和自动查询总行数等逻辑,不想把主题分散,所以给省略了。
     下面是结果集转换器的完整代码

package com.lgdlgd.hibernate;

import java.lang.reflect.Field;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.property.ChainedPropertyAccessor;
import org.hibernate.property.PropertyAccessor;
import org.hibernate.property.PropertyAccessorFactory;
import org.hibernate.property.Setter;
import org.hibernate.transform.ResultTransformer;

import com.esc.common.baseclass.BaseModel;

/**
 * 自定义的数据库字库转换成POJO
 */
public class EscColumnToBean implements ResultTransformer {
	private static final long serialVersionUID = 1L;
	private final Class<? extends BaseModel> resultClass;
	private Setter[] setters;
	private PropertyAccessor propertyAccessor;
	
	public EscColumnToBean(Class<? extends BaseModel> resultClass) {
		if(resultClass==null) throw new IllegalArgumentException("resultClass cannot be null");
		this.resultClass = resultClass;
		propertyAccessor = new ChainedPropertyAccessor(new PropertyAccessor[] { PropertyAccessorFactory.getPropertyAccessor(resultClass,null), PropertyAccessorFactory.getPropertyAccessor("field")}); 		
	}

	//结果转换时,HIBERNATE调用此方法
	public Object transformTuple(Object[] tuple, String[] aliases) {
		Object result;
		
		try {
			if(setters==null) {//首先初始化,取得目标POJO类的所有SETTER方法
				setters = new Setter[aliases.length];
				for (int i = 0; i < aliases.length; i++) {
					String alias = aliases[i];
					if(alias != null) {
						//我的逻辑主要是在getSetterByColumnName方法里面,其它都是HIBERNATE的另一个类中COPY的
						//这里填充所需要的SETTER方法
						setters[i] = getSetterByColumnName(alias);
					}
				}
			}
			result = resultClass.newInstance();
			
			//这里使用SETTER方法填充POJO对象
			for (int i = 0; i < aliases.length; i++) {
				if(setters[i]!=null) {
					setters[i].set(result, tuple[i], null);
				}
			}
		} catch (InstantiationException e) {
			throw new HibernateException("Could not instantiate resultclass: " + resultClass.getName());
		} catch (IllegalAccessException e) {
			throw new HibernateException("Could not instantiate resultclass: " + resultClass.getName());
		}
		
		return result;
	}

	//根据数据库字段名在POJO查找JAVA属性名,参数就是数据库字段名,如:USER_ID
	private Setter getSetterByColumnName(String alias) {
		//取得POJO所有属性名
		Field[] fields = resultClass.getDeclaredFields();
		if(fields==null || fields.length==0){
			throw new RuntimeException("实体"+resultClass.getName()+"不含任何属性");
		}
		//把字段名中所有的下杠去除
		String proName = alias.replaceAll("_", "").toLowerCase();
		for (Field field : fields) {
			if(field.getName().toLowerCase().equals(proName)){//去除下杠的字段名如果和属性名对得上,就取这个SETTER方法
				return propertyAccessor.getSetter(resultClass, field.getName());
			}
		}
		throw new RuntimeException("找不到数据库字段 :"+ alias + " 对应的POJO属性或其getter方法,比如数据库字段为USER_ID或USERID,那么JAVA属性应为userId");
	}

	@SuppressWarnings("unchecked")
	public List transformList(List collection) {
		return collection;
	}

}



我这个转换器工作的前提是数据库的字段名除了可以含有下杠外,不能再含有其它 POJO对应属性中没有的字符,如USER_ID 对应POJO的userId。
扩展点:
1、可以自定义数据库与POJO属性的对应关系,在查询方法中构造转换器时,通过参数把转换关系传入进来,这样就可实现自由转换了,但请注意这种转换关系只有非常通用才有价值(上面的转换规则就是:字段名去除下杠 = JAVA属性名),反之如果每个实体都有自己特有的转换关系,那就还不如编写一个HIBERNATE的映射文件了。
2、本类没有考虑关联,即自定义的对象属性,但这些都不难实现,有兴趣的朋友可结合我的第一篇文章来实现。

注意:HIBERNATE3.25以前(后面的没看),对SQL查询的CHAR类型字段处理不好,只返回一位,且是JAVA的CHAR类型,要修改CharacterType类的代码以返回该类型字段的全值

后记:有朋友说到,HIBERNATE本身也支持这种封装,直接使用HIBERNATE原有的封装:
Query query = this.getSession().createSQLQuery(queryString).addEntity(pojoClass);

这样后面就不需要设置结果转换器了,但是这样做的前提是实体必须要有映射

//刷屏
14
3
分享到:
评论
12 楼 寻找 2012-02-20  
我想看封装的分页应用,可以发到我邮箱吗?zhaining123129@gmail.com ,对我真的很有用处!顶!
11 楼 nt1327 2011-09-29  
BaseModel怎么写啊?
10 楼 nt1327 2011-09-29  
BaseModel是什么
9 楼 lgdlgd 2010-08-03  
还有一种解决方法,上面给出的EscColumnToBean类中
if(field.getName().toLowerCase().equals(proName)){。。。}
这里只判断了方法名,你可以根据你的情况,对方法的参数类型也加以判断,可以从根本上解决问题,这样setAge(int) 和 setAge(BigDecimal)可以并存
8 楼 lgdlgd 2010-08-03  
还要把原来的setAge(int i)去除,否则循环时不知道会取到哪一个set方法
7 楼 lgdlgd 2010-08-03  
zhxsup 写道
你好,在转化数据时报灵下错(包装类型和基本类型),请问怎么解决

你的age字段在JAVA类里面是int类型,然而Hibernate给你传入的是BigDecimal类型是吗?如果是这样的话,根据原因可能是你的数据库字段长度弄得太大了,不合理,Hibernate将其封装成BigDecimal了,年龄改小成三位足够了,
当然你可增加一个setAge(BigDecimal b)方法就解决问题了,不需要改数据库
6 楼 zhxsup 2010-08-03  
你好,在转化数据时报灵下错(包装类型和基本类型),请问怎么解决
ERROR [main] (BasicPropertyAccessor.java:118)(2010-08-03 17:36:09) - IllegalArgumentException in class: com.entity.SysUser, setter method of property: age
ERROR [main] (BasicPropertyAccessor.java:122)(2010-08-03 17:36:09) - expected type: java.lang.Integer, actual value: java.math.BigDecimal


ERROR [main] (BasicPropertyAccessor.java:118)(2010-08-03 17:41:56) - IllegalArgumentException in class: com.entity.SysUser, setter method of property: age
ERROR [main] (BasicPropertyAccessor.java:122)(2010-08-03 17:41:56) - expected type: int, actual value: java.math.BigDecimal
5 楼 liushu1234 2010-03-09  
恩,这是我看过的解决未映射SQL查询的最好办法了,不过每个多要配一下,其实也蛮麻烦的。嘿嘿。谢谢楼主分享。
4 楼 lgdlgd 2009-11-13  
....tianya 写道
哥们,你用的是哪个版本的hibernate阿,我怎么就找不到你写的方法呢

不论是org.hibernate.classic.Session
还是org.hibernate.Session
它们createSQLQuery(sql).addEntity(arg0,arg1) ;

都是2个参数的阿,你怎么就能用一个参数呢。。。。。。

还有,我也没找到你的
query.setResultTransformer(new EscColumnToBean(pojoClass));

这个方法是你自己封装的么???

如果看到留言希望能够给与解答!



我的版本是3.25
两个方法都是org.hibernate.SQLQuery接口的方法,不是我自定义的
3 楼 fucktianya 2009-11-09  
哥们,你用的是哪个版本的hibernate阿,我怎么就找不到你写的方法呢

不论是org.hibernate.classic.Session
还是org.hibernate.Session
它们createSQLQuery(sql).addEntity(arg0,arg1) ;

都是2个参数的阿,你怎么就能用一个参数呢。。。。。。

还有,我也没找到你的
query.setResultTransformer(new EscColumnToBean(pojoClass));

这个方法是你自己封装的么???

如果看到留言希望能够给与解答!
2 楼 lgdlgd 2009-10-29  
雁行 写道
如果pojo类根本就没有数据库表相对应,只是想方便数据检索,不用object数组,而是 对象方式访问,这样,是否就不能在hql里实现如下方式?

select new MyPojo(u,t) from Sysuser as U,Sysdep as t

我试过,报无法定位MyPojo类


不能,HQL要求必须要有映射的表可以,否则,HIBERNATE也不知道去查哪个表啊。
1 楼 雁行 2009-10-29  
如果pojo类根本就没有数据库表相对应,只是想方便数据检索,不用object数组,而是 对象方式访问,这样,是否就不能在hql里实现如下方式?

select new MyPojo(u,t) from Sysuser as U,Sysdep as t

我试过,报无法定位MyPojo类

相关推荐

    java6string源码-JdbcMapper:JdbcMapper是一个ORM,可以快速轻松地将普通SQL查询映射到各种POJO(Plai

    查询,并使用它们从不同类型集合的数据库中快速轻松地选择 POJO(Plain Old Java 对象)。 类型安全贯穿始终,因此不需要强制转换或忽略警告。 有两种不同的方法可以实现这一点。 JdbcMapper 在编译时生成代码,...

    支持多数据库的ORM框架ef-orm.zip

    也无需编写代码将这些查询条件转换为SQL/HQL/JPQL。DAO层也不会有老要改来改去的接口和API,几乎可以做到零编码。 对单个对象进行CRUD的操作API现在和Criteria API合并在一起。Session对象可以直接提供原本要...

    xmljava系统源码-memory:超轻量级Java持久化工具:比dbutils更小巧、好用的的持久化工具,支持Oracle&MYSQL

    xml java系统源码 清瘦的记录者: 一个比dbutils更小巧、好用的的持久化工具 1. 概述 1.1 连接、语句和结果集  从上看,其对数据访问层有相当简洁的抽象:1、连接... 获取结果集,就是把ResultSet转换为目标数

    Grails权威指南

     11.2.3 在pojo实体中使用约束  11.2.4 得到sessionfactory对象  11.3 使用spring进行依赖注入  11.3.1 使用grails中的bean  11.3.2 重载bean的定义  11.4 在grails中使用spring控制器  ...

    JAVA程序开发大全---上半部分

    9.2.3 创建Hibernate使用的数据库连接 140 9.2.4 创建SessionFactory类 140 9.2.5 使用Hibernate配置文件编辑器 141 9.2.6 使用反向工程生成持久化对象、映射文件和DAO类 143 9.2.7 使用Hibernate功能 151 9.3 ...

    JPA-eclipselink-project:使用 EclipseLink 实现的 JPA 项目示例

    API 提供类似于 SQL 的查询语言,但使用对象而不是数据库中的关系实体。 Java Persistence API 基于实体,这些实体是简单的带注释的 POJO,以及这些实体的管理器 (EntityManager),它提供处理它们的功能(添加、...

    DWR.xml配置文件说明书(含源码)

    一种不能采用默认方式定义的converter就是Bean Converter,这个是将POJO对象转换成javascript相关的数组,反向也一样.基于安全因素的考虑这种类型的converter不能采用默认的方式实现. 假设有个bean并且通过语句设置成...

    从Java走向Java+EE+.rar

    12.3 使用Hibernate 161 12.4 小结 165 第13章 Struts和Hibernate实例——两个与登录有关的实例 166 13.1 Struts和Hibernate的开发环境配置 166 13.1.1 数据库的安装和管理 166 13.1.2 Hibernate的安装 ...

    Spring中文帮助文档

    11.5.2. 使用SimpleJdbcInsert来获取自动生成的主键 11.5.3. 指定SimpleJdbcInsert所使用的字段 11.5.4. 使用SqlParameterSource提供参数值 11.5.5. 使用SimpleJdbcCall调用存储过程 11.5.6. 声明SimpleJdbcCall...

    Spring API

    11.5.2. 使用SimpleJdbcInsert来获取自动生成的主键 11.5.3. 指定SimpleJdbcInsert所使用的字段 11.5.4. 使用SqlParameterSource提供参数值 11.5.5. 使用SimpleJdbcCall调用存储过程 11.5.6. 声明SimpleJdbcCall...

    springmybatis

    现在运行这个程序,是不是得到查询结果了。恭喜你,环境搭建配置成功,接下来第二章,将讲述基于接口的操作方式,增删改查。 整个工程目录结构如下: 除非申明,文章均为一号门原创,转载请注明本文地址,谢谢! ...

    play framework 框架手册 word 版

    陆续集成,并自动运行测试 - 111 - 16.安全指南 - 112 - Sessions - 112 - 守住你的安全…安全 - 112 - 不要存储关键性的数据 - 112 - 跨站点脚本攻击 - 112 - SQL注入 - 113 - 跨站点请求伪造 - 114 - 17.Play模块和...

    play框架手册

    陆续集成,并自动运行测试 - 111 - 16.安全指南 - 112 - Sessions - 112 - 守住你的安全…安全 - 112 - 不要存储关键性的数据 - 112 - 跨站点脚本攻击 - 112 - SQL注入 - 113 - 跨站点请求伪造 - 114 - 17.Play模块和...

    整理后java开发全套达内学习笔记(含练习)

    注意:默认类型转换(自动类型提升)会丢失精度,但只有三种情况: int&gt;float; long&gt;float; long&gt;double. 看一下他们的有效位就明白。 二进制是无法精确的表示 0.1 的。 进行高精度运算可以用java.math包中...

Global site tag (gtag.js) - Google Analytics