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

这样我们就把查询到的结果放到了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的语句为:

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

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

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

(0)

相关推荐

  • Java的Hibernate框架数据库操作中锁的使用和查询类型

     Hibernate与数据库锁 一.为什么要使用锁? 要想弄清楚锁机制存在的原因,首先要了解事务的概念. 事务是对数据库一系列相关的操作,它必须具备ACID特征: A(原子性):要么全部成功,要么全部撤销. C(一致性):要保持数据库的一致性. I(隔离性):不同事务操作相同数据时,要有各自的数据空间. D(持久性):一旦事务成功结束,它对数据库所做的更新必须永久保持. 我们常用的关系型数据库RDBMS实现了事务的这些特性.其中,原子性. 一致性和持久性都是采用日志来保证的.而隔离性就是由今天我

  • 扩展Hibernate使用自定义数据库连接池的方法

    本文实例讲述了扩展Hibernate使用自定义数据库连接池的方法.分享给大家供大家参考,具体如下: 在Hibernate的过程中往往碰到这样的问题:我们现成的产品本来已使用自有的数据库连接池,同时使用Hibernate的话在Hibernate配置中也得配置数据库连接信息,这样就需要在两个地方维护数据库连接信息,维护起来感觉相当别扭. 由于我们不是在产品刚开始开发就加入Hibernate的,所以不合适让产品直接使用Hibernate的连接池,只好让Hibernate来使用产品自有的连接池,还好Hi

  • 解决hibernate+mysql写入数据库乱码

    hibernate.cfg.xml加上属性. <property name="connection.useUnicode">true</property> <property name="connection.characterEncoding">UTF-8</property> mysql 的驱动用3.0.15以上版本的, 加个Filter, 使用UTF-8字符集就可以了, 若使用Spring则写在spring中的s

  • Java Hibernate中使用HQL语句进行数据库查询的要点解析

    一.实体对象查询 实体对象查询是hql查询的基础,作为一种对象查询语言,在查询操作时和sql不同,查询字符串中的内容要使用类名和类的属性名来代替.这种查询方法相对简单,只要有SQL功底,使用hql是很简单的,但是有一些问题需要注意,就是查询获取数据不是目的,需要考虑的是如何编写出高效的查询语句,这才是讨论的重点. 1.N+1问题 (1)什么是N+1问题 在刚听到这个名词时疑惑可能是有的,以前根本就没有听过N+1问题,那么它是指什么呢?N+1指的是一张表中有N条数据,那么在获取这N条数据时会产生N

  • 在Java的Hibernate框架中对数据库数据进行查询操作

    Hibernate查询语言(HQL)是一种面向对象的查询语言,类似于SQL,但不是对表和列操作,HQL适用于持久对象和它们的属性. HQL查询由Hibernate转换成传统的SQL查询,这在圈上的数据库执行操作. 虽然可以直接使用SQL语句和Hibernate使用原生SQL,但建议使用HQL尽可能避免数据库可移植性的麻烦,并采取Hibernate的SQL生成和缓存策略的优势. 都像SELECT,FROM和WHERE等关键字不区分大小写,但如表名和列名的属性是区分在HQL敏感. FROM 语句 使

  • Hibernate对数据库删除、查找、更新操作实例代码

    本节继续hibernate对数据库的其他操作,删除.查询.修改. Hibernate对数据删除操作 删除User表中个一条数据,是需要更具User表的主键id值来删除的.首先根据id值向数据库中查询出来对应的对象.可以采用两种方式一是session的get方法,一个是session的load方法. Session的Get方法:调用这个方法会返回一个Object对象.然后我们对其强制转换.Useruser = (User)session.get(User.class," 402881e5441c0

  • Hibernate通用数据库操作代码

    insert方法 复制代码 代码如下: public void insert(Object o){Session session = HibernateSessionFactory.currentSession();Transaction t = session.beginTransaction();session.save(o);t.commit();HibernateSessionFactory.clossSession();} delete方法 复制代码 代码如下: public void

  • Java的Hibernate框架中用于操作数据库的HQL语句讲解

    上次我们一起学习了用Criteria进行相关的操作,但由于Criteria并不是Hibernate官方推荐的查询方式,我们也并不多用.现在我们来看一下官方推荐的HQL,一起学习一下它的强大.  说是HQL,也就是Hibernate查询语句,和SQL有什么区别呢?一个字母的区别,哈哈.  当然不是这样,HQL和SQL的区别在于思想的不同,HQL是用面向对象的方向进行查询,而SQL则是对数据库二维表进行查询,这里包含的是思想的不同.HQL实际上也是SQL,它由Hibernate帮我们在内部进行转换,

  • 详解Java的Hibernate框架中的缓存与原生SQL语句的使用

    Hibernate缓存 缓存是所有关于应用程序的性能优化和它位于应用程序和数据库之间,以避免数据库访问多次,让性能关键型应用程序有更好的表现. 缓存对Hibernate很重要,它采用了多级缓存方案下文所述: 第一级缓存: 第一级缓存是Session的缓存,是一个强制性的缓存,通过它所有的请求都必须通过. Session对象不断自身的动力的对象,提交到数据库之前. 如果发出多个更新一个对象,Hibernate试图拖延尽可能长的时间做了更新,以减少发出的更新SQL语句的数量.如果您关闭会话,所有被缓

  • 详解Java的Hibernate框架中的注解与缓存

    注解 Hibernate注解是一个没有使用XML文件来定义映射的最新方法.可以在除或替换的XML映射元数据使用注解. Hibernate的注解是强大的方式来提供元数据对象和关系表的映射.所有的元数据被杵到一起的代码POJO java文件这可以帮助用户在开发过程中同时要了解表的结构和POJO. 如果打算让应用程序移植到其他EJB3规范的ORM应用程序,必须使用注解来表示映射信息,但仍然如果想要更大的灵活性,那么应该使用基于XML的映射去. 环境设置Hibernate注释 首先,必须确保使用的是JD

  • 深入解析Java的Hibernate框架中的持久对象

    一.持久对象生命周期 应用程序在使用Hibernate框架后,创建的持久对象会经历一整套生命周期来完成数据库的操作,其中主要的三个状态分别是瞬态(Transient).持久化(Persistent).脱管(detached).这三种状态的转换是能够在应用程序中控制的,如下图: 为了能清楚的了解这几种状态,这里使用一个实例来查看下这几种状态下对象的不同,下面状态内的代码,具体步骤如下: (1)创建Hibernate_session程序集,并添加像相应的jar包: (2)配置Hibernate,添加

  • 详解Java的Hibernate框架中的List映射表与Bag映射

    List映射表 List列表是一个java集合存储在序列中的元素,并允许重复的元素.此接口的用户可以精确地控制,其中列表中的每个元素插入.用户可以通过他们的整数索引访问元素,并搜索列表中的元素.更正式地说,列表通常允许对元素e1和e2,使得e1.equals(e2),它们通常允许多个null元素,如果他们允许的null元素. List列表被映射在该映射表中的<list>元素,并将java.util.ArrayList中初始化. 定义RDBMS表: 考虑一个情况,需要员工记录存储在EMPLOYE

  • 解析Java的Hibernate框架中的持久化类和映射文件

    持久化类 Hibernate的整个概念是采取从Java类属性的值,并将持久到数据库表.一个映射文件Hibernate的帮助确定如何从拉动类的值,并将它们映射与表和相关的域. 其对象或实例将存储在数据库表中的Java类在Hibernate中称为持久化类. Hibernate的效果最好,如果这些类遵循一些简单的规则,也称为普通Java对象(POJO)编程模型.有下列持久化类的主要规则,但是,这些规则并不是必需的. 将所有的持久化Java类需要一个默认的构造函数. 所有类应该包含为了让容易识别对象内H

  • 详解Java的Hibernate框架中的set映射集与SortedSet映射

    Set 集合Set是一个java集合不包含任何重复的元素.更正式地说,Set不包含任何元素对e1和e2,使得e1.equals(e2),和至多一个空元素.所以被添加到一组对象必须实现equals()和hashCode()方法,使Java可以判断任何两个元素/对象是否是相同的. 集被映射到与映射表中<set>元素,并在java.util.HashSet中初始化.可以使用Set集合在类时,有一个集合中不需要重复的元素. 定义RDBMS表: 考虑一个情况下,我们需要我们的员工记录存储在EMPLOYE

  • 举例讲解Java的Hibernate框架中的多对一和一对多映射

    多对一(Many-to-One)映射 多对一(many-to-one)关联是最常见的关联关系,其中一个对象可以与多个对象相关联.例如,一个相同的地址对象可以与多个雇员的对象相关联. 定义RDBMS表: 考虑一个情况,我们需要员工记录存储在EMPLOYEE表,将有以下结构: create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20

  • Java的Hibernate框架中的组合映射学习教程

    一.组合映射 组合是关联关系的一种特殊情况,是关联关系耦合度最高的一种关系,组合的主对象和子对象拥有相同的生命周期,主对像消亡的话子对象也会消亡.这里使用雇主和用户作为示例,用户和雇主都拥有联系方式属性,如果这里站在对象角度思考的话,常常会把对象模型绘制成为组合的方式,抽象出来一个共同的联系方式类,然后两种人分别包含相应的联系方式对象即可,向应的对象模型时它的对象示例如下图所示: 组合对象模型在生成相应的关系模型后会把对应的子类包含到主表中,所以对应的表结构会将相应的属性生成到对应的表中,相应的

  • Java的Hibernate框架中的双向主键关联与双向外键关联

    一.双向主键关联 双向的主键关联其实是单向一对一主键关联的一种特殊情况,只不过要在关联对象的两端的映射文件中都要进行<one-to-one>的配置,另外还要在主映射的主键一端采用foreign外键关联属性. 这里同样使用Person和IdCard来讨论,一个人对应着一个唯一的身份证,而且一个身份证也唯一映射着一个人,所以这就产生了双向的关联关系,Person的主键同样也是IdCard的主键,分别是主键的同时也是外键,这种关联关系成为双向一对一映射,表现到关系模型中可如下图: 图中的两个表采用了

随机推荐