Hibernate之HQL基本用法
Posted 云中之歌
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate之HQL基本用法相关的知识,希望对你有一定的参考价值。
关于HQL
HQL与SQL非常类似,只不过SQL的操作对象是数据表,列等对象,而HQL操作的是持久化类,实例,属性等。
HQL是完全面向对象的查询语言,因此也具有面向对象的继承,多态等特性。
使用HQL的一般步骤为:
获取session对象
编写HQL语句
使用session的createQuery方法创建查询对象(Query对象)
使用SetXxx(index/para_name, value)为参数复制
使用Query对象的list()方法返回查询结果列表(持久化实体集)
下面演示一下HQL的基本用法,演示之前先附上之前的一个例子,双向N-N关联映射,
假设有下面两个持久化类Person和Event之间成N-N双向关联,代码如下,
Person类
1 package hql; 2 3 import java.util.*; 4 5 import javax.persistence.*; 6 7 8 @Entity 9 @Table(name = "person_inf") 10 public class Person 11 { 12 @Id @Column(name = "person_id") 13 @GeneratedValue(strategy=GenerationType.IDENTITY) 14 private Integer id; 15 private String name; 16 private int age; 17 @ManyToMany(cascade=CascadeType.ALL, targetEntity=MyEvent.class) 18 @JoinTable(name = "person_event" , 19 joinColumns = @JoinColumn(name = "person_id" 20 , referencedColumnName="person_id"), 21 inverseJoinColumns = @JoinColumn(name = "event_id" 22 , referencedColumnName="event_id") 23 ) 24 private Set<MyEvent> myEvents 25 = new HashSet<>(); 26 @ElementCollection(targetClass=String.class) 27 @CollectionTable(name="person_email_inf", 28 joinColumns=@JoinColumn(name="person_id" , nullable=false)) 29 @Column(name="email_detail" , nullable=false) 30 private Set<String> emails 31 = new HashSet<>(); 32 33 public void setId(Integer id) 34 { 35 this.id = id; 36 } 37 public Integer getId() 38 { 39 return this.id; 40 } 41 42 public void setName(String name) 43 { 44 this.name = name; 45 } 46 public String getName() 47 { 48 return this.name; 49 } 50 51 public void setAge(int age) 52 { 53 this.age = age; 54 } 55 public int getAge() 56 { 57 return this.age; 58 } 59 60 public void setMyEvents(Set<MyEvent> myEvents) 61 { 62 this.myEvents = myEvents; 63 } 64 public Set<MyEvent> getMyEvents() 65 { 66 return this.myEvents; 67 } 68 69 public void setEmails(Set<String> emails) 70 { 71 this.emails = emails; 72 } 73 public Set<String> getEmails() 74 { 75 return this.emails; 76 } 77 public Person() {} 78 public Person(String name, int age) { 79 this.name = name; 80 this.age = age; 81 } 82 83 }
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 }
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 }
首先执行上面的PersonManager,我们需要生成数据表如下,
MariaDB [test]> select * from event_inf; |
MariaDB [test]> select * from person_event; |
MariaDB [test]> select * from person_inf; |
MariaDB [test]> select * from person_email_inf; |
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 参加工作
Select 子句
select子句接单个持久化类
如果select后面只查询单个持久化类,那么返回的查询结果是一个集合,每一个集合元素可以直接通过强制类型转换还原成原来的数据类型,例如
1 List list0 = sess 2 .createQuery("select p from Person p").list();
首先可以用for(Object ele : list0)遍历结果集,对每一个元素可以直接强制转换成原来的数据类型,
1 for (Object ele : list0) { 2 Person p = (Person)ele; 3 System.out.println(p.getName()); 4 }
select子句接持久化类和属性混合
select子句可以接持久化类,或者其属性,通常select子句查询的结果就是一个集合,集合的每个元素都是数组,相当于返回的是一个二维数组结果集,
1 List pl = sess 2 .createQuery( 3 "select p.name, e from Person p join p.myEvents e " 4 + "where e.title = :title") 5 .setString("title", "回家过年").list();
因此需要先将每个集合元素还原成数组,再次将数组每一项强制换换成对应数据类型,
1 for (Object ele : pl) { 2 Object[] objs = (Object[]) ele; 3 String name = (String) objs[0]; 4 MyEvent e = (MyEvent) objs[1]; 5 System.out.println(name + "," + e.getTitle()); 6 }
首先上面sess.createQuery(xxx).list()返回的是一个集合,因此用for(Object ele : pl)遍历每一个元素,每一个元素又是一个数组,因此用Object[] objs = (Object[])ele; 强制转换去还原集合每一个元素,而对于数组每一项的数据类型,则是根据HQL中select后面的顺序,逐一匹配,因此在for循环里用来String和MyEvent来还原数组每一项。
这是select最通用的用法。
select 子句直接生成list对象或者map对象
Query对象返回的集合中,每一个元素就是一个list对象,每个list对象,例如像下面这样,
1 List list1 = sess 2 .createQuery( 3 "select new list(p.name, p.age, e) from Person p left join p.emails e") 4 .list();
返回的集合中,每一个元素就是list对象,用强制类型转换还原即可,遍历每一个list对象,就是select后面list中的每个元素,
1 for (Object ele : list1) { 2 List name = (List) ele; 3 Iterator it = name.iterator(); 4 while (it.hasNext()) { 5 System.out.println(it.next()); 6 } 7 }
当然也可以在select子句之后直接生成map对象,使用别名p.name as pname作为map的key,其实际值作为value,
相当于Query对象通过.list()方法返回的结果集中,包含了n个map对象,每个map对象里都只有一个key-value对,每个key名字都交pname,
1 List list2 = sess 2 .createQuery( 3 "select new map(p.name as pname) from Person p") 4 .list();
可以像下面这样遍历map,
1 for (Object ele : list2) { 2 Map mName = (HashMap) ele; 3 Set keySet = (Set)mName.keySet(); 4 Iterator it = keySet.iterator(); 5 while( it.hasNext()) { 6 Object key = it.next(); 7 System.out.println(key+"->"+mName.get(key)); 8 } 9 }
输出结果,
1 pname->张三 2 pname->李四
select子句甚至可以直接跟持久化类的构造函数
1 List list3 = sess 2 以上是关于Hibernate之HQL基本用法的主要内容,如果未能解决你的问题,请参考以下文章Hibernate hql语句 find_in_set 用法