作者微信 bishe2022

代码功能演示视频在页面下方,请先观看;如需定制开发,联系页面右侧客服
Java的Hibernate框架中用于操作数据库的HQL语句讲解

Custom Tab

上次我们一起学习了用Criteria进行相关的操作,但由于Criteria并不是Hibernate官方推荐的查询方式,我们也并不多用。现在我们来看一下官方推荐的HQL,一起学习一下它的强大。
 说是HQL,也就是Hibernate查询语句,和SQL有什么区别呢?一个字母的区别,哈哈。
 当然不是这样,HQL和SQL的区别在于思想的不同,HQL是用面向对象的方向进行查询,而SQL则是对数据库二维表进行查询,这里包含的是思想的不同。HQL实际上也是SQL,它由Hibernate帮我们在内部进行转换,生成SQL。
 1)废话不多说,我们直接看一下它的强大。
from User 
  这个代码很熟悉吧,因为我们在SQL中经常也用到from 表名,但这里有点不同的是User在这里并不是表名,而是实体类的名称,由hibernate帮我们进行映射。
 
 联想SQL语句,如果我们想查出某个属性,并且根据某个属性进行条件限制,很简单可以得到类似语句:

1    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

这样我们就把查询到的结果放到了User对象中,注意,这里调用的是User的构造函数,User类中必须存在接收两个参数的User构造函数,否则会报错,错误信息大概如下:

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的语句为:

1    Hibernate: insert into USER (USER_NAME, age) values (?, ?)

  它新插入一条,而不是更新。
 那么如果我们需要它进行更新的时候就需要把ID一起查出:

1    select new User(usr.name,usr.age,usr.id) from User usr where usr.age > (select avg(usr.age

  记得修改User构造方法。
 这时我们再执行我们的测试代码,此时会得到:

1    Hibernate: update USER set USER_NAME=?, age=? where USER_ID=?

)我们可以在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表数据如下:

1111.png

22222.png

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

 我们在mysql中运行可以看到结果:

33333.png

我们可以看到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

 

似乎语句没什么区别,但是当我们查出来后它得到的是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

  我们在mysql中进行查出,看到:

4444444444.png

我们看到,尽管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

 我们在mysql中执行后可以看到结果:

555555555555555.png

这里我们可以看到address为Test4的并没有相应的user与它对应,但它还是并查出来了,right outer join是指把右边表的记录全部查出。
 fetch的情况如上,如果不明白可以看一下inner join fetch。



转载自:http://www.jb51.net/article/77377.htm











Home