Hibernate之HQL基本用法

Posted 云中之歌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate之HQL基本用法相关的知识,希望对你有一定的参考价值。

关于HQL

HQL与SQL非常类似,只不过SQL的操作对象是数据表,列等对象,而HQL操作的是持久化类,实例,属性等。

HQL是完全面向对象的查询语言,因此也具有面向对象的继承,多态等特性。

使用HQL的一般步骤为:

获取session对象

编写HQL语句

使用session的createQuery方法创建查询对象(Query对象)

使用SetXxx(index/para_name, value)为参数复制

使用Query对象的list()方法返回查询结果列表(持久化实体集)

下面演示一下HQL的基本用法,演示之前先附上之前的一个例子,双向N-N关联映射,

假设有下面两个持久化类Person和Event之间成N-N双向关联,代码如下,

Person类

 1 package hql;
 2 
 3 import java.util.*;
 4 
 5 import javax.persistence.*;
 6 
 7 
 8 @Entity
 9 @Table(name = "person_inf")
10 public class Person
11 {
12     @Id @Column(name = "person_id")
13     @GeneratedValue(strategy=GenerationType.IDENTITY)
14     private Integer id;
15     private String name;
16     private int age;
17     @ManyToMany(cascade=CascadeType.ALL, targetEntity=MyEvent.class)
18     @JoinTable(name = "person_event" ,
19         joinColumns = @JoinColumn(name = "person_id"
20             , referencedColumnName="person_id"),
21         inverseJoinColumns = @JoinColumn(name = "event_id"
22             , referencedColumnName="event_id")
23     )
24     private Set<MyEvent> myEvents
25         = new HashSet<>();
26     @ElementCollection(targetClass=String.class)
27     @CollectionTable(name="person_email_inf",
28         joinColumns=@JoinColumn(name="person_id" , nullable=false))
29     @Column(name="email_detail" , nullable=false)
30     private Set<String> emails
31         = new HashSet<>();
32 
33     public void setId(Integer id)
34     {
35         this.id = id;
36     }
37     public Integer getId()
38     {
39         return this.id;
40     }
41 
42     public void setName(String name)
43     {
44         this.name = name;
45     }
46     public String getName()
47     {
48         return this.name;
49     }
50 
51     public void setAge(int age)
52     {
53         this.age = age;
54     }
55     public int getAge()
56     {
57         return this.age;
58     }
59 
60     public void setMyEvents(Set<MyEvent> myEvents)
61     {
62         this.myEvents = myEvents;
63     }
64     public Set<MyEvent> getMyEvents()
65     {
66         return this.myEvents;
67     }
68 
69     public void setEmails(Set<String> emails)
70     {
71         this.emails = emails;
72     }
73     public Set<String> getEmails()
74     {
75         return this.emails;
76     }
77     public Person() {}
78     public Person(String name, int age) {
79         this.name = name;
80         this.age = age;
81     }
82 
83 }
View Code

Event类

 1 package hql;
 2 
 3 import java.util.*;
 4 
 5 import javax.persistence.*;
 6 
 7 @Entity
 8 @Table(name="event_inf")
 9 public class MyEvent
10 {
11     @Id @Column(name="event_id")
12     @GeneratedValue(strategy=GenerationType.IDENTITY)
13     private Integer id;
14     private String title;
15     private Date happenDate;
16     @ManyToMany(targetEntity=Person.class , mappedBy="myEvents")
17     private Set<Person> actors
18         = new HashSet<>();
19 
20     public void setId(Integer id)
21     {
22         this.id = id;
23     }
24     public Integer getId()
25     {
26         return this.id;
27     }
28 
29     public void setTitle(String title)
30     {
31         this.title = title;
32     }
33     public String getTitle()
34     {
35         return this.title;
36     }
37 
38     public void setHappenDate(Date happenDate)
39     {
40         this.happenDate = happenDate;
41     }
42     public Date getHappenDate()
43     {
44         return this.happenDate;
45     }
46 
47     public void setActors(Set<Person> actors)
48     {
49         this.actors = actors;
50     }
51     public Set<Person> getActors()
52     {
53         return this.actors;
54     }
55     public MyEvent() {}
56     public MyEvent(String title, Date happenDate) {
57         this.title = title;
58         this.happenDate = happenDate;
59     }
60 }
View Code

