hibernate将本地SQL查询结果封装成对象

时间:2022-09-24 08:20:43

hibernate将本地SQL查询结果封装成对象

    不知道大家有没有碰过这种情况,迫于很多情况只能用native SQL来查询(如:复杂统计等),然而使用native查询后,结果会被放到object里,要想拿到对应的数据只能由object来强制转换,真的好烦人。因为工作原因,笔者(sourcefour)正在做大量的统计而每次都要进行强制转换,这其实是很麻烦的。我在想如果native SQL查询结果也能够封装对象就好了,于是狠狠的查看了hibernate文档,终于发现了一件令人高兴的事情——native SQL查询的结果也是可以封装成对象滴(具体请看hibernate文档【第16章】)。这种情况不得不高兴啊,于是动手做实验,结果还是令人兴奋的----成功了(^_^)于是将实验结果整理以备以后用。

1.     适用对象

可以将以下几种查询结果用Bean封装起来。通俗点说就是查询出来之后就直接将内容放到了Bean实体里。

总的来说,使用本地SQL查询出来的结果也可以直接放到Bean里,如以下情况(只能想到这么多):

直接使用本地(native)SQL查询(废话,说的不就是使用native sql^_^)

2.     具体实现步骤

2.1. 实体Bean

实体Bean的写法和对象Bean的写法是一样的。区别在于该实体Bean是不需要做Hibernate映射的。

我例子中的Bean如下:

package com.sourcefour.bean;
/**
 * 这里说明一下,该Bean中的field没必要一定是数据表中的field。
 * 而应该是DAO中要查询字段的别名,具体请看例子NativeSqlDao,
 * 我想你会明白的
 * 
 * @author sourcefour
 */
public class NativeSqlBean {
	private double maxMos;
	private double minMos;
	private double avgMos;
	private int userCount;
	private String name;

	public double getMaxMos() {
		return maxMos;
	}

	public void setMaxMos(double maxMos) {
		this.maxMos = maxMos;
	}

	public double getMinMos() {
		return minMos;
	}

	public void setMinMos(double minMos) {
		this.minMos = minMos;
	}

	public double getAvgMos() {
		return avgMos;
	}

	public void setAvgMos(double avgMos) {
		this.avgMos = avgMos;
	}

	public int getUserCount() {
		return userCount;
	}

	public void setUserCount(int userCount) {
		this.userCount = userCount;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

 

2.2.  Dao操作

直接上代码:

package com.sourcefour.dao.base;

import java.sql.SQLException;
import java.util.List;

import javax.annotation.Resource;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author sourcefour
 */
@SuppressWarnings("unchecked")
@Transactional
public abstract class BaseDaoSupport<T> extends HibernateDaoSupport {

	@Resource(name = "sessionFactory")
	public void setSuperSessionFactory(SessionFactory sessionFactory) {
		super.setSessionFactory(sessionFactory);
	}

	/**
	 * 使用sql语句进行分页查询
	 * 
	 * @param sql
	 *            sql语句
	 * @param values
	 *            参数
	 * @param offSet
	 *            第一条记录序号 >-1
	 * @param pageSize
	 *            每页要显示的记录数 >0
	 * @param beanClass
	 *            将查询结果转换为<tt>T</tt>对象
	 * @param fieldList
	 *            查询Bean的成员变量名称
	 */
	@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
	public List<T> list(final String sql, final Object[] values, final int offSet, final int pageSize,
			final Class<T> beanClass, final List<String> fieldList) {
		List<T> list = getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				SQLQuery sqlQuery = session.createSQLQuery(sql);

				// 添加要查询字段的标量
				AddScalar.addSclar(sqlQuery, beanClass, fieldList);

				Query query = sqlQuery;

				// 转换查询结果为T
				if (beanClass != null) {
					query.setResultTransformer(Transformers.aliasToBean(beanClass));
				}

				if ((values != null) && values.length > 0) {
					int i = 0;
					for (Object obj : values) {
						query.setParameter(i++, obj);
					}
				}

				if (offSet > -1) {
					query.setFirstResult(offSet);
				}

				if (pageSize > 0) {
					query.setMaxResults(pageSize);
				}
				return query.list();
			}
		});
		return list;
	}
}

 

亮点在代码中注释的两句。其中AddScalar.addSclar是自己写的,具体请看代码:

 

package com.sourcefour.dao.base;

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

import org.hibernate.Hibernate;
import org.hibernate.SQLQuery;

/**
 * @author sourcefour
 */
