在Hibernate整个查询体系中,存在以下几种查询语言:NativeSQL、HQL、EJBQL(JPQL)、QBC、QBE。他们之间的关系式NativeSQL>HQL>EJBQL(JPQL)>QBC>QBE。可以简单认为后者为前者的子集,NativeSQL功能最为强大。下面对于在Hibernate中用的查询需要注意的几点做一个总结:
第一:NativeSQL 与本地数据库有关,由于是针对具体数据库,所以不可以跨越数据库平台,但是也是功能最为强大和最为灵活的。一般在做本地报表系统的时候,会大量的使用NativeSQL。
第二:HQL,Hibernate Query Language ,这种语句在执行的时候,在系统内部会根据设置的Hibernate方言将其转化为具体的本地SQL。
第三:EJBQL(JPQL),95%以上的时候,都是使用的这个查询语言,而且可以跨越ORMapping平台。而且EJBQL是导航性质的。
第四:QL应该与导航关系相结合,共同为查询提供服务。
第五:关于分页的实现:Oracle中使用三层嵌套实现,SQL使用Limit,而在HQL中使用的setMaxResult()和setFirstResult()两个方法来实现。
第六:一个面试题:JAVA中有没有内存泄露?其实,java在语法级别是没有内存泄露的,但是在实际运用中,会存在内存泄露的情况,比如这样一个场景,在一个有上千万条数据的表中,不断分页取出数据,试想一下,每次取出一百条,如果处理完成之后,不手动的执行session.clear()清理内存,这个时候由于内存中有数据的引用,这个时候,垃圾收集器是不会主动去回收内存,这样,在不断的分页取出数据的时候,一定时间以后,内存就会被占满。还有一个问题需要说明是:为什么java打开的文件一定要手动关闭?原因是JAVA打开一个文件操作实际是调用了底层的C来完成的,然后C再去调用windows API执行相关的操作,但是虽然JAVA有内存垃圾回收机制,但是C却没有这种机制。所以,JAVA打开的文件一定要手动的去关闭。
下面就常用的QL用法做一个总结说明:
1:HQL采用面向对象的思维,直接通过类取对象:比如 Query query = session.createQuery("from Category");
2:从面向对象的角度,在1的基础上添加各种过滤条件:比如 Query q = session.createQuery("from Category c where c.name > 'c5'");
3:从面向对象的角度,在1的基础上添加排序:Query q = session.createQuery("from Category c order by c.name desc");
4:过滤重复值:Query q = session.createQuery("select distinct c from Category c order by c.name desc");
5:设定过滤条件参数(两种方式可选 ? 和 :xxx):
5.1:Query q = session.createQuery("from Category c where c.id > :min and c.id < :max order by c.name desc").setParameter("min", 3).setParameter("max", 6);
5.2:Query q = session.createQuery("from Category c where c.id > ? and c.id < ? order by c.name desc").setParameter(0, 3).setParameter(1, 6);
6:只选取对象中的特定列组成对象数组:
Query q = session.createQuery("select c.id , c.name from Category c");
List<Object[]> categories = (List<Object[]>)q.list();
7:通过对象导航实现查询:Query q = session.createQuery("from Topic t where t.category.id = 1");8:将查询出来的值组装成VO对象:比如:Query q = session.createQuery("select new com.zxb.model.MsgInfo(m.id, m.cont, m.topic.title, m.topic.category.name) from Msg m");注意,这里的MsgInfo对象在数据库中是不存在对应表的,只是程序中用来完成特定功能的一个辅助工具类,比如VO ,DTO一般都属于这类对象。
9:表之间的链接查询:比如:Query q = session.createQuery("select t.title, c.name from Topic t join t.category c");注意,这里的t.category c不可以是Category c,因为在Topic 可能存在多个类型为Category的属性,这样写的话就不知道到底是以那个属性作为链接条件使用了。必须通过t.属性名的形式指定具体是那个属性作为链接条件。
10:UniqueResult的使用:使用的过滤条件可以是传入的一个对象。示例代码如下:
Query q = session.createQuery("from Msg m where m = :MsgToSearch "); //不重要
Msg m = new Msg();
m.setId(1);
q.setParameter("MsgToSearch", m);
Msg mResult = (Msg)q.uniqueResult();
11:在HQL中,虽然从面向对象的角度考虑,但是sql中很多特性依旧可用,比如,聚合函数,between , in 等,例如:
Query q = session.createQuery("select count(*) from Msg m");
Query q = session.createQuery("select max(m.id), min(m.id), avg(m.id), sum(m.id) from Msg m");
Query q = session.createQuery("from Msg m where m.id between 3 and 5");
Query q = session.createQuery("from Msg m where m.id in (3,4, 5)");
Query q = session.createQuery("from Topic t where t.title like '_5'");
Query q = session.createQuery("select lower(t.title)," + "upper(t.title)," +"trim(t.title)," +"concat(t.title, '***')," + "length(t.title)" + "
from Topic t ");
Query q = session.createQuery("select t.title ,count(*) from Topic t group by t.title");
Query q = session.createQuery("from Topic t where exists (select m.id from Msg m where m.topic.id=t.id)") ;
12:null 、not null、 empty 、 not empty的说明,在HQL中,null 和 not null 是判断对象的非集合属性是否为空,而empty 和 not empty使用来判断对象的集合属性是否有值,即集合的长度是否是0,而不是说空与非空,概念完全不一样。
Query q = session.createQuery("from Msg m where m.cont is not null"); 判断m的cont属性是否是空值。
Query q = session.createQuery("from Topic t where t.msgs is empty");判断t这个对象的集合属性msgs的大小是否是0。
13:特别说明一个HQL语句:
Query q = session.createQuery("select current_date,current_time,current_timestamp ,t.id from Topic t");
这个hql语句中,取得的是数据库的当前日期,时间以及时间戳信息,一般我们会认为,服务器在数据库服务器中取得这些当前信息有意义吗?服务器去取得自己的这些信息不就ok了吗?但是如果说在集群环境下,这个时候,多个服务器需要通过时间信息相互协作,这个时候,有可能各个服务器的时间信息并不是一致的,这个时候就会出问题,但是如果都以数据库服务器中的时间信息为准的话,就不会存在问题。
14:命名sql: 这种方式,类似于配置文件的配置方式,先配好语句,用到的时候填充条件。比如。我们在要查询的类上加上如下配置:(目前为止:JAP还不支持@NameNativeQuery.)
@Entity
@NamedQueries({
@NamedQuery(name="topicQuery",query="from Topic t where t.id = :id")
})
/*@NamedNativeQueries({
@NamedNativeQuery(name="topicNativeQuery", query="select * from topic limit 2, 5")
})*/
public class Topic {
private int id;
private String title;
@Id
@GeneratedValue
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public void setId(int id) {
this.id = id;
}
public void setTitle(String title) {
this.title = title;
}
}
由于我们配置了一条根据ID来查询Topic类实例的命名语句,所以程序中每次我们要根据id取得topic实例的时候,我们就不需要每次都写HQL语句了。只需要取得这个命名QL语句,然后填充条件即可。如下代码所示:@Test
public void testNameQuery() {
Session session = this.sessionFactory.openSession();
session.beginTransaction();
Query q = session.getNamedQuery("topicQuery");
q.setParameter("id", 5);
Topic t = (Topic)q.uniqueResult();
System.out.println(t.getTitle());
session.getTransaction().commit();
session.close();
}
15:QBE查询方式示例代码:public void testQBE(){
Session session = this.sessionFactory.openSession();
session.beginTransaction();
Topic topic = new Topic();
topic.setTitle("T_");
Example example = Example.create(topic).enableLike().ignoreCase();
Criteria criteria = session.createCriteria(Topic.class)
.add(Restrictions.ge("id", 2))
.add(Restrictions.le("id", 9))
.add(example);
List<Topic> topics = (List<Topic>)criteria.list();
for(Topic t :topics ){
System.out.println(t.getTitle());
}
session.getTransaction().commit();
session.close();
}
16:QBC查询方式示例代码:public void testQBC(){
Session session = this.sessionFactory.openSession();
session.beginTransaction();
Criteria criteria = session.createCriteria(Topic.class)
.add(Restrictions.gt("id", 2))
.add(Restrictions.lt("id", 8))
.add(Restrictions.like("title", "t_"))
.createCriteria("category")
.add(Restrictions.between("id",1,5));
List<Topic> topics = (List<Topic>)criteria.list();
for(Topic t :topics ){
System.out.println(t.getTitle());
}
session.getTransaction().commit();
session.close();
}