PersonManager类

 1 package hql;
 2 
 3 import org.hibernate.SessionFactory;
 4 import org.hibernate.Transaction;
 5 import org.hibernate.Session;
 6 import org.hibernate.cfg.Configuration;
 7 
 8 import java.text.ParseException;
 9 import java.text.SimpleDateFormat;
10 import java.util.Date;
11 import java.util.Set;
12 import java.util.HashSet;
13 
14 public class PersonManager
15 {
16 
17     public static void testPerson() throws ParseException
18     {
19         Configuration conf = new Configuration().configure();
20         conf.addAnnotatedClass(Person.class);
21         conf.addAnnotatedClass(MyEvent.class);
22         SessionFactory sf = conf.buildSessionFactory();
23         Session sess = sf.openSession();
24         Transaction tx = sess.beginTransaction();
25         Person p1 = new Person("张三",20);
26         p1.getEmails().add("zhangsan@baidu.com");
27         p1.getEmails().add("zhangsan@google.com");
28         
29         
30         Person p2 = new Person("李四",30);
31         p2.getEmails().add("lisi@jd.com");
32         
33         
34         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
35         MyEvent e1 = new MyEvent("大学毕业", sdf.parse("2012-06-01"));
36         MyEvent e2 = new MyEvent("参加工作", sdf.parse("2012-10-01"));
37         MyEvent e3 = new MyEvent("出国旅游", sdf.parse("2013-05-01"));
38         MyEvent e4 = new MyEvent("回家过年", sdf.parse("2013-12-20"));
39         MyEvent e5 = new MyEvent("升职加薪", sdf.parse("2014-01-01"));
40         
41         p1.getMyEvents().add(e1);
42         p1.getMyEvents().add(e3);
43         p1.getMyEvents().add(e4);
44         p1.getMyEvents().add(e5);
45         
46         p2.getMyEvents().add(e2);
47         p2.getMyEvents().add(e3);
48         p2.getMyEvents().add(e4);
49         
50         sess.save(p1);
51         sess.save(p2);
52         
53         tx.commit();
54         sess.close();
55     }
56     
57     public static void main(String[] args) throws ParseException {
58         testPerson();
59     }
60 }
View Code

首先执行上面的PersonManager,我们需要生成数据表如下,

MariaDB [test]> select * from event_inf;
+----------+---------------------+----------+
| event_id | happenDate | title |
+----------+---------------------+----------+
| 1 | 2013-12-20 00:00:00 | 回家过年 |
| 2 | 2013-05-01 00:00:00 | 出国旅游 |
| 3 | 2012-06-01 00:00:00 | 大学毕业 |
| 4 | 2014-01-01 00:00:00 | 升职加薪 |
| 5 | 2012-10-01 00:00:00 | 参加工作 |
+----------+---------------------+----------+
5 rows in set (0.00 sec)

MariaDB [test]> select * from person_event;
+-----------+----------+
| person_id | event_id |
+-----------+----------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 1 |
| 2 | 2 |
| 2 | 5 |
+-----------+----------+
7 rows in set (0.00 sec)

MariaDB [test]> select * from person_inf;
+-----------+-----+------+
| person_id | age | name |
+-----------+-----+------+
| 1 | 20 | 张三 |
| 2 | 30 | 李四 |
+-----------+-----+------+
2 rows in set (0.00 sec)

MariaDB [test]> select * from person_email_inf;
+-----------+---------------------+
| person_id | email_detail |
+-----------+---------------------+
| 1 | zhangsan@baidu.com |
| 1 | zhangsan@google.com |
| 2 | lisi@jd.com |
+-----------+---------------------+
3 rows in set (0.00 sec)

 

