SSH整合后,HQL查询必须知道的内容,HibernateTemplate中实现分页

时间:2021-12-22 22:17:06

前言

在之前笔者一直对于HQL查询是很不满意的,因为它不仅不能够实现分页查询,而且通过select attr1,attr2,attr3 from User,查询出来的还都是Object类型的,取值的时候非常不愉快。

HQL有选择的的查询出对象类里面的值:

第一种方式:我想大家都很熟悉下面这种查询方式,查询出来的值都是对象:

    @Test
public void showFromAll(){
Session session=HibernateUtils.getSessionFactory().openSession();
Query query=session.createQuery("from User");
List results=query.list();
for(Object i:results){
System.out.println(i);
}
}

SSH整合后,HQL查询必须知道的内容,HibernateTemplate中实现分页
第二种方式:我想大家都很苦恼下面这种查询值的方式,查询出来的值都是Object类型的:

    @Test
public void showTeam(){
Session session=HibernateUtils.getSessionFactory().openSession();
Query query=session.createQuery("select uid,uname,pword from User");
List results=query.list();
for(Object i:results){
System.out.println("##########开始");
Object []x=(Object[])i;
for(Object j:x){
System.out.println(j);
}
System.out.println("##########结束");
}
}

SSH整合后,HQL查询必须知道的内容,HibernateTemplate中实现分页

第三种方式:这就是笔者要补充的内容,有选择的的查询出类里面的值:

    @Test
public void showUserTeam(){
Session session=HibernateUtils.getSessionFactory().openSession();
Query query=session.createQuery("select new User(uname,pword) from User");
List result=query.list();
for(Object i:result){
System.out.println(i);
}
}

SSH整合后,HQL查询必须知道的内容,HibernateTemplate中实现分页

而且是要什么查什么:

SSH整合后,HQL查询必须知道的内容,HibernateTemplate中实现分页

注意!注意!但是这种查询方式 必须有相应的构造方法:

    public User(String uname, String pword) {
super();
this.uname = uname;
this.pword = pword;
}

最后我要说个效率问题:

对于 第一种 方式查询出来的是持久化对象,它们会被置于Hibernate的Session缓存之中,并且Session会负责它们在缓存中的唯一性以及与后台数据库数据的同步,只有事务提交后它们才会从缓存中被清除;

对于后面的两种 方式返回的是关系数据而并非是持久化对象,因此它们不会占用Hibernate的Session缓存,只要在检索之后应用程序不在访问它们,它们所占用的内存就有可能被JVM的垃圾回收器回收,而且Hibernate不会同步对它们的修改。在我们的系统开发中,尤其是Mis系统,不可避免的要进行统计查询的开发,这类功能有两个特点:第一数据量大;第二一般情况下都是只读操作而不会涉及到对统计数据进行修改,那么如果采用第一种查询方式,必然会导致大量持久化对象位于Hibernate的Session缓存中,而且Hibernate的Session缓存还要负责它们与数据库数据的同步。而如果采用后面的两种查询方式,显然就会提高查询性能,因为不需要Hibernate的Session缓存的管理开销,而且只要应用程序不在使用这些数据,它们所占用的内存空间就会被回收释放。因此在开发统计查询系统时,尽量使用通过select语句写出需要查询的属性的方式来返回关系数据,而避免使用第一种查询方式返回持久化对象(这种方式是在有修改需求时使用比较适合),这样可以提高运行效率并且减少内存消耗。㊣真正的高手并不是精通一切,而是精通在合适的场合使用合适的手段。

HibernateTemplate中实现分页

在SSH整合后,使用查询时,基本上都用的是HibernateTemplate对象,而这个对象没有直接提供分页的功能。没有以前的setFirstResult()和setMaxResults()方法了。

网上很多人都用的是实现HibernateCallback这个接口实现分页。笔者看别人用的这种方式,我也就用这种方式了,听我老师说,他用的离线对象(DetachedCriteria)来进行的分页查询。而我感觉用这个类很麻烦,缺少灵活,所以就用的网上这种方法:

网上方法:通过HibernateTemplate.execute(回调对象)来执行回调对象里面的方法实现分页。要是回调类必须实现HibernateCallback这个接口。

这是笔者实现HibernateCallback的类:

