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                 .createQuery(
3                         "select new MyEvent(e.title, e.happenDate) from Person p left join p.myEvents e")
4                 .list();

这样查询出的效果跟直接查询一个持久化类一样,只不过这里是用select的结果去初始化一个持久化类了,依然可以遍历每一个集合元素,直接强制转换成对应持久化类实例,

1         for (Object ele : list3) {
2             MyEvent e = (MyEvent)ele;
3             System.out.println(e.getTitle());
4         }

多态查询

HQL支持多态,from后跟持久化类名,不仅会查出持久化类的全部实例,还会查出该类的子类的全部实例。  

即,当我们用HQL查询父类或者接口时,父类的子类,或者接口的实现类的实例都会一起被查询出来。

注意,一张数据表代表一个持久化类,表中一行就带表一个持久化类的实例。

所以按照多态查询的规则,如果在我们前面的测试工程中,查询 java.lang.Object类会有什么结果呢,例如

1 List list4 = sess.createQuery("from java.lang.Object o").list();

分析上面的HQL,结合HQL的多态性质, 我们知道Object是所有java类的父类,在本工程中,Person和MyEvent都是Object的子类,所以这两个类的所有实例都将被查询出来,

Person_inf表中有两条记录,event_inf表中有5条记录,所以最终会总共会查询出7个实例,我们直接将每个实例的内存映射打印出来,

1         for(Object ele : list4) {
2             System.out.println(ele);
3         }

首先我们会看到Hibernate生成了两条SQL语句,分别用来查询person_inf和event_inf表,这个可以理解的,因为本工程中Object有两个子持久化类。

 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 Hibernate: 
 9     select
10         myevent0_.event_id as event_id1_0_,
11         myevent0_.happenDate as happenDa2_0_,
12         myevent0_.title as title3_0_ 
13     from
14         event_inf myevent0_

打印的实例的内存映射如下,

1 hql.Person@350c420a
2 hql.Person@e6c75827
3 hql.MyEvent@75ae7c20
4 hql.MyEvent@62ee0677
5 hql.MyEvent@214e7ddc
6 hql.MyEvent@cfad90e0
7 hql.MyEvent@226c3fff

可以看到内存映射中,也是按持久化类的顺序排列的,我们甚至可以将每一个实例的内存映射进行强制类型转换,还原成真正的持久化类的对象,比如下面这样,

 1         Person p1 = (Person)list4.get(0);
 2         Person p2 = (Person)list4.get(1);
 3         System.out.println(p1.getName()+","+p2.getName());
 4         
 5         MyEvent e1 = (MyEvent)list4.get(2);
 6         MyEvent e2 = (MyEvent)list4.get(3);
 7         MyEvent e3 = (MyEvent)list4.get(4);
 8         MyEvent e4 = (MyEvent)list4.get(5);
 9         MyEvent e5 = (MyEvent)list4.get(6);
10         System.out.println(e1.getTitle()+","+e2.getTitle()+","+e3.getTitle()+","
11                 + ""+e4.getTitle()+","+e5.getTitle());

现在就可以直接用对象去访问属性了,上面程序片段输出结果为,

1 张三,李四
2 出国旅游,升职加薪,大学毕业,回家过年,参加工作

可见刚好将每张表的所有记录(即实例)打印出来了!

where子句

引用关联属性的隐式连接和显示连接

where子句后面可以可以使用属性来限定范围,属性可以是普通属性或者组件属性,但是需要特别注意集合属性。

在3.2.3以后的版本中,如果属性为集合属性,那么不能直接在where子句后面使用点号(.)来访问,因为这在底层会转换成多表连接查询,即隐式连接。 3.2.3之后的版本是不支持集合属性的隐式连接的,需要显示join连接。

例如下面这个查询,Person的myEvents是一个集合属性,p.myEvents对应的是一个关联的MyEvent实体,在底层会隐式连接person_inf和event_inf表。

1 List list0 = sess.createQuery("select p from Person p where p.myEvents.title is not null").list();