HQL的基本用法

现在可以写一个查询类用来查询上面的数据,一个最简单的查询是使用session的createQuery方法返回一个Query对象,再用Query对象的list()方法返回结果集,

通常结果集是持久化实体的结果集,可以用类型强制转换还原成原来的持久化类的对象,例如下面这样,

 1     public static void findPersons() {
 2         Configuration conf = new Configuration().configure();
 3         conf.addAnnotatedClass(Person.class);
 4         conf.addAnnotatedClass(MyEvent.class);
 5         SessionFactory sf = conf.buildSessionFactory();
 6         Session sess = sf.openSession();
 7         Transaction tx = sess.beginTransaction();
 8         
 9         List pl = sess.createQuery("select distinct p from Person p " 
10                 + "join p.myEvents where title = :eventTitle")
11                 .setString("eventTitle", "出国旅游")    //执行setString()为参数赋值
12                 .list(); //Query()调用list()方法获取查询的全部实例
13         for (Object ele : pl) {
14             Person p = (Person)ele;
15             System.out.println(p.getName());
16         }
17         tx.commit();
18         sess.close();
19         sf.close();
20     }

 

上面是最基本的查询方法,HQL语法与SQL非常类似,只不过HQL查询针对的是持久化类,实例及属性,上面查询的是持久化类的实例集合,

在设置参数的时候,可以在HQL中使用冒号(:)后紧接参数名来作为一个参数的占位符,然后在setXXX()为参数赋值,上面代码执行结果如下,

1 Hibernate: select distinct person0_.person_id as person_i1_3_, person0_.age as age2_3_, person0_.name as name3_3_ from person_inf person0_ inner join person_event myevents1_ on person0_.person_id=myevents1_.person_id inner join event_inf myevent2_ on myevents1_.event_id=myevent2_.event_id where title=?
2 张三
3 李四

 

也可以在HQL中使用问号(?)紧接索引的方式设置参数占位符,然后在setXXX()中用按索引为参数赋值,例如下面,

 1     public static void findPersonsByHappendDate() throws ParseException {
 2         Configuration conf = new Configuration().configure();
 3         conf.addAnnotatedClass(Person.class);
 4         conf.addAnnotatedClass(MyEvent.class);
 5         SessionFactory sf = conf.buildSessionFactory();
 6         Session sess = sf.openSession();
 7         Transaction tx = sess.beginTransaction();
 8         
 9         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 
10         List pl = sess.createQuery("select distinct p from Person p "
11                 + "inner join p.myEvents event where event.happenDate "
12                 + "between ?1 and ?2")
13                 .setDate("1", sdf.parse("2012-06-01"))
14                 .setDate("2", new Date())
15                 .list();
16         for (Object ele : pl) {
17             Person p = (Person)ele;
18             System.out.println(p.getName());
19         }
20         tx.commit();
21         sess.close();
22         sf.close();
23     }

 

上面执行结果,

1 Hibernate: select distinct person0_.person_id as person_i1_3_, person0_.age as age2_3_, person0_.name as name3_3_ from person_inf person0_ inner join person_event myevents1_ on person0_.person_id=myevents1_.person_id inner join event_inf myevent2_ on myevents1_.event_id=myevent2_.event_id where myevent2_.happenDate between ? and ?
2 张三
3 李四

 

除了在select中查询持久化类实例之外,也能直接查询属性,例如,

 1     public static void findPersonProperty() {
 2         Configuration conf = new Configuration().configure();
 3         conf.addAnnotatedClass(Person.class);
 4         conf.addAnnotatedClass(MyEvent.class);
 5         SessionFactory sf = conf.buildSessionFactory();
 6         Session sess = sf.openSession();
 7         Transaction tx = sess.beginTransaction();
 8         
 9         List pl = sess.createQuery("select distinct p.id, p.name, p.age "
10                 + "from Person p join p.myEvents")
11                 .list();
12         for (Object ele : pl) {
13             Object[] objs = (Object[])ele;
14             System.out.println(java.util.Arrays.toString(objs));
15         }
16         tx.commit();
17         sess.close();
18         sf.close();
19     }

 