@Component//把类写出来后我就直接让Spring容器来进行管理这个类的对象了。
public class HibernateCallBackCutePageImpl<T> implements HibernateCallback<List<T>>{

//当我们用这个类来分页查询的时候,我们都会在不同的dao中注入该对象,比如UserDao,BookDao等等。
//也就是说不同的dao里面就可以有不同的类型,为了更面向对象,所以我就对返回值加了个泛型
private String hql;
private int firstResult,offset;
private Object [] args;

public void setHql(String hql) {
this.hql = hql;
}

public void setFirstResult(int firstResult) {
this.firstResult = firstResult;
}

public void setOffset(int offset) {
this.offset = offset;
}
//初始化参数
public void init(String hql,int firstResult,int offset,Object ...args){
setHql(hql);
setFirstResult(firstResult);
setOffset(offset);
this.args=args;
}
//重写接口的方法
@Override
public List<T> doInHibernate(Session session) throws HibernateException {
Query<T> query=session.createQuery(hql);
//给hql里面的绑定参数赋值
int len=args.length;
for(int i=0;i<len;i++){
query.setParameter(i, args[i]);
}
//实现分页
query.setFirstResult(firstResult).setMaxResults(offset);
//把结果返回
return query.list();
}

}

测试代码:

    public void showCutePage(){
String hql="select new User(uname,pword) from User";
hibernateCallBackCutePageImpl.init(hql, 0, 3);//该回调类对象已经通过Spring注入到当前测试类里面了。
List<User> results=hibernateTemplate.execute(hibernateCallBackCutePageImpl);
for(User i:results){
System.out.println(i);
}
}

参数绑定

Hibernate中对动态查询参数绑定提供了丰富的支持,那么什么是查询参数动态绑定呢?其实如果我们熟悉传统JDBC编程的话,我们就不难理解查询参数动态绑定,
如下代码传统JDBC的参数绑定:
PrepareStatement pre=connection.prepare(“select * from User where user.name=?”);
pre.setString(1,”zhaoxin”);
ResultSet rs=pre.executeQuery();
在Hibernate中也提供了类似这种的查询参数绑定功能,而且在Hibernate中对这个功能还提供了比传统JDBC操作丰富的多的特性,在Hibernate*存在4种参数绑
定的方式,下面我们将分别介绍:
A、 按参数名称绑定:
在HQL语句中定义命名参数要用”:”开头,形式如下:
Query query=session.createQuery(“from User user where user.name=:customername and user.customerage=:age ”);
query.setString(“customername”,name);
query.setInteger(“customerage”,age);
上面代码中用:customername和:customerage分别定义了命名参数customername和customerage,然后用Query接口的setXXX()方法设定名参数值,setXXX()方法包
含两个参数,分别是命名参数名称和命名参数实际值。
B、 按参数位置邦定:
在HQL查询语句中用”?”来定义参数位置,形式如下:
Query query=session.createQuery(“from User user where user.name=? and user.age =? ”);
query.setString(0,name);
query.setInteger(1,age);
同样使用setXXX()方法设定绑定参数,只不过这时setXXX()方法的第一个参数代表邦定参数在HQL语句中出现的位置编号(由0开始编号),第二个参数仍然代表参
数实际值。
注:在实际开发中,提倡使用按名称邦定命名参数,因为这不但可以提供非常好的程序可读性,而且也提高了程序的易维护性,因为当查询参数的位置发生改变时
,按名称邦定名参数的方式中是不需要调整程序代码的。
C、 setParameter()方法:
在Hibernate的HQL查询中可以通过setParameter()方法邦定任意类型的参数,如下代码:
String hql=”from User user where user.name=:customername ”;
Query query=session.createQuery(hql);
query.setParameter(“customername”,name,Hibernate.STRING);
如上面代码所示,setParameter()方法包含三个参数,分别是命名参数名称,命名参数实际值,以及命名参数映射类型。对于某些参数类型setParameter()方法可
以更具参数值的Java类型,猜测出对应的映射类型,因此这时不需要显示写出映射类型,像上面的例子,可以直接这样写:
query.setParameter(“customername”,name);但是对于一些类型就必须写明映射类型,比如java.util.Date类型,因为它会对应Hibernate的多种映射类型,比如
Hibernate.DATA或者Hibernate.TIMESTAMP。
D、 setProperties()方法:
在Hibernate中可以使用setProperties()方法,将命名参数与一个对象的属性值绑定在一起,如下程序代码:
Customer customer=new Customer();
customer.setName(“pansl”);
customer.setAge(80);
Query query=session.createQuery(“from Customer c where c.name=:name and c.age=:age ”);
query.setProperties(customer);
setProperties()方法会自动将customer对象实例的属性值匹配到命名参数上,但是要求命名参数名称必须要与实体对象相应的属性同名。
这里还有一个特殊的setEntity()方法,它会把命名参数与一个持久化对象相关联,如下面代码所示:
Customer customer=(Customer)session.load(Customer.class,”1”);
Query query=session.createQuery(“from Order order where order.customer=:customer ”);
query. setProperties(“customer”,customer);
List list=query.list();
上面的代码会生成类似如下的SQL语句:
Select * from order where customer_ID=’1’;