zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    React antd如何实现<Upload>组件上传附件再次上传已清除附件缓存问题。
    spring项目logback日志与logstash和Elasticsearch整合
    Java后端面试经验总结分享(一)
    【设计模式】访问者模式
    【设计模式】命令模式
    【设计模式】模板方法模式
    【设计模式】代理模式
    【设计模式】享元模式
    【设计模式】外观模式
    【设计模式】组合模式
  • 原文地址:https://www.cnblogs.com/fysola/p/6291349.html
Copyright © 2011-2022 走看看