Java的Hibernate框架中用于操作数据库的HQL语句讲解
上次我们一起学习了用criteria进行相关的操作,但由于criteria并不是hibernate官方推荐的查询方式,我们也并不多用。现在我们来看一下官方推荐的hql,一起学习一下它的强大。
说是hql,也就是hibernate查询语句,和sql有什么区别呢?一个字母的区别,哈哈。
当然不是这样,hql和sql的区别在于思想的不同,hql是用面向对象的方向进行查询,而sql则是对数据库二维表进行查询,这里包含的是思想的不同。hql实际上也是sql,它由hibernate帮我们在内部进行转换,生成sql。
1)废话不多说,我们直接看一下它的强大。
from user
这个代码很熟悉吧,因为我们在sql中经常也用到from 表名,但这里有点不同的是user在这里并不是表名,而是实体类的名称,由hibernate帮我们进行映射。
联想sql语句,如果我们想查出某个属性,并且根据某个属性进行条件限制,很简单可以得到类似语句:
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是面向对象的,而我们这样做,就不是面向对象的思想了。我们来改一下:
select new user(usr.name,usr.age) from user usr where usr.age > 20
unable to locate appropriate constructor on class [org.hibernate.tutorial.domain8.user]
它找不到合适的构造函数。很明白,加上接收对应参数的构造函数就可以了。
注意,上面当我们进行查出的时候并没有查出相应的id,如果此时我们调用saveorupdate方法时,它实际上执行的是保存的操作。
我们看一下测试代码:
我在执行完上面的查询语句后,进行下面的操作:
while(iter.hasnext()) { user user = (user)iter.next(); user.setname("sun2"); session.saveorupdate(user); }
这时hibernate的语句为:
hibernate: insert into user (user_name, age) values (?, ?)
它新插入一条,而不是更新。
那么如果我们需要它进行更新的时候就需要把id一起查出:
select new user(usr.name,usr.age,usr.id) from user usr where usr.age > (select avg(usr.age) from usr)
记得修改user构造方法。
这时我们再执行我们的测试代码,此时会得到:
hibernate: update user set user_name=?, age=? where user_id=?
3)我们可以在hql语句中加上sql函数:
select usr.name from user usr where usr.age > (select avg(usr.age) from usr)
这段hql查出年龄大于平均年龄的user的name。
4)在hibernate 3中我们可以很方便地更新和删除对象,而不必像2中需要先load然后再delete,我们可以直接一条语句搞定:
update user set name='123123' where name='sun33'
删除语句类似:
delete user where name='123123'
5)hibernate中也可以方便地进行分组和排序,只要运用group by 和 order by 即可,这时不多讲了。
6)我们看到上面都是直接把值写入进行查询或更新的,如果我们需要动态赋值,或赋值的太多,总不能跟jdbc一样用字符串拼接吧,估计超过5个,项目组的人都想骂娘了,呵呵。
还是用着现代化的方法,用占位符来代替然后再设置具体值。
我们直接代码:
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并不多说它了。
上面我们用的这种占位符叫顺序占位符,另外有一种叫引用占位符的,我们来看一下:
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语句中的占位符的值。
当然,也许有人又会说,这个不面向对象,那么我们就又来面向对象一把:
首先弄一个类来封装我们查询的值
public class userquery { private string name; private int age; //省略get/set方法 } 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);
我们在代码从直接通过此类进行封装我们需要查询的值。很面向对象吧。
有些项目组有一些奇怪的规定,不许在代码中出现sql语句,如果这是一个规范,那我见过的我们公司的代码,全部都是不合格的,杯具的一大堆字符串拼接,看着就郁闷啊。维护现有项目的人真是伤不起啊。
代码中不允许出现sql语句,这是建议是不错,但还是要看场合。我们来看一下hibernate怎么把hql配置在映射文件中。
直接看配置文件:
<query name="querybyname"> <![cdata[ from user usr where usr.name=:name ]]> </query>
我们添加了一个这样的标签,它表明里面是hql语句。
当我们需要取到这个语句时,也只需要在代码中加入一句:
query query = session.getnamedquery("querybyname");
这样也就取到了这个hql语句。
hql也可以用sql中的组合查询,比如inner join,left outer join,right outer join,full join。
下面我们来看一下它们的用法:
还是先看一下实体类,我们测试中要用到的:
public class tuser implements serializable{ private static final long serialversionuid = 1l; private int id; private int age; private string name; private set<address> addresses = new hashset<address>(); //省略get/set方法 } public class address implements serializable{ private static final long serialversionuid = 1l; private int id; private string address; private tuser user; //省略get/set方法 }
下面我们看一下映射文件:
<hibernate-mapping package="org.hibernate.tutorial.domain6"> <class name="tuser" table="t_user" dynamic-insert="true" dynamic-update="true"> <id name="id" column="id"> <generator class="native" /> </id> <property name="name" type="java.lang.string" column="name"/> <property name="age" type="java.lang.integer" column="age"/> <set name="addresses" cascade="all" table="t_address" inverse="true"> <key column="user_id" /> <one-to-many class="address"/> </set> </class> </hibernate-mapping> <hibernate-mapping package="org.hibernate.tutorial.domain6"> <class name="address" table="t_address" dynamic-insert="false" dynamic-update="false"> <id name="id" column="id" type="java.lang.integer"> <generator class="native" /> </id> <property name="address" column="address" type="java.lang.string" /> <many-to-one name="user" class="tuser" column="user_id" not-null="true"></many-to-one> </class> </hibernate-mapping>
大家只要做一下相应的包名修改就可以了。
下面我们正式进行测试:
在测试前我们看一下表中的数据:
t_address表数据如下:
t_user表数据如下:
1)首先我们看一下inner join,它在hql中由inner join fetch,注意这里fetch的意思是指把需要的数据取出来,如果不用fetch,我们取出来的数据是object[]数据类型的。
我们先看一下
from tuser usr inner join fetch usr.addresses
当我们运行它时,我们看到hibernate输出为:
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时,它打印的语句为:
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时,我们需要这样来解析:
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]); } }
我们看到打印的结果:
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的左连接,我们直接看一下例子:
from tuser usr left outer join fetch usr.addresses
当我们运行上面的语句时,hibernate打印出:
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有点关系的,我们直接看例子就可以明显看出了。
from tuser usr right outer join fetch usr.addresses
我们执行它,得到hibernate输出的结果语句为:
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
这里我们可以看到address为test4的并没有相应的user与它对应,但它还是并查出来了,right outer join是指把右边表的记录全部查出。
fetch的情况如上,如果不明白可以看一下inner join fetch。
上一篇: Asp.net防止盗链的实现原理分析
下一篇: (七)C++基础之运算符重载(一)
推荐阅读
-
Java的Hibernate框架中用于操作数据库的HQL语句讲解
-
详解Java的Hibernate框架中的缓存与原生SQL语句的使用
-
在Java的Hibernate框架中对数据库数据进行查询操作
-
举例讲解Java的Hibernate框架中的多对一和一对多映射
-
在Java的Hibernate框架中对数据库数据进行查询操作
-
Java的Hibernate框架数据库操作中锁的使用和查询类型
-
Java的Hibernate框架数据库操作中锁的使用和查询类型
-
在Java的Hibernate框架中对数据库数据进行查询操作
-
详解Java的Hibernate框架中的缓存与原生SQL语句的使用
-
详解Java的Hibernate框架中的缓存与原生SQL语句的使用