public class AddScalar {
	/**
	 * 将field type 和 Hibernate的类型进行了对应。这里其实不是多余的,如果不进行一定的对应可能会有问题。
	 * 问题有两个:
	 * 	1. 在oracle中我们可能把一些字段设为NUMBER(%),而在Bean中的字段定的是long。那么查询时可能会报:
	 * 	   java.math.BeigDecimal不能转换成long等错误
	 *  2. 如果不这样写的话,可能Bean中的field就得是大写的,如:name就得写成NAME,userCount就得写成USERCOUNT
	 *     这样是不是很扯(V_V)
	 * 
	 * @param <T>
	 * @param sqlQuery
	 *            SQLQuery
	 * @param clazz
	 *            T.class
	 * @param fieldList
	 *            要查询的成员变量名称
	 */
	public static <T> void addSclar(SQLQuery sqlQuery, Class<T> clazz, List<String> fieldList) {
		if (clazz == null) {
			throw new NullPointerException("[clazz] could not be null!");
		}

		if ((fieldList != null) && (fieldList.size() > 0)) {

			Field[] fields = clazz.getDeclaredFields();

			for (String fieldName : fieldList) {
				for (Field field : fields) {
					if (fieldName.equals(field.getName())) {
						if ((field.getType() == long.class) || (field.getType() == Long.class)) {
							sqlQuery.addScalar(field.getName(), Hibernate.LONG);
						} else if ((field.getType() == int.class) || (field.getType() == Integer.class)) {
							sqlQuery.addScalar(field.getName(), Hibernate.INTEGER);
						} else if ((field.getType() == char.class) || (field.getType() == Character.class)) {
							sqlQuery.addScalar(field.getName(), Hibernate.CHARACTER);
						} else if ((field.getType() == short.class) || (field.getType() == Short.class)) {
							sqlQuery.addScalar(field.getName(), Hibernate.SHORT);
						} else if ((field.getType() == double.class) || (field.getType() == Double.class)) {
							sqlQuery.addScalar(field.getName(), Hibernate.DOUBLE);
						} else if ((field.getType() == float.class) || (field.getType() == Float.class)) {
							sqlQuery.addScalar(field.getName(), Hibernate.FLOAT);
						} else if ((field.getType() == boolean.class) || (field.getType() == Boolean.class)) {
							sqlQuery.addScalar(field.getName(), Hibernate.BOOLEAN);
						} else if (field.getType() == String.class) {
							sqlQuery.addScalar(field.getName(), Hibernate.STRING);
						} else if (field.getType() == Date.class) {
							sqlQuery.addScalar(field.getName(), Hibernate.TIMESTAMP);
						}
					}
				}
			}
		}
	}
}

 

所有的准备工作都做好了,那么就看我们的具体的DAO操作及单元测试。

具体DAO

package com.sourcefour.dao;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Repository;

import com.sourcefour.bean.NativeSqlBean;
import com.sourcefour.dao.base.BaseDaoSupport;

/**
 * @author sourcefour
 */
@Repository
public class NativeSqlDao extends BaseDaoSupport<NativeSqlBean> {
	public List<NativeSqlBean> listAll(int offSet, int pageSize) {
		String sql = "SELECT MAX(t.mos) maxMos, MIN(t.mos) minMos, AVG(t.mos) avgMos, COUNT(t.id) userCount FROM t_native_sql t";

		List<String> fieldList = new ArrayList<String>();
		fieldList.add("maxMos");
		fieldList.add("minMos");
		fieldList.add("avgMos");
		fieldList.add("userCount");

		return super.list(sql, new Object[] {}, offSet, pageSize, NativeSqlBean.class, fieldList);
	}
}

 

说明:sql语句中的别名就是我们Bean中的字段

Junit测试代码:

package com.sourcefour.test.dao;

import java.util.List;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.sourcefour.bean.NativeSqlBean;
import com.sourcefour.dao.NativeSqlDao;

/**
 * @author sourcefour
 */
public class NativeSqlDaoTest {
	private static NativeSqlDao nativeSqlDao;
	private static ApplicationContext applicationContext;

	@BeforeClass
	public static void setUp() {
		applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		nativeSqlDao = (NativeSqlDao) applicationContext.getBean("nativeSqlDao");
	}

	@Test
	public void testListIntInt() {
		List<NativeSqlBean> list = nativeSqlDao.listAll(-1, -1);
		Assert.assertNotNull(list);
		for (NativeSqlBean nativeSqlBean : list) {
			System.out.println("maxMos: " + nativeSqlBean.getMaxMos());
		}
	}
}

 

运行结果:

hibernate将本地SQL查询结果封装成对象

到了这里应该说一切OK鸟。

完整的文档,SQL,工程下载地址:CSDN下载

说明:本说明是关于资源附件的,在资源中附件名称叫做 ‘hibernate将本地SQL查询结果封装成对象(最终)’,其实只有一版,这里只所以叫最终版是因为该附件我上传了好几天传不上去,到最后报告说‘资源已经存在’,但我确实没有看到附件。所以没办法只能改个名字了…………

。(V_V)。

PS:

Jdk1.6+

Junit4

工程编码:utf-8


转载请注明作者与出处