抛出异常,

 1 Exception in thread "main" org.hibernate.QueryException: illegal attempt to dereference collection [person0_.person_id.myEvents] with element property reference [title] [select p from hql.Person p where p.myEvents.title is not null]
 2     at org.hibernate.QueryException.generateQueryException(QueryException.java:137)
 3     at org.hibernate.QueryException.wrapWithQueryString(QueryException.java:120)
 4     at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:234)
 5     at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:158)
 6     at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:131)
 7     at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:93)
 8     at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:167)
 9     at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:301)
10     at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:236)
11     at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1836)
12     at hql.HqlQuery.testWhere(HqlQuery.java:245)
13     at hql.HqlQuery.main(HqlQuery.java:265)
View Code

这将引发集合属性隐式连接异常,抛出 illegal attempt to dereference collection 的错误,

必须将上面的查询改为显示连接,

1         List list0 = sess.createQuery("select p from Person p inner join p.myEvents e "
2                 +"where e.title is not null").list();

用特殊关键字【id】引用任何主键

不论持久化类中的标识属性(表关键字)定义成什么名字,在HQL中都可以用关键字id来代替,例如

在MyEvent持久化类中,定义了eventId为标识属性,

1 @Entity
2 @Table(name="event_inf")
3 public class MyEvent
4 {
5     @Id @Column(name="event_id")
6     @GeneratedValue(strategy=GenerationType.IDENTITY)
7     private Integer eventId;
8 ...

在HQL中,使用id引用持久化类的标识属性,

1 List list1 = sess.createQuery("from MyEvent e where e.id = 1").list();

 子查询

HQL中支持在select和where后面进行子查询,例如这样,

1         List list1 = sess.createQuery("select (select id from MyEvent e where id=1) from MyEvent "
2                 + "where id = (select id from Person p where id = 2)").list();

上面代码将生成以下SQL语句,可以看到HQL子查询与SQL子查询基本一致。

 1 Hibernate: 
 2     select
 3         (select
 4             myevent1_.event_id 
 5         from
 6             event_inf myevent1_ 
 7         where
 8             myevent1_.event_id=1) as col_0_0_ 
 9     from
10         event_inf myevent0_ 
11     where
12         myevent0_.event_id=(
13             select
14                 person2_.person_id 
15             from
16                 person_inf person2_ 
17             where
18                 person2_.person_id=2
19         )

 命名查询(注解查询)

Hibernate提供了一个@NamedQuery注解可以将原本写在createQuery()中的HQL放在注解中,之后通过sess.getNamedQuery()取出注解上的配置进行查询,一样会返回Query对象,后续跟普通查询流程一样,如下面的例子,

在Person实体类上我们增加一个命名查询注解

1 @Entity
2 @Table(name = "person_inf")
3 @NamedQuery(name="myNamedQuery", query="select p from Person as p where p.age > ?")
4 public class Person
5 {
6 ...

调用方法如下,

1         List list1 = sess.getNamedQuery("myNamedQuery")
2                 .setInteger(0, 25)
3                 .list();
4         for(Object ele : list1) {
5             Person p = (Person)ele;
6             System.out.println(p.getName()+","+p.getAge());
7         }

命名查询的本质只是将java代码中的HQL放在注解中去配置了。

条件查询

条件查询需要使用sess.createCriteria(Class)来返回Criteria对象,一个Criteria对象就代表一次查询,通过Criteria对象的add方法可以添加查询条件, 查询条件通过工具类Restrictions中的方法来指定,例如下面这样,

1         List list1 = sess.createCriteria(Person.class)
2                 .add( Restrictions.gt("age", 25))
3                 .list();

其中工具类Restrictions支持很多静态方法,用来做查询条件,例如 gt代表“大于”,lt代表“小于”等等。

之后就能得到查询结果集,和之前的处理方法一样。

1         for(Object ele : list1) {
2             Person p = (Person)ele;
3             System.out.println(p.getName()+","+p.getAge());
4         }

关联属性实体的条件查询

如果要在关联属性的实体上增加查询条件,就需要对关联属性再次使用 createCritieria()方法,例如要在Person的关联属性myEvents上增加条件查询,

1         List list2 = sess.createCriteria(Person.class)
2                 .add( Restrictions.gt("age", 25))
3                 .createCriteria("myEvents", JoinType.LEFT_OUTER_JOIN)
4                 .add( Restrictions.isNotNull("title"))
5                 .list();
6         for(Object ele : list2) {
7             Person p = (Person)ele;
8             System.out.println(p.getName()+","+p.getAge());
9         }

createAlias()

createAlias()也可以实现在关联属性上增加条件查询,与createCritieria()不同的是,createAlias()仅仅是给关联实体起一个别名,让后面的过滤条件可以根据该关联实体的别名进行筛选,而不是创建一个新的Criteria实例。

1         List list3 = sess.createCriteria(Person.class)
2                 .add( Restrictions.gt("age", 25))
3                 .createAlias("myEvents", "eve")
4                 .add( Restrictions.isNotNull("eve.title"))
5                 .list();
6         for(Object ele : list3) {
7             Person p = (Person)ele;
8             System.out.println(p.getName()+","+p.getAge());
9         }

条件查询上的延迟加载

与HQL中的fetch关键字一样,Critieria实例也可以增加延迟加载配置,使用setFetchMode()即可,有三个可选值,

DEFAULT:使用配置文件指定延迟加载策略

JOIN:使用外连接、预初始化关联实体.(即不使用延迟加载)

SELECT:启用延迟加载,系统将使用单独的select语句来初始化关联实体,之后真正要访问关联实体的时候,才会执行第二天条select语句。

下面是一个启用延迟加载的例子,

 1         List list4 = sess.createCriteria(Person.class)
 2                 .add( Restrictions.gt("age", 25))
 3                 .setFetchMode("myEvents", FetchMode.SELECT)
 4                 .list();
 5         tx.commit();
 6         sess.close();
 7         sf.close();
 8         
 9         for(Object ele : list4) {
10             Person p = (Person)ele;
11             System.out.println(p.getName()+","+p.getAge());
12             Set<MyEvent> e = p.getMyEvents();
13             Iterator it = e.iterator();
14             while (it.hasNext()) {
15                 MyEvent ee = (MyEvent)it.next();
16                 System.out.println(ee.getTitle());
17             }
18         }

我们在第3行启用延迟加载,那么一旦session关闭之后,关联实体就不能访问了,在第12行会抛出 failed to lazily initialize a collection of role 的异常,

查看Hibernate生成的SQL,会发现只查询了person_inf表,

 1 Hibernate: 
 2     select
 3         this_.person_id as person_i1_3_0_,
 4         this_.age as age2_3_0_,
 5         this_.name as name3_3_0_ 
 6     from
 7         person_inf this_ 
 8     where
 9         this_.age>?
10 李四,30
11 Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: hql.Person.myEvents, could not initialize proxy - no Session
12     at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:576)
13     at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:215)
14     at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:555)
15     at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:143)
16     at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:180)
17     at hql.HqlQuery.testCriteria(HqlQuery.java:353)
18     at hql.HqlQuery.main(HqlQuery.java:372)