程序运行结果,

1 Hibernate: select distinct person0_.person_id as col_0_0_, person0_.name as col_1_0_, person0_.age as col_2_0_ from person_inf person0_ inner join person_event myevents1_ on person0_.person_id=myevents1_.person_id inner join event_inf myevent2_ on myevents1_.event_id=myevent2_.event_id
2 [1, 张三, 20]
3 [2, 李四, 30]

 

关联和连接

在HQL中最简单的查询语句是from子句(前面不需要select关键字),from后接持久化了类名称(大小写敏感),持久化类可以用有别名,用as关键字(可以省略)。

from后面可以接多个实体类进行表关联,但是实际上这种用法不多,更多的是使用隐式或者显示连接实现跨表连接。

隐式连接

不使用join关键字,而使用点号(.)来隐式连接实体,例如 "from Person p where p.address = xxxx"

但是需要注意的是,在Hibernate 3.2.3之后,隐式连接的使用需要特别注意,当关联的是普通组件时候,可以使用隐式连接,如果关联的是集合属性,就会抛出illegal attempt to dereference collection...异常

显式连接

HQL中支持以下显示连接,分别与SQL99中的各种连接对应

inner join, 可简写成join

left outer join, 可简写成left join

right outer join, 可简写成right join

full join

下面是一个显式连接的例子,通过打印出来的SQL语句会发现,HQL会自动根据持久化类之间的关联关系,生成对应的连接表的 with (等同于SQL语句里join的on关键字)条件,即使没有在HQL中显式地写出with条件。

HQL:

1 "select p from Person p inner join p.emails e where e = :email"

 

SQL:

 1 Hibernate: 
 2     select
 3         person0_.person_id as person_i1_3_,
 4         person0_.age as age2_3_,
 5         person0_.name as name3_3_ 
 6     from
 7         person_inf person0_ 
 8     inner join
 9         person_email_inf emails1_ 
10             on person0_.person_id=emails1_.person_id 
11     where
12         emails1_.email_detail=?

 

查询结果集

 

延迟加载

Hibernate默认开启了延迟加载,如果session关闭,则无法继续通过实体对象获取数据。

例如Person关联的属性emails,默认加载Person时候并不会去获取emails属性值,一旦session关闭就无法获取emails了,

为了解决这个问题,可以在HQL中使用join fetch关键字,例如下面的例子,

 1     public static void joinFetch() {
 2         Configuration conf = new Configuration().configure();
 3         conf.addAnnotatedClass(Person.class);
 4         conf.addAnnotatedClass(MyEvent.class);
 5         SessionFactory sf = conf.buildSessionFactory();
 6         Session sess = sf.openSession();
 7         Transaction tx = sess.beginTransaction();
 8         
 9         List pl = sess.createQuery("from Person p join fetch p.myEvents")
10                 .list();
11         tx.commit();
12         sess.close();
13         sf.close();
14         
15         for(Object ele : pl) {
16             Person p = (Person)ele;
17             System.out.println(p.getMyEvents().iterator().next().getTitle());
18         }
19     }

 

我们将读取数据放在session关闭之后,发现依然可以后去myEvents属性,

 1 Hibernate: 
 2     select
 3         person0_.person_id as person_i1_3_0_,
 4         myevent2_.event_id as event_id1_0_1_,
 5         person0_.age as age2_3_0_,
 6         person0_.name as name3_3_0_,
 7         myevent2_.happenDate as happenDa2_0_1_,
 8         myevent2_.title as title3_0_1_,
 9         myevents1_.person_id as person_i1_3_0__,
