我们上次一起学习HQL,知道了怎么使用HQL,现在我们继续来学习一下HQL的其他方面,如通过配置文件来进行配置。
有些项目组有一些奇怪的规定,不许在代码中出现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
我们在mysql中执行后可以看到结果:
这里我们可以看到address为Test4的并没有相应的user与它对应,但它还是并查出来了,right outer join是指把右边表的记录全部查出。
fetch的情况如上,如果不明白可以看一下inner join fetch。
4)接下来我们看最后一个full join,这个用得比较少,我们就大概看一下:
实际上前面两个理解了,这个就不难理解了,只是把前面两个的结果结合一下而已。
我们直接看例子:
from TUser usr full join fetch usr.addresses
我们执行它,很抱歉,不管怎样执行,我们都会得到一个错误:
org.hibernate.AssertionFailure: undefined join type 23
这个错误暂时没找到解决方法,可能是hibernate的bug,等以后我们深入源代码时再重回来看看。
HQL当然也是可以用子查询和多表联合查询的啦,这个就不多讲了。
HQL我们就学习到这里啦,以后会有更多高级的,我们再继续研究。