如果我们将上面代码第3行改成.setFetchMode("myEvents", FetchMode.JOIN),即关闭延迟加载,那么关联属性就会立即查询出来,可以看到生成的SQL使用left out join进行了连接查询。

 1 Hibernate: 
 2     select
 3         this_.person_id as person_i1_3_1_,
 4         this_.age as age2_3_1_,
 5         this_.name as name3_3_1_,
 6         myevents2_.person_id as person_i1_3_3_,
 7         myevent3_.event_id as event_id2_2_3_,
 8         myevent3_.event_id as event_id1_0_0_,
 9         myevent3_.happenDate as happenDa2_0_0_,
10         myevent3_.title as title3_0_0_ 
11     from
12         person_inf this_ 
13     left outer join
14         person_event myevents2_ 
15             on this_.person_id=myevents2_.person_id 
16     left outer join
17         event_inf myevent3_ 
18             on myevents2_.event_id=myevent3_.event_id 
19     where
20         this_.age>?
21 李四,30
22 回家过年
23 出国旅游
24 参加工作
25 李四,30
26 回家过年
27 出国旅游
28 参加工作
29 李四,30
30 回家过年
31 出国旅游
32 参加工作

投影,聚合,分组

Hibernate的条件查询中的所谓的投影运算就是按列查询,具体分成两种,一种是根据列来进行统计,使用Projection接口实现,类似于SQL中的聚集函数(count,AVG,groupby 等)