10         myevents1_.event_id as event_id2_2_0__ 
11     from
12         person_inf person0_ 
13     inner join
14         person_event myevents1_ 
15             on person0_.person_id=myevents1_.person_id 
16     inner join
17         event_inf myevent2_ 
18             on myevents1_.event_id=myevent2_.event_id
19 出国旅游
20 出国旅游
21 出国旅游
22 出国旅游
23 参加工作
24 参加工作
25 参加工作
View Code

 

Select 子句

select子句接单个持久化类

如果select后面只查询单个持久化类,那么返回的查询结果是一个集合,每一个集合元素可以直接通过强制类型转换还原成原来的数据类型,例如

1         List list0 = sess
2                 .createQuery("select p from Person p").list();

 

首先可以用for(Object ele : list0)遍历结果集,对每一个元素可以直接强制转换成原来的数据类型,

1         for (Object ele : list0) {
2             Person p = (Person)ele;
3             System.out.println(p.getName());
4         }

select子句接持久化类和属性混合

select子句可以接持久化类,或者其属性,通常select子句查询的结果就是一个集合,集合的每个元素都是数组,相当于返回的是一个二维数组结果集,

1         List pl = sess
2                 .createQuery(
3                         "select p.name, e from Person p join p.myEvents e "
4                                 + "where e.title = :title")
5                 .setString("title", "回家过年").list();

 

因此需要先将每个集合元素还原成数组,再次将数组每一项强制换换成对应数据类型,

1         for (Object ele : pl) {
2             Object[] objs = (Object[]) ele;
3             String name = (String) objs[0];
4             MyEvent e = (MyEvent) objs[1];
5             System.out.println(name + "," + e.getTitle());
6         }

 

首先上面sess.createQuery(xxx).list()返回的是一个集合,因此用for(Object ele : pl)遍历每一个元素,每一个元素又是一个数组,因此用Object[] objs = (Object[])ele; 强制转换去还原集合每一个元素,而对于数组每一项的数据类型,则是根据HQL中select后面的顺序,逐一匹配,因此在for循环里用来String和MyEvent来还原数组每一项。

这是select最通用的用法。

select 子句直接生成list对象或者map对象

Query对象返回的集合中,每一个元素就是一个list对象,每个list对象,例如像下面这样,

1         List list1 = sess
2                 .createQuery(
3                         "select new list(p.name, p.age, e) from Person p left join p.emails e")
4                 .list();

 

返回的集合中,每一个元素就是list对象,用强制类型转换还原即可,遍历每一个list对象,就是select后面list中的每个元素,

1         for (Object ele : list1) {
2             List name = (List) ele;
3             Iterator it = name.iterator();
4             while (it.hasNext()) {
5                 System.out.println(it.next());
6             }
7         }

 

当然也可以在select子句之后直接生成map对象,使用别名p.name as pname作为map的key,其实际值作为value,

相当于Query对象通过.list()方法返回的结果集中,包含了n个map对象,每个map对象里都只有一个key-value对,每个key名字都交pname,

1         List list2 = sess
2                 .createQuery(
3                         "select new map(p.name as pname) from Person p")
4                 .list();

 

可以像下面这样遍历map,

1         for (Object ele : list2) {
2             Map mName = (HashMap) ele;
3             Set keySet = (Set)mName.keySet();
4             Iterator it = keySet.iterator();
5             while( it.hasNext()) {
6                 Object key = it.next();
7                 System.out.println(key+"->"+mName.get(key));
8             }
9         }

 

输出结果,

1 pname->张三
2 pname->李四

 

select子句甚至可以直接跟持久化类的构造函数

1         List list3 = sess
2 以上是关于Hibernate之HQL基本用法的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate 之强大的HQL查询

hibernate之HQL,Criteria与SQL

Hibernate的HQL多表查询

Hibernate hql语句 find_in_set 用法

Hibernate hql语句 find_in_set 用法

hibernate之HQL