Hibernate查询之hql

loverqiao 贡献于2013-08-23

作者 LJ  创建于2011-05-29 23:40:00   修改者LJ  修改于2011-05-29 23:41:00字数10818

文档摘要:上次我们一起学习了用Criteria进行相关的操作,但由于Criteria并不是Hibernate官方推荐的查询方式,我们也并不多用。现在我们来看一下官方推荐的HQL,一起学习一下它的强大。说是HQL,也就是Hibernate查询语句,和SQL有什么区别呢?一个字母的区别,哈哈。
关键词:

上次我们一起学习了用Criteria进行相关的操作,但由于Criteria并不是Hibernate官方推荐的查询方式,我们也并不多用。现在我们来看一下官方推荐的HQL,一起学习一下它的强大。 说是HQL,也就是Hibernate查询语句,和SQL有什么区别呢?一个字母的区别,哈哈。 当然不是这样,HQL和SQL的区别在于思想的不同,HQL是用面向对象的方向进行查询,而SQL则是对数据库二维表进行查询,这里包含的是思想的不同。HQL实际上也是SQL,它由Hibernate帮我们在内部进行转换,生成SQL。 1)废话不多说,我们直接看一下它的强大。 Java代码   1. from User   from User   这个代码很熟悉吧,因为我们在SQL中经常也用到from 表名,但这里有点不同的是User在这里并不是表名,而是实体类的名称,由hibernate帮我们进行映射。   联想SQL语句,如果我们想查出某个属性,并且根据某个属性进行条件限制,很简单可以得到类似语句: Java代码   1. select usr.name,usr.age from User where usr.age > 20 and usr.age < 60   select usr.name,usr.age from User where usr.age > 20 and usr.age < 60   这样我们就查出了年龄大于20且小于60的User的姓名和年龄。很容易理解。 SQL语句中的and,or,like,<,>,=等都可以在HQL中进行使用。 需要注意的是当我们查询多个属性时,返回的结果是一个Object[]数组,而只有单个时是返回Object,这个需要不同的解析方式,所以在查询时需要注意。   2)当然,我们前面说了HQL是面向对象的,而我们这样做,就不是面向对象的思想了。我们来改一下: Java代码   1. select new User(usr.name,usr.age) from User usr where usr.age > 20   select new User(usr.name,usr.age) from User usr where usr.age > 20   这样我们就把查询到的结果放到了User对象中,注意,这里调用的是User的构造函数,User类中必须存在接收两个参数的User构造函数,否则会报错,错误信息大概如下: Java代码   1. Unable to locate appropriate constructor on class [org.hibernate.tutorial.domain8.User]    Unable to locate appropriate constructor on class [org.hibernate.tutorial.domain8.User]   它找不到合适的构造函数。很明白,加上接收对应参数的构造函数就可以了。 注意,上面当我们进行查出的时候并没有查出相应的ID,如果此时我们调用saveOrUpdate方法时,它实际上执行的是保存的操作。 我们看一下测试代码: 我在执行完上面的查询语句后,进行下面的操作: Java代码   1. while(iter.hasNext()) {    2.     User user = (User)iter.next();    3.     user.setName("sun2");    4.     session.saveOrUpdate(user);    5. }   while(iter.hasNext()) { User user = (User)iter.next(); user.setName("sun2"); session.saveOrUpdate(user); }   这时Hibernate的语句为: Java代码   1. Hibernate: insert into USER (USER_NAME, age) values (?, ?)   Hibernate: insert into USER (USER_NAME, age) values (?, ?)   它新插入一条,而不是更新。 那么如果我们需要它进行更新的时候就需要把ID一起查出: Java代码   1. select new User(usr.name,usr.age,usr.id) from User usr where usr.age > (select avg(usr.age) from usr)   select new User(usr.name,usr.age,usr.id) from User usr where usr.age > (select avg(usr.age) from usr)   记得修改User构造方法。 这时我们再执行我们的测试代码,此时会得到: Java代码   1. Hibernate: update USER set USER_NAME=?, age=? where USER_ID=?   Hibernate: update USER set USER_NAME=?, age=? where USER_ID=?     3)我们可以在HQL语句中加上SQL函数: Java代码   1. select usr.name from User usr where usr.age > (select avg(usr.age) from usr)   select usr.name from User usr where usr.age > (select avg(usr.age) from usr)   这段HQL查出年龄大于平均年龄的User的name。   4)在Hibernate 3中我们可以很方便地更新和删除对象,而不必像2中需要先load然后再delete,我们可以直接一条语句搞定: Java代码   1. update User set name='123123' where name='sun33'   update User set name='123123' where name='sun33'   删除语句类似: Java代码   1. delete User where name='123123'   delete User where name='123123'   5)Hibernate中也可以方便地进行分组和排序,只要运用group by 和 order by 即可,这时不多讲了。   6)我们看到上面都是直接把值写入进行查询或更新的,如果我们需要动态赋值,或赋值的太多,总不能跟JDBC一样用字符串拼接吧,估计超过5个,项目组的人都想骂娘了,呵呵。 还是用着现代化的方法,用占位符来代替然后再设置具体值。 我们直接代码: Java代码   1. Query query = session.createQuery("select new User(usr.name,usr.age,usr.id) from User usr where usr.name=?");    2. query.setString(0,"shun");   Query query = session.createQuery("select new User(usr.name,usr.age,usr.id) from User usr where usr.name=?"); query.setString(0,"shun");   我们看到这种方法跟我们直接用的PreparedStatement类似,都是通过set***进行设值的,但不同的是,这里的position从0开始,而PreparedStatement从1开始,这里要特别注意。 Hibernate2中还有session.find这种方法的,但由于现在用的是3并不多说它了。 上面我们用的这种占位符叫顺序占位符,另外有一种叫引用占位符的,我们来看一下: Java代码   1. Query query = session.createQuery("select new User(usr.name,usr.age,usr.id) from User usr where usr.name=:name");    2. query.setParameter("name","shun");   Query query = session.createQuery("select new User(usr.name,usr.age,usr.id) from User usr where usr.name=:name"); query.setParameter("name","shun");   看到我们HQL语句当中有一个:name这样的东西,这个就是引用占位符,我们只需要在后面通过setParameter进行设值即可,注意这里的第一个参数需要对应HQL语句中的占位符的值。 当然,也许有人又会说,这个不面向对象,那么我们就又来面向对象一把: 首先弄一个类来封装我们查询的值 Java代码   1. public class UserQuery {    2.    3.     private String name;    4.     private int age;    5.         //省略Get/Set方法    6.         7. }   public class UserQuery { private String name; private int age; //省略Get/Set方法 } Java代码   1. Query query = session.createQuery("select new User(usr.name,usr.age,usr.id) from User usr where usr.name=:name");    2.    3. UserQuery uq = new UserQuery();    4. uq.setName("shun");    5.             6. query.setProperties(uq);   Query query = session.createQuery("select new User(usr.name,usr.age,usr.id) from User usr where usr.name=:name"); UserQuery uq = new UserQuery(); uq.setName("shun"); query.setProperties(uq);   我们在代码从直接通过此类进行封装我们需要查询的值。很面向对象吧。   HQL我们就先接触到这里,以后还有更多高级我们慢慢学习。 我们上次一起学习HQL,知道了怎么使用HQL,现在我们继续来学习一下HQL的其他方面,如通过配置文件来进行配置。 有些项目组有一些奇怪的规定,不许在代码中出现SQL语句,如果这是一个规范,那我见过的我们公司的代码,全部都是不合格的,杯具的一大堆字符串拼接,看着就郁闷啊。维护现有项目的人真是伤不起啊。 代码中不允许出现SQL语句,这是建议是不错,但还是要看场合。我们来看一下Hibernate怎么把HQL配置在映射文件中。 直接看配置文件: Xml代码   1.    2.        5.      我们添加了一个这样的标签,它表明里面是HQL语句。 当我们需要取到这个语句时,也只需要在代码中加入一句: Java代码   1. Query query = session.getNamedQuery("queryByName");   Query query = session.getNamedQuery("queryByName");   这样也就取到了这个HQL语句。   HQL也可以用SQL中的组合查询,比如inner join,left outer join,right outer join,full join。 下面我们来看一下它们的用法: 还是先看一下实体类,我们测试中要用到的: Java代码   1. public class TUser implements Serializable{    2.    3.     private static final long serialVersionUID = 1L;    4.    5.     private int id;    6.     private int age;    7.     private String name;    8.     private Set
 addresses = new HashSet
();    9.         //省略Get/Set方法    10. }   public class TUser implements Serializable{ private static final long serialVersionUID = 1L; private int id; private int age; private String name; private Set
addresses = new HashSet
(); //省略Get/Set方法 } Java代码   1. public class Address implements Serializable{    2.    3.     private static final long serialVersionUID = 1L;    4.    5.     private int id;    6.     private String address;    7.     private TUser user;    8.         //省略Get/Set方法    9. }   public class Address implements Serializable{ private static final long serialVersionUID = 1L; private int id; private String address; private TUser user; //省略Get/Set方法 }   下面我们看一下映射文件: Xml代码   1.    2.        3.            4.                5.            6.            7.            8.            9.                10.                11.            12.        13.    Java代码   1.    2.         3.             4.                 5.             6.             7.             9.         10.      大家只要做一下相应的包名修改就可以了。 下面我们正式进行测试: 在测试前我们看一下表中的数据: t_address表数据如下: t_user表数据如下: 1)首先我们看一下inner join,它在HQL中由inner join fetch,注意这里fetch的意思是指把需要的数据取出来,如果不用fetch,我们取出来的数据是Object[]数据类型的。 我们先看一下 Java代码   1. from TUser usr inner join fetch usr.addresses   from TUser usr inner join fetch usr.addresses   当我们运行它时,我们看到hibernate输出为: Java代码   1. Hibernate: select tuser0_.id as id1_0_, addresses1_.id as id0_1_, tuser0_.name as name1_0_, tuser0_.age as age1_0_, addresses1_.address as address0_1_, addresses1_.user_id as user3_0_1_, addresses1_.user_id as user3_0__, addresses1_.id as id0__ from t_user tuser0_ inner join t_address addresses1_ on tuser0_.id=addresses1_.user_id   Hibernate: select tuser0_.id as id1_0_, addresses1_.id as id0_1_, tuser0_.name as name1_0_, tuser0_.age as age1_0_, addresses1_.address as address0_1_, addresses1_.user_id as user3_0_1_, addresses1_.user_id as user3_0__, addresses1_.id as id0__ from t_user tuser0_ inner join t_address addresses1_ on tuser0_.id=addresses1_.user_id   我们在mysql中运行可以看到结果:   我们可以看到hibernate将它转换成inner join语句,并查出address。 我们看到结果中并没有shun4这个记录,因为他并没有相应的address与它记录。   而我们用inner join而不要fetch时,它打印的语句为: Java代码   1. Hibernate: select tuser0_.id as id1_0_, addresses1_.id as id0_1_, tuser0_.name as name1_0_, tuser0_.age as age1_0_, addresses1_.address as address0_1_, addresses1_.user_id as user3_0_1_ from t_user tuser0_ inner join t_address addresses1_ on tuser0_.id=addresses1_.user_id   Hibernate: select tuser0_.id as id1_0_, addresses1_.id as id0_1_, tuser0_.name as name1_0_, tuser0_.age as age1_0_, addresses1_.address as address0_1_, addresses1_.user_id as user3_0_1_ from t_user tuser0_ inner join t_address addresses1_ on tuser0_.id=addresses1_.user_id   似乎语句没什么区别,但是当我们查出来后它得到的是Object[]数组类型的,这个解析的时候需注意。   当我们不用fetch,而只是inner join时,我们需要这样来解析: Java代码   1. Query query = session.createQuery("from TUser usr inner join usr.addresses");    2.    3. List list = query.list();    4. Iterator iter = list.iterator();    5.             6. while(iter.hasNext()) {    7.        Object[] results = (Object[])iter.next();    8.        for (int i = 0; i < results.length; i ++ ) {    9.         System.out.println(results[i]);    10.        }    11. }   Query query = session.createQuery("from TUser usr inner join usr.addresses"); List list = query.list(); Iterator iter = list.iterator(); while(iter.hasNext()) { Object[] results = (Object[])iter.next(); for (int i = 0; i < results.length; i ++ ) { System.out.println(results[i]); } }   我们看到打印的结果: Java代码   1. org.hibernate.tutorial.domain6.TUser@16925b0   2. org.hibernate.tutorial.domain6.Address@914f6a   3. org.hibernate.tutorial.domain6.TUser@787d6a   4. org.hibernate.tutorial.domain6.Address@71dc3d   5. org.hibernate.tutorial.domain6.TUser@1326484   6. org.hibernate.tutorial.domain6.Address@16546ef   org.hibernate.tutorial.domain6.TUser@16925b0 org.hibernate.tutorial.domain6.Address@914f6a org.hibernate.tutorial.domain6.TUser@787d6a org.hibernate.tutorial.domain6.Address@71dc3d org.hibernate.tutorial.domain6.TUser@1326484 org.hibernate.tutorial.domain6.Address@16546ef   它的每个结果都是相应查出来的对象。   2)left outer join,这个相当于SQL的左连接,我们直接看一下例子: Java代码   1. from TUser usr left outer join fetch usr.addresses   from TUser usr left outer join fetch usr.addresses   当我们运行上面的语句时,hibernate打印出: Java代码   1. Hibernate: select tuser0_.id as id1_0_, addresses1_.id as id0_1_, tuser0_.name as name1_0_, tuser0_.age as age1_0_, addresses1_.address as address0_1_, addresses1_.user_id as user3_0_1_, addresses1_.user_id as user3_0__, addresses1_.id as id0__ from t_user tuser0_ left outer join t_address addresses1_ on tuser0_.id=addresses1_.user_id   Hibernate: select tuser0_.id as id1_0_, addresses1_.id as id0_1_, tuser0_.name as name1_0_, tuser0_.age as age1_0_, addresses1_.address as address0_1_, addresses1_.user_id as user3_0_1_, addresses1_.user_id as user3_0__, addresses1_.id as id0__ from t_user tuser0_ left outer join t_address addresses1_ on tuser0_.id=addresses1_.user_id   我们在mysql中进行查出,看到:   我们看到,尽管shun4没有对应的adress,但还是把它查出来,left outer join是指把左边表的记录全部查出。 没有fetch的情况这里就不讲了。   3)接下来我们看一下right outer join,看名字肯定就和left outer join有点关系的,我们直接看例子就可以明显看出了。 Java代码   1. from TUser usr right outer join fetch usr.addresses   from TUser usr right outer join fetch usr.addresses   我们执行它,得到Hibernate输出的结果语句为: Java代码   1. Hibernate: select tuser0_.id as id1_0_, addresses1_.id as id0_1_, tuser0_.name as name1_0_, tuser0_.age as age1_0_, addresses1_.address as address0_1_, addresses1_.user_id as user3_0_1_, addresses1_.user_id as user3_0__, addresses1_.id as id0__ from t_user tuser0_ right outer join t_address addresses1_ on tuser0_.id=addresses1_.user_id   Hibernate: select tuser0_.id as id1_0_, addresses1_.id as id0_1_, tuser0_.name as name1_0_, tuser0_.age as age1_0_, addresses1_.address as address0_1_, addresses1_.user_id as user3_0_1_, addresses1_.user_id as user3_0__, addresses1_.id as id0__ from t_user tuser0_ right outer join t_address addresses1_ on tuser0_.id=addresses1_.user_id   我们在mysql中执行后可以看到结果:   这里我们可以看到address为Test4的并没有相应的user与它对应,但它还是并查出来了,right outer join是指把右边表的记录全部查出。 fetch的情况如上,如果不明白可以看一下inner join fetch。   4)接下来我们看最后一个full join,这个用得比较少,我们就大概看一下: 实际上前面两个理解了,这个就不难理解了,只是把前面两个的结果结合一下而已。 我们直接看例子: Java代码   1. from TUser usr full join fetch usr.addresses   from TUser usr full join fetch usr.addresses   我们执行它,很抱歉,不管怎样执行,我们都会得到一个错误: Java代码   1. org.hibernate.AssertionFailure: undefined join type 23   org.hibernate.AssertionFailure: undefined join type 23   这个错误暂时没找到解决方法,可能是hibernate的bug,等以后我们深入源代码时再重回来看看。     HQL当然也是可以用子查询和多表联合查询的啦,这个就不多讲了。 HQL我们就学习到这里啦,以后会有更多高级的,我们再继续研究。 · · 大小: 3.8 KB · · 大小: 3.6 KB · · 大小: 6 KB · · 大小: 7.5 KB · · 大小: 7.4 KB · 查看图片附件 我们在之前一起学习了Hibernate的HQL查询语法。但我们用得比较熟的还是数SQL语句,那么应该怎么来让Hibernate支持SQL呢?这个不用我们去考虑了,Hibernate团队已经早就做好了。        废话不说,直接来例子啦。 Java代码   1. select * from t_user usr   select * from t_user usr   上面是一条SQL语句,又是废话,是个人都知道。我们想让Hibernate执行这条语句,怎么办呢?看代码: Java代码   1. Query query = session.createSQLQuery("select * from t_user usr");   Query query = session.createSQLQuery("select * from t_user usr");   就这样,剩下来的,大家应该都知道了,平常的查询。         那查询完之后,返回的是什么东西呢? Java代码   1. while(iter.hasNext()){    2.     Object[] objs = (Object[])iter.next();    3.     for (int i = 0; i < objs.length; i++) {    4.         System.out.print(objs[i]);    5.     }    6.     System.out.println();    7. }   while(iter.hasNext()){ Object[] objs = (Object[])iter.next(); for (int i = 0; i < objs.length; i++) { System.out.print(objs[i]); } System.out.println(); }   返回的每个结果都是Object[]数组,                这时又有人跑出来说面向对象啦。对,就是面向对象,唉,没办法。         我们继续看: Java代码   1. select {usr.*} from t_user usr   select {usr.*} from t_user usr   看到这里,估计某些童鞋开始鸡动啦,那个大括号什么东西啦?     别急,慢慢来。我们先继续看代码。 Java代码   1. Query query = session.createSQLQuery("select {usr.*} from t_user usr").addEntity(TUser.class);   Query query = session.createSQLQuery("select {usr.*} from t_user usr").addEntity(TUser.class);   我们看到跟前面不同的是,我们加了一个addEntity,是什么意思呢?     我们直接API,看到这样一串解释:     addEntity SQLQuery addEntity(String tableAlias,                    Class entityType) Declare a "root" entity Parameters: tableAlias - The SQL table alias entityType - The java type of the entity to add as a root        有跟没一个样,杯具。只能自己动手用用。        第一个参数是指表的别名,就像上面的语句,我们表的别名是usr,所以第一个参数为usr,而第二个是指查询到的结果需要映射到哪个类,这里由于我们在映射文件中是通过TUser映射到t_user表,所以我们这里当然也就是TUser啦。然后一查,有SQL语句出来,而且查到的结果是TUser类型的。        我们查到的结果是: Java代码   1. org.hibernate.tutorial.domain6.TUser@198cb3d   org.hibernate.tutorial.domain6.TUser@198cb3d   当然,你们的肯定跟我不一样的。不要鸡动。       也许我们并不需要全部进行查出,这时,我们需要的只是设定别名即可: Java代码   1. select u.id as {usr.id},u.name as {usr.name},u.age as {usr.age} from t_user u   select u.id as {usr.id},u.name as {usr.name},u.age as {usr.age} from t_user u   我们看到我们用了as指定了字段的别名,程序中还是一样: Java代码   1. Query query = session.createSQLQuery("select u.id as {usr.id},u.name as {usr.name},u.age as {usr.age} from t_user u").addEntity("usr",TUser.class);   Query query = session.createSQLQuery("select u.id as {usr.id},u.name as {usr.name},u.age as {usr.age} from t_user u").addEntity("usr",TUser.class);   这个简单,不多说。            以前我们讲到,有些团队会规定不给在代码中写SQL语句,这又需要配置文件出马啦。          我们可以在配置文件中添加: Xml代码   1.    2.        3.     select {usr.*} from t_user usr where name=:name    4.    select {usr.*} from t_user usr where name=:name   注意,这里的entity-name需要写完整的包名,不然会报错的。这里我们有子标签return,它指定了表的别名和类名,这样我们在运行时就不需要再addEntity了。     看代码: Java代码   1. Query query = session.getNamedQuery("queryTUser");    2. query.setParameter("name","shun");    3. List list = query.list();    4. Iterator iter = list.iterator();   Query query = session.getNamedQuery("queryTUser"); query.setParameter("name","shun"); List list = query.list(); Iterator iter = list.iterator();   我们直接这样就OK了,注意,我们并没有加addEntity了,主要还是归功于配置文件中的配置。     注意,如果在配置文件中配置,一定要有return子标签指定表别名和类名。这个主要是避免了我们读取语句时的重复判断。           上面讲了这么久,我们一直在讲有别名的表,那么如果我们的表没有别名,但又想返回的结果封装在对象内,我们应该怎样呢? Java代码   1. select * from t_user usr   select * from t_user usr   很简单,只要调用addEntity的重载方法addEntity(Class clazz)就行了,只需要提供一个类名,而不需要表别名。       当然,hibernate也支持存储过程,只需要在配置文件中把sql-query的callable属性设为true即表示当前调用的是存储过程,由于存储过程我接触地不多,以后多研究一下再跟大家一起研究。         我们在调用session.save等相应的对数据操作的方法时,它会转换成hibernate内置的SQL语句,但如果我们想自己控制SQL语句的格式呢,怎么办?     Hibernate实际上也想到了。     我们直接在映射文件中加入: Xml代码   1.    2.         INSERT INTO T_USER (NAME,AGE) values (?,?)    3.        4.        5.         UPDATE USER SET USER_NAME=?,AGE=? WHERE uSER_ID=?    6.     INSERT INTO T_USER (NAME,AGE) values (?,?) UPDATE USER SET USER_NAME=?,AGE=? WHERE uSER_ID=?   注意,这个需要添加在class标签内,作为子标签。我们这里全部是大写字母,是为了跟hibernate默认的语句分清,没有其他意思。     我们先来看一下insert的调用: Java代码   1. User user = new User();     2. user.setName("shun123123");    3. user.setAge(23);   User user = new User(); user.setName("shun123123"); user.setAge(23);   当我们调用保存时,hibernate的语句是: Java代码   1. Hibernate: INSERT INTO USER(USER_NAME,AGE) values(?,?)   Hibernate: INSERT INTO USER(USER_NAME,AGE) values(?,?)   它调用了我们配置的sql-insert标签内的语句 我们再来看一下update的调用:   Java代码   1. User user = (User)session.get(User.class,new Long(29));    2. user.setName("shun123123");    3. user.setAge(23);    4. session.save(user);   User user = (User)session.get(User.class,new Long(29)); user.setName("shun123123"); user.setAge(23); session.save(user);   我们调用保存,它会自动调用更新,此时的语句是: Java代码   1. Hibernate: UPDATE USER SET USER_NAME=?,AGE=? WHERE uSER_ID=?   Hibernate: UPDATE USER SET USER_NAME=?,AGE=? WHERE uSER_ID=?   我们看到输出的语句是大写的,也就是调用了我们配置的语句。       delete语句也是同样的配置。   我们这些基础的就学到这里啦,接下来的我们就会一起学习一下"高级"的东西。  

下载文档到电脑,查找使用更方便

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 2 金币 [ 分享文档获得金币 ] 0 人已下载

下载文档