hibernate查询语言hql
在hql中关键字不区分大小写,但是属性和类名区分大小写
1、简单属性查询【重要】
* 单一属性查询,返回结果集属性列表,元素类型和实体类中相应的属性类型一致
* 多个属性查询,返回的集合元素是对象数组,数组元素的类型和对应的属性在实体类中的类型一致
数组的长度取决与select中属性的个数
* 如果认为返回数组不够对象化,可以采用hql动态实例化Student对象
- //返回结果集属性列表,元素类型和实体类中相应的属性类型一致
- List students = session.createQuery("select name from Student").list();
- //查询多个属性,其集合元素是对象数组
- //数组元素的类型和对应的属性在实体类中的类型一致
- //数组的长度取决与select中属性的个数
- List students = session.createQuery("select id, name from Student").list();
- //如果认为返回数组不够对象化,可以采用hql动态实例化Student对象
- //此时list中为Student对象集合
- List students = session.createQuery("select new Student(id, name) from Student").list();
- //可以使用别名
- List students = session.createQuery("select s.id, s.name from Student s").list();
- //可以使用as命名别名
- List students = session.createQuery("select s.id, s.name from Student as s").list();
2、实体对象查询【重要】
* N + 1问题,在默认情况下,使用query.iterate查询,有可以能出现N+1问题
所谓的N+1是在查询的时候发出了N+1条sql语句
1: 首先发出一条查询对象id列表的sql
N: 根据id列表到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的sql语句
* list和iterate的区别?
* list每次都会发出sql语句,list会向缓存中放入数据,而不利用缓存中的数据
* iterate:在默认情况下iterate利用缓存数据,但如果缓存中不存在数据有可以能出现N+1问题
- //返回Student对象的集合
- //可以忽略select
- List students = session.createQuery("from Student").list();
- //返回Student对象的集合
- //可以忽略select,表可以使用别名
- List students = session.createQuery("from Student s").list();
- //返回Student对象的集合
- //可以忽略select,表可以使用as命名别名
- List students = session.createQuery("from Student as s").list();
- //返回Student对象的集合
- //使用select查询实体对象,必须采用别名
- List students = session.createQuery("select s from Student as s").list();
- //不支持select * from .....这样的查询语句
- List students = session.createQuery("select * from Student").list();
- /**
- * 采用list查询发出一条查询语句,取得Student对象数据、
- *
- * Hibernate: select student0_.id as id1_, student0_.name as name1_,
- * student0_.createTime as createTime1_, student0_.classesid as classesid1_
- * from t_student student0_
- *
- */
- List students = session.createQuery("from Student").list();
- /**
- * 出现N+1问题
- *
- * 1:发出查询id列表的sql
- * Hibernate: select student0_.id as col_0_0_ from t_student student0_
- *
- * N:在依次发出根据id查询Student对象的sql
- * Hibernate: select student0_.id as id1_0_, student0_.name as name1_0_,
- * student0_.createTime as createTime1_0_, student0_.classesid as classesid1_0_
- * from t_student student0_ where student0_.id=?
- *
- */
- Iterator iter = session.createQuery("from Student").iterate();
- /**
- * 不会出现N+1问题
- *
- * 因为list操作已经将Student对象放到了一级缓存中,所以再次使用iterate操作的时候
- * 它首先发出一条查询id列表的sql,在根据id到缓存中去数据,只有在缓存中找不到相应的
- * 数据时,才会发出sql到<a href="http://lib.csdn.net/base/mysql" class='replace_word' title="MySQL知识库" target='_blank' style='color:#df3434; font-weight:bold;'>数据库</a>中查询
- *
- */
- /**
- * 再次发出查询sql
- *
- * 在默认情况下list每次都会向数据库发出查询对象的sql,除非配置查询缓存,所以下面的list操作
- * 虽然在一级缓存中已经有了对象数据,但list默认情况下不会利用缓存,而再次发出sql
- *
- * 默认情况下,list会向缓存中放入数据,但不会利用数据
- *
- */
- students = session.createQuery("from Student").list();
3、条件查询【重要】
* 可以采用拼字符串的方式传递参数
* 可以采用 ?来传递参数(索引从0开始)
* 可以采用 :参数名 来传递参数
* 如果传递多个参数,可以采用setParamterList方法
* 在hql中可以使用数据库的函数,如:date_format
- //可以拼字符串
- List students = session.createQuery("select s.id, s.name from Student s where s.name like '%1%'").list();
- //Query query = session.createQuery("select s.id, s.name from Student s where s.name like ?");
- //query.setParameter(0, "%1%");
- //List students = query.list();
- //可以使用?方式传递参数
- //参数的索引从0开始
- //传递的参数值,不用单引号引起来
- //注意方法链编程
- List students = session.createQuery("select s.id, s.name from Student s where s.name like ?")
- .setParameter(0, "%1%")
- .list();
- //使用 :参数名称 的方式传递参数值
- List students = session.createQuery("select s.id, s.name from Student s where s.name like :myname")
- .setParameter("myname", "%1%")
- .list();
- //使用 :参数名称 的方式传递参数值
- List students = session.createQuery("select s.id, s.name from Student s where s.name like :myname and s.id=:myid")
- .setParameter("myname", "%1%")
- .setParameter("myid", 12)
- .list();
- //支持in,需要使用setParameterList进行参数传递
- List students = session.createQuery("select s.id, s.name from Student s where s.id in(:myids)")
- .setParameterList("myids", new Object[]{1, 2, 3, 4, 5})
- .list();
- //查询2008年2月创建的学生
- List students = session.createQuery("select s.id, s.name from Student s where date_format(s.createTime, '%Y-%m')=?")
- .setParameter(0, "2008-02")
- .list();
- //查询2008-01-10到2008-02-15创建的学生
- List students = session.createQuery("select s.id, s.name from Student s where s.createTime between ? and ?")
- .setParameter(0, sdf.parse("2008-01-10 00:00:00"))
- .setParameter(1, sdf.parse("2008-02-15 23:59:59"))
- .list();
4、hibernate也支持直接使用sql进行查询
- List students = session.createSQLQuery("select * from t_student").list();
- for (Iterator iter=students.iterator(); iter.hasNext();) {
- Object[] obj = (Object[])iter.next();
- System.out.println(obj[0] + "," + obj[1]);
- }
5、外置命名查询
* 在映射文件中采用<query>标签来定义hql
* 在程序中采用session.getNamedQuery()方法得到hql查询串
参见:Student.hbm.xml、NameQueryTest.java
- <query name="searchStudents">
- <!--[CDATA[
- SELECT s FROM Student s where s.id<?
- ]]-->
- </query>
- List students = session.getNamedQuery("searchStudents")
- .setParameter(0, 10)
- .list();
6、查询过滤器
* 在映射文件中定义过滤器参数
* 在类的映射中使用这些参数
* 在程序中启用过滤器
- <class name="com.bjsxt.hibernate.Student" table="t_student">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <property name="createTime"/>
- <many-to-one name="classes" column="classesid"/>
- <filter name="filtertest" condition="id < :myid"/>
- </class>
- <filter-def name="filtertest">
- <filter-param name="myid" type="integer"/>
- </filter-def>
- session.enableFilter("filtertest").setParameter("myid", 10);
- List students = session.createQuery("from Student").list();
7、分页查询【重要】
* setFirstResult(),从0开始
* setMaxResults,每页显示多少条数据
参见:PageQueryTest.java
- List students = session.createQuery("from Student")
- .setFirstResult(1)
- .setMaxResults(2)
- .list();
8、对象导航查询,在hql中采用 . 进行导航【重要】
参见:ObjectNavQueryTest.java
- List students = session.createQuery("select s.name from Student s where s.classes.name like '%1%'").list();
9、连接查询【重要】
* 内连
* 外连接(左连接/右连接)
- //List students = session.createQuery("select c.name, s.name from Student s join s.classes c").list();
- List students = session.createQuery("select c.name, s.name from Student s inner join s.classes c").list();
10、统计查询【重要】
参见:StatQueryTest.java
- //List students =session.createQuery("select count(*) from Student").list();
- //Long count = (Long)students.get(0);
- //System.out.println(count);
- Long count = (Long)session.createQuery("select count(*) from Student").uniqueResult();
11、DML风格的操作(尽量少用,因为和缓存不同步)
- session.createQuery("update Student s set s.name=? where s.id < ?")
- .setParameter(0, "李四")
- .setParameter(1, 5)
- .executeUpdate();
12.get/load
一.load加载方式
当使用load方法来得到一个对象时,此时hibernate会使用延迟加载的机制来加载这个对象,即:当我们使用session.load()方法来加载一个对象时,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,只有当我们要使用这个对象,得到其它属性时,这个时候才会发出sql语句,从数据库中去查询我们的对象。
session = HibernateUtil.openSession(); /* * 通过load的方式加载对象时,会使用延迟加载机制,此时并不会发出sql语句,只有当我们需要使用的时候才会从数据库中去查询 */ User user = (User)session.load(User.class, 2);
我们看到,如果我们仅仅是通过load来加载我们的User对象,此时从控制台我们会发现并不会从数据库中查询出该对象,即并不会发出sql语句,但如果我们要使用该对象时:
session = HibernateUtil.openSession(); User user = (User)session.load(User.class, 2); System.out.println(user);
此时我们看到控制台会发出了sql查询语句,会将该对象从数据库中查询出来:
Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.born as born0_0_ from user user0_ where user0_.id=?
User [id=2, username=aaa, password=111, born=2013-10-16 00:14:24.0]
这个时候我们可能会想,那么既然调用load方法时,并不会发出sql语句去从数据库中查出该对象,那么这个User对象到底是个什么对象呢?
其实这个User对象是我们的一个代理对象,这个代理对象仅仅保存了id这个属性:
session = HibernateUtil.openSession(); /* * 通过load的方式加载对象时,会使用延迟加载机制,此时得到的User对象其实是一个 * 代理对象,该代理对象里面仅仅只有id这个属性 */ User user = (User)session.load(User.class, 2); System.out.println(user.getId()); console: 2
我们看到,如果我们只打印出这个user对象的id值时,此时控制台会打印出该id值,但是同样不会发出sql语句去从数据库中去查询。这就印证了我们的这个user对象仅仅是一个保存了id的代理对象,但如果我需要打印出user对象的其他属性值时,这个时候会不会发出sql语句呢?答案是肯定的:
session = HibernateUtil.openSession(); /* * 通过load的方式加载对象时,会使用延迟加载机制,此时得到的User对象其实是一个 * 代理对象,该代理对象里面仅仅只有id这个属性 */ User user = (User)session.load(User.class, 2); System.out.println(user.getId()); // 如果此时要得到user其他属性,则会从数据库中查询 System.out.println(user.getUsername());
此时我们看控制台的输出:
2 Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.born as born0_0_ from user user0_ where user0_.id=? aaa
相信通过上述的几个例子,大家应该很好的了解了load的这种加载对象的方式了吧。
二、get加载方式
相对于load的延迟加载方式,get就直接的多,当我们使用session.get()方法来得到一个对象时,不管我们使不使用这个对象,此时都会发出sql语句去从数据库中查询出来:
session = HibernateUtil.openSession(); /* * 通过get方法来加载对象时,不管使不使用该对象,都会发出sql语句,从数据库中查询 */ User user = (User)session.get(User.class, 2);
此时我们通过get方式来得到user对象,但是我们并没有使用它,但是我们发现控制台会输出sql的查询语句:
Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.born as born0_0_ from user user0_ where user0_.id=?
因此我们可以看到,使用load的加载方式比get的加载方式性能要好一些,因为load加载时,得到的只是一个代理对象,当真正需要使用这个对象时再去从数据库中查询。
三、使用get和load时的一些小问题
当了解了load和get的加载机制以后,我们此时来看看这两种方式会出现的一些小问题:
①如果使用get方式来加载对象,当我们试图得到一个id不存在的对象时,此时会报NullPointException的异常
session = HibernateUtil.openSession(); /* * 当通过get方式试图得到一个id不存在的user对象时,此时会报NullPointException异常 */ User user = (User)session.get(User.class, 20); System.out.println(user.getUsername());
此时我们看控制台的输出信息,会报空指针的异常:
Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.born as born0_0_ from user user0_ where user0_.id=?
java.lang.NullPointerException .........
这是因为通过get方式我们会去数据库中查询出该对象,但是这个id值不存在,所以此时user对象是null,所以就会报NullPointException的异常了。
②如果使用load方式来加载对象,当我们试图得到一个id不存在的对象时,此时会报ObjectNotFoundException异常:
session = HibernateUtil.openSession(); /* * 当通过get方式试图得到一个id不存在的user对象时,此时会报ObjectNotFoundException异常 */ User user = (User)session.load(User.class, 20); System.out.println(user.getId()); System.out.println(user.getUsername());
我们看看控制台的输出:
20 Hibernate: select user0_.id as id0_0_, user0_.username as username0_0_, user0_.password as password0_0_, user0_.born as born0_0_ from user user0_ where user0_.id=? org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.xiaoluo.bean.User#20]......
为什么使用load的方式和get的方式来得到一个不存在的对象报的异常不同呢??其原因还是因为load的延迟加载机制,使用load时,此时的user对象是一个代理对象,仅仅保存了当前的这个id值,当我们试图得到该对象的username属性时,这个属性其实是不存在的,所以就会报出ObjectNotFoundException这个异常了。
③org.hibernate.LazyInitializationException异常
接下来我们再来看一个例子:
public class UserDAO { public User loadUser(int id) { Session session = null; Transaction tx = null; User user = null; try { session = HibernateUtil.openSession(); tx = session.beginTransaction(); user = (User)session.load(User.class, 1); tx.commit(); } catch (Exception e) { e.printStackTrace(); tx.rollback(); } finally { HibernateUtil.close(session); } return user; } }
@Test public void testLazy06() { UserDAO userDAO = new UserDAO(); User user = userDAO.loadUser(2); System.out.println(user); }
模拟了一个UserDAO这样的对象,然后我们在测试用例里面来通过load加载一个对象,此时我们发现控制台会报LazyInitializationException异常
org.hibernate.LazyInitializationException: could not initialize proxy - no Session .............
这个异常是什么原因呢??还是因为load的延迟加载机制,当我们通过load()方法来加载一个对象时,此时并没有发出sql语句去从数据库中查询出该对象,当前这个对象仅仅是一个只有id的代理对象,我们还并没有使用该对象,但是此时我们的session已经关闭了,所以当我们在测试用例中使用该对象时就会报LazyInitializationException这个异常了。
所以以后我们只要看到控制台报LazyInitializationException这种异常,就知道是使用了load的方式延迟加载一个对象了,解决这个的方法有两种,一种是将load改成get的方式来得到该对象,另一种是在表示层来开启我们的session和关闭session。
13
Hibernate中createCriteria即QBC查询的详细用法
现在假设有一个Student类,内有id,name,age属性
String hql = "from Student s";
按照以前的做法,我们通常是
Query query = session.createQuery(hql);
或者要按照条件检索的话.
String hql = "from Student s where s.name like '王%'"
Query query = session.createQuery(hql);
不用HQL而使用QBC的话,那么代码为:
Criteria criteria =session.createCriteria(Student. class);
Criterion criterion = Expression.like( "name", "王%");
这样还看不出来.那我们把检索条件加上年龄为22.
HQL:
String hql = "from Student s where s.name like '王%' and s.age= 22 ";
Query query = session.createQuery(hql);
List list = query.list();
QBC:
Criteria criteria =session.createCriteria(Student. class);
Criterion criterion1 = Expression.like( "name", "王%");
Criterion criterion2 = Expression.eq( "age",newInteger(22));
criteria.add(criterion1).add(criterion2);
List list = criteria.list();
看上去烦琐很多.但是做过项目的人都知道,当一个模块业务逻辑发生改变的时候,往往要重写sql,最烦也是最讨厌的就是拿着别人的hql或者sql,两眼瞪的溜园找到底要修改什么地方呢?
如果使用QBC大大的增加了代码的可读性,以及可维护性.
需要注意的是 null值
比如我们要查找姓名为 null的Student对象时应该这么写
Criteria criteria =session.createCriteria(Student. class);
Criterion criterion = Expression.isNull( "name");
criteria.add(criterion).list();
以及使用between...and的时候
Criteria criteria =session.createCriteria(Student. class);
Criterion criterion1 = Expression.ge( "age", new Integer(20)); //下限
Criterion criterion2 = Expression.le( "age", new Integer(25)); //上限
//这里也可以把上述两个条件添加到第三个条件里
Criterion criterion3 =Expression.and(criterion1,criterion2);
criteria.add(criterion3).list();
相当于from Student s where s.age between 20 and 25
等同于from Student s where s.age >= 20 and s.age <=25
下面是就HQL和QBC常用的查询条件做的比较
表达式含义 HQL QBC
大于等于 >= Expression.ge()
大于 > Expression.gt()
小于等于 <= Expression.le()
小于 < Expression.lt()
等于 = Expression.eq()
不等于 <>或者!= Expression.ne()
为空 is null Expression.isNull()
不为空 is notnull Expression.isNotNull()
在指定范围内 betweenand Expression.between()
不在指定范围 not betweenand Expression.not(Expression.between())
属于某个集合 in Expression.in()
不属于某个集合 notin Expression.not(Expression.in())
与 and Expression.and()
或 or Expression.or()
非 not Expression.not()
模糊查询 like Expression.like