另一种就是直接按列查询,Hibernate的条件查询中使用Property()方法,其作用类似SQL中的select

Projection投影运算

所谓HQL中的投影,聚合,分组,其实就是SQL中的一些聚合函数,例如统计记录条数count(), 计算平均值avg(),统计最大值max(),以及分组统计group by等等。

在条件查询中可以通过Projection接口实现这些功能。工具类Projections提供了很多静态方法来实现上面的功能,下面是基本用法,

 1         List list1 = sess.createCriteria(Person.class)
 2                 .createAlias("myEvents", "eve")
 3                 .setProjection(Projections.projectionList()
 4                         .add(Projections.rowCount())
 5                         .add(Projections.max("eve.title"))
 6                         .add(Projections.groupProperty("eve.title")))
 7                 .list();
 8         for(Object ele : list1) {
 9             Object[] objs = (Object[])ele;
10             for (Object obj : objs) {
11                 System.out.print(obj+",");
12             }
13             System.out.println("
==========");
14         }

看看hibernate生成的SQL就能知道Projection的功能了,

 1 Hibernate: 
 2     select
 3         count(*) as y0_,
 4         max(eve1_.title) as y1_,
 5         eve1_.title as y2_ 
 6     from
 7         person_inf this_ 
 8     inner join
 9         person_event myevents3_ 
10             on this_.person_id=myevents3_.person_id 
11     inner join
12         event_inf eve1_ 
13             on myevents3_.event_id=eve1_.event_id 
14     group by
15         eve1_.title

其实就是在SQL中使用了一些聚集函数统计而已,统计结果如下,

 1 2,出国旅游,出国旅游,
 2 ==========
 3 1,升职加薪,升职加薪,
 4 ==========
 5 1,参加工作,参加工作,
 6 ==========
 7 2,回家过年,回家过年,
 8 ==========
 9 1,大学毕业,大学毕业,
10 ==========

指定别名及排序

可以为Projection方式的聚集统计结果起一个别名,用来根据统计结果排序等。通常会有三种方式起别名。

第一种是使用Projections工具的Alias()方法

 1         List list2 = sess.createCriteria(Person.class)
 2                 .createAlias("myEvents", "eve")
 3                 .setProjection(Projections.projectionList()
 4                         .add(Projections.alias(Projections.rowCount(), "c"))
 5                         .add(Projections.max("eve.title"))
 6                         .add(Projections.groupProperty("eve.title")))
 7                         .addOrder(Order.asc("c"))
 8                 .list();
 9         for(Object ele : list2) {
10             Object[] objs = (Object[])ele;
11             for (Object obj : objs) {
12                 System.out.print(obj+",");
13             }
14             System.out.println("
==========");
15         }

上面是按照记录条数排序,结果如下,

 1 1,升职加薪,升职加薪,
 2 ==========
 3 1,大学毕业,大学毕业,
 4 ==========
 5 1,参加工作,参加工作,
 6 ==========
 7 2,出国旅游,出国旅游,
 8 ==========
 9 2,回家过年,回家过年,
10 ==========

第二种方法是使用SimpleProject的as()方法指定别名

这种方法要求Projections后的聚集函数必须是SimpleProject类或者子类,

 1         List list3 = sess.createCriteria(Person.class)
 2                 .createAlias("myEvents", "eve")
 3                 .setProjection(Projections.projectionList()
 4                         .add(Projections.rowCount())
 5                         .add(Projections.max("eve.title"))
 6                         .add(Projections.groupProperty("eve.title").as("c")))
 7                         .addOrder(Order.asc("c"))
 8                 .list();
 9         for(Object ele : list3) {
10             Object[] objs = (Object[])ele;
11             for (Object obj : objs) {
12                 System.out.print(obj+",");
13             }
14             System.out.println("
==========");
15         }

上面是按照title排序,结果如下,

 1 2,出国旅游,出国旅游,
 2 ==========
 3 1,升职加薪,升职加薪,
 4 ==========
 5 1,参加工作,参加工作,
 6 ==========
 7 2,回家过年,回家过年,
 8 ==========
 9 1,大学毕业,大学毕业,
10 ==========

第三种是使用ProjectList的重载方法add()时指定别名

 1         List list4 = sess.createCriteria(Person.class)
 2                 .createAlias("myEvents", "eve")
 3                 .setProjection(Projections.projectionList()
 4                         .add(Projections.rowCount(),"c")
 5                         .add(Projections.max("eve.title"))
 6                         .add(Projections.groupProperty("eve.title")))
 7                         .addOrder(Order.asc("c"))
 8                 .list();
 9         for(Object ele : list4) {
10             Object[] objs = (Object[])ele;
11             for (Object obj : objs) {
12                 System.out.print(obj+",");
13             }
14             System.out.println("
==========");
15         }

上面也是按照记录条数排序

Property投影运算

除了Projections工具类之外,Property方法也可以进行投影运算,但Projections主要是用来进行统计计算,而Property则主要是用来选择指定的类,作用类似SQL中的select.

基本用法如下,

 1         List list6 = sess.createCriteria(Person.class)
 2                 .createAlias("myEvents", "eve")
 3                 .setProjection(Projections.projectionList()
 4                         .add(Property.forName("name"))
 5                         .add(Property.forName("eve.title")))
 6                 .add(Property.forName("eve.title").eq("回家过年"))
 7                 .list();
 8         for(Object ele : list6) {
 9             Object[] objs = (Object[])ele;
10             for (Object obj : objs) {
11                 System.out.print(obj+",");
12             }
13             System.out.println("
==========");
14         }

上面代码的意思是,先选出Person类的name属性和MyEvent类的title属性(当然事先需要对这两个持久化类进行关联查询),然后按照title过滤,只选出title为“回家过年”的记录,

Hibernate生成的SQL如下,

 1     select
 2         this_.name as y0_,
 3         eve1_.title as y1_ 
 4     from
 5         person_inf this_ 
 6     inner join
 7         person_event myevents3_ 
 8             on this_.person_id=myevents3_.person_id 
 9     inner join
10         event_inf eve1_ 
11             on myevents3_.event_id=eve1_.event_id 
12     where
13         eve1_.title=?

查询结果如下,

1 张三,回家过年,
2 ==========
3 李四,回家过年,
4 ==========

 DetachedCriteria离线查询和子查询

这里所谓的离线查询,就是在session打开之前定义好查询语句,获取一个DetachedCriteria的实例,这样可以在任意的session中使用这个查询对象,

例如这样,

1         DetachedCriteria query = DetachedCriteria
2                 .forClass(Person.class)
3                 .createAlias("myEvents", "eve")
4                 .setProjection(Property.forName("name"));

之后在任意的session中,使用DetachedCriteria类的getExecutableCriteria()可以调用这个查询实例进行离线查询,

例如这样,

1 List list1 = query.getExecutableCriteria(sess).list();

另外,如果在session使用了条件查询,在条件查询中又使用了Projection的eq(), eqAll(), gt(), in()...等一系列类似运算符的方法,这时可以将session外面定义的DetachedCriteria放在eq(), eqAll(), gt(), in()中作为一个子查询,

例如这样,

1         List list2 = sess.createCriteria(Person.class)
2                 .add( Property.forName("name").in(query))
3                 .list();

也就是说,当直接使用DetachedCriteria对象的getExecutableCriteria()进行查询的时候,DetachedCriteria对象就是一个离线查询,

当在session中使用DetachedCriteria查询的时候,就是一个子查询,

下面是一个完整的离线查询和子查询的例子,

 1     public static void testDetachedCriteria() {
 2         DetachedCriteria query = DetachedCriteria
 3                 .forClass(Person.class)
 4                 .createAlias("myEvents", "eve")
 5                 .setProjection(Property.forName("name"));
 6         
 7         Configuration conf = new Configuration().configure();
 8         conf.addAnnotatedClass(Person.class);
 9         conf.addAnnotatedClass(MyEvent.class);
10         SessionFactory sf = conf.buildSessionFactory();
11         Session sess = sf.openSession();
12         Transaction tx = sess.beginTransaction();
13         
14         List list1 = query.getExecutableCriteria(sess).list();
15         System.out.println(list1);
16         
17         List list2 = sess.createCriteria(Person.class)
18                 .add( Property.forName("name").in(query))
19                 .list();
20         System.out.println(list2);
21         tx.commit();
22         sess.close();
23         sf.close();
24     }

对于上面第14行的离线查询,将会看到下面这样的SQL

 1 Hibernate: 
 2     select
 3         this_.name as y0_ 
 4     from
 5         person_inf this_ 
 6     inner join
 7         person_event myevents3_ 
 8             on this_.person_id=myevents3_.person_id 
 9     inner join
10         event_inf eve1_ 
11             on myevents3_.event_id=eve1_.event_id

对于上面第17行的子查询,将会看到下面这样的SQL

 1 Hibernate: 
 2     select
 3         this_.person_id as person_i1_3_0_,
 4         this_.age as age2_3_0_,
 5         this_.name as name3_3_0_ 
 6     from
 7         person_inf this_ 
 8     where
 9         this_.name in (
10             select
11                 this_.name as y0_ 
12             from
13                 person_inf this_ 
14             inner join
15                 person_event myevents3_ 
16                     on this_.person_id=myevents3_.person_id 
17             inner join
18                 event_inf eve1_ 
19                     on myevents3_.event_id=eve1_.event_id
20             )

 原生SQL查询

Hibernate还支持原生的SQL查询,但是通常不建议在新项目中这么做,而是用在老系统上。使用session的createSQLQuery()方法可以用原生SQL进行查询并得到Query对象,后续用法与之前一样。

标量查询

即直接查出值(而不是实体类对象),因为是直接查出的值,需要使用Query对象的addScalar()筛选指定的列,并为其制定数据类型

比如这样,

1         String sqlString = "select p.* from person_inf p";
2         List list1 = sess.createSQLQuery(sqlString)
3                 .addScalar("name", StandardBasicTypes.STRING)
4                 .addScalar("age",StandardBasicTypes.INTEGER)
5                 .list();
6         for(Object ele : list1) {
7             Object[] row = (Object[])ele;
8             System.out.println(row[0]+","+row[1]);
9         }

实体查询

也可以将SQL查出的结果转换成实体类,前提条件是必须查出所有列才行。使用Query对象的addEntity()方法可以将查询结果集转换成实体类,

比如这样,

1         List list2 = sess.createSQLQuery(sqlString)
2                 .addEntity(Person.class)
3                 .list();
4         for(Object ele : list2) {
5             Person p = (Person)ele;
6             System.out.println(p.getName()+","+p.getAge());
7         }

涉及到多表查询的时候,可以同时转换多个实体类,比如这样,

1         String sqlString3 = "select p.*,pe.*,e.* from person_inf p, person_event pe, event_inf e "
2                 + "where p.person_id = pe.person_id "
3                 + "and e.event_id = pe.event_id";
4         List list3 = sess.createSQLQuery(sqlString3)
5                 .addEntity("p",Person.class)
6                 .addEntity("e", MyEvent.class)
7                 .list();

转为普通javabean

可以使用Query对象的setResultTransformer()方法,

1         String sqlString4 = "select p.name, p.age from person_inf p";
2         List list4 = sess.createSQLQuery(sqlString4)
3                 .setResultTransformer(Transformers.aliasToBean(Student.class))
4                 .list();

关联实体类处理

使用Query对象的addJoin()方法可以将原生SQL中的部分查询结果转换为关联的实体类,例如下面,

1         String sqlString5 = "select * from person_inf p, person_event pe, event_inf e "
2                 + "where p.person_id = pe.person_id "
3                 + "and e.event_id = pe.event_id";
4         List list5 = sess.createSQLQuery(sqlString5)
5                 .addEntity("p", Person.class)
6                 .addJoin("e", "p.myEvents")
7                 .list();

对比上面的多表查询转换为实体类的例子,发现与这里的关联实体类处理结果是一样的。

命名SQL

可以使用配置文件或者注解,将SQL从源码中拿出来单独管理,以实现程序解耦。

单一实体类SQL查询

Hibernate中使用@NamedNativeQuery注解来定义命名SQL查询,通常其结构如下,

1   @NamedNativeQuery(name="simpleQuery" 
2  , query="select e.event_id as person_id, e.title as name, 1 as age from event_inf e"
3  , resultClass=Person.class)

可以看到命名SQL查询的注解中,通常有三个参数,其中resultClass这个参数可以指定查出的结果将要转换成哪个实体类的实例。

在程序中,将会这样调用SQL命名查询,

1 List list6 = sess.getNamedQuery("simpleQuery").list();

然后在session关闭之后,依然可以遍历list中的数据,我们在前面指定了此命名SQL查询将返回Person类的实例,所以下面直接将结果转换,

1         for (Object ele : list6) {
2             Person p = (Person)ele;
3             System.out.println(p.getName()+","+p.getAge());
4         }

多个实体类、结果集查询

命名SQL不仅可以返回实体类,同时还可以返回结果集,最终的返回结果将是包含实体类和普通结果集的混合体,

这种情况下, resultClass属性已经无法满足了,我们需要使用resultSetMapping来指定将会返回什么,而resultMapping属性又依赖于@SqlResultSetMapping注解的定义。

下面是一个会返回多个实体类,结果集混合查询注解的例子,首先需要定义命名SQL查询注解,

1 @NamedNativeQuery(name = "queryTest"
2 , query = "select p.*,e.*,p.name from person_inf p, person_event pe, event_inf e "
3         + "where p.person_id = pe.person_id " + "and e.event_id = pe.event_id"
4         , resultSetMapping = "firstMapping")

我们指定返回方式为resultSetMapping, 并指定代称为firstMapping,下面需要配置@SqlResultSetMapping来指定返回的结果集,

1 @SqlResultSetMapping(name = "firstMapping"
2 , entities = {
3         @EntityResult(entityClass = Person.class),
4         @EntityResult(entityClass = MyEvent.class, fields = {
5                 @FieldResult(name="eventId", column="e.event_id"),
6                 @FieldResult(name = "title", column = "e.title"),
7                 @FieldResult(name = "happenDate", column = "e.happenDate"), }) }
8 , columns = { @ColumnResult(name = "p.name", type = String.class) })

即,在Query的结果集的每一行中,我们需要先返回Person实体类实例,再返回MyEvent实体类实例,还要返回一个字符串,那么对应读取结果集的代码如下,

1         for(Object ele : list7) {
2             Object[] ents = (Object[])ele;
3             Person p = (Person)ents[0];
4             MyEvent e = (MyEvent)ents[1];
5             String name = (String)ents[2];
6             System.out.println(p.getName()+","+e.getTitle()+","+name);
7         }

 调用存储过程

Hibernate是通过命名SQL查询来调用存储过程的,只需将存储过程函数放入命名SQL注解的query参数中。

下面我们在mysql中定义一个存储过程如下,

1 create PROCEDURE select_all_event() 
2 select e.event_id as person_id, e.title as name, 1 as age from event_inf e

然后将存储过程函数名放入命名SQL注解的query参数中,

1 @Entity
2 @Table(name="event_inf")
3 @NamedNativeQuery(name="simpleQuery" 
4 , query="{call select_all_event()}"
5 , resultClass=Person.class)
6 public class MyEvent
7 {

之后在代码中的调用方法与普通的执行命名SQL的方法没有区别,

1         List list1 = sess.getNamedQuery("myNamedQuery")
2                 .setInteger(0, 25)
3                 .list();
4         for(Object ele : list1) {
5             Person p = (Person)ele;
6             System.out.println(p.getName()+","+p.getAge());
7         }

使用定制SQL

当我们持久化一个实体的时候(调用session.save()或session.persist()),Hibernate会自动为我们生产成SQL语句,

但是如果我们不想使用Hibernate自动生成的SQL,而是希望自定义SQL呢。

那么我们可以使用Hibernate的定制SQL的注解:@SQLInsert, @SQLUpdate,@SQLDelete,@SQLDeleteAll等等,例如下面这样,

 1 @SQLInsert(sql="insert into student_inf(student_id,age,name) values(100,?,?)")
 2 @SQLUpdate(sql="update student_inf set name=?,age=? were person_id=?")
 3 @SQLDelete(sql="delete from student_inf where person_id=?")
 4 @SQLDeleteAll(sql="delete from student_inf")
 5 @Entity
 6 @Table(name="student_inf")
 7 public class Student {
 8     @Id @Column(name = "student_id")
 9     @GeneratedValue(strategy=GenerationType.IDENTITY)
10     private Integer id;
11     private String name;
12     private int age;

我们定制了4个SQL语句,其中在insert语句中,我们故意让student_id=100,

在持久化的代码中没有任何特殊,

1         Student s = new Student("abc",22);
2         sess.save(s);

在不定制SQL的情况下,Hibernate将为我们生成 Hibernate: insert into student_inf (age, name) values (?, ?) 这样的SQL语句插入数据,

而在定制SQL的情况下,我们发现Hibernate使用的是我们定制的SQL语句来插入数据,

1 Hibernate: insert into student_inf(student_id,age,name) values(100,?,?)

查看mysql数据库,发现确实插入了一条id为100的记录,

1 MariaDB [test]> select * from student_inf;
2 +------------+-----+------+
3 | student_id | age | name |
4 +------------+-----+------+
5 |          1 |  22 | abc  |
6 |        100 |  22 | abc  |
7 +------------+-----+------+
8 2 rows in set (0.00 sec)

数据过滤

在Hibernate中可以使用@Filter进行数据过滤,其实就是将HQL中的where条件提取到注解中,有点类似于命名SQL一样。

使用@Filter 之前需要先使用@FilterDef定义过滤器,将过滤器应用到具体实体类或者关联属性之后,还需要在session中开启过滤器。下面是一个例子,

首先在Person实体类上定义一个过滤器,

1 //定义一个过滤器
2 @FilterDef(name="eDate"
3 ,parameters={@ParamDef(name="eff_start_date", type="date"),@ParamDef(name="eff_end_date", type="date")})
4 @Entity
5 @Table(name = "person_inf")
6 public class Person
7 {

接着我们在一个关联属性上使用这个过滤器,

 1     @ManyToMany(cascade=CascadeType.ALL, targetEntity=MyEvent.class)
 2     @JoinTable(name = "person_event" ,
 3         joinColumns = @JoinColumn(name = "person_id"
 4             , referencedColumnName="person_id"),
 5         inverseJoinColumns = @JoinColumn(name = "event_id"
 6             , referencedColumnName="event_id")
 7     )
 8     @Filter(name="eDate"
 9     , condition="happenDate BETWEEN :eff_start_date and :eff_end_date")
10     private Set<MyEvent> myEvents
11         = new HashSet<>();

之后,我们还需要在session中开启过滤器,后续装载实体类时,就会自动应用这个过滤器,

 1         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 
 2         sess.enableFilter("eDate")
 3         .setParameter("eff_start_date", sdf.parse("2013-06-01"))
 4         .setParameter("eff_end_date", sdf.parse("2015-06-01"));
 5         
 6         List list1 = sess.createQuery("select distinct e from Person p join p.myEvents e").list();
 7         for( Object ele : list1 ) {
 8             MyEvent e = (MyEvent)ele;
 9             System.out.println(e.getTitle()+","+e.getHappenDate());
10         }

我们看到,Hibernate生成的SQL,自动添加了过滤条件,

 1 Hibernate: 
 2     select
 3         distinct myevent2_.event_id as event_id1_0_,
 4         myevent2_.happenDate as happenDa2_0_,
 5         myevent2_.title as title3_0_ 
 6     from
 7         person_inf person0_ 
 8     inner join
 9         person_event myevents1_ 
10             on person0_.person_id=myevents1_.person_id 
11     inner join
12         event_inf myevent2_ 
13             on myevents1_.event_id=myevent2_.event_id 
14             and myevent2_.happenDate BETWEEN ? and ?
1 回家过年,2013-12-20 00:00:00.0
2 升职加薪,2014-01-01 00:00:00.0
原文地址:https://www.cnblogs.com/fysola/p/6291349.html