1. 前言

本章节我们讨论Hibernate一对多查询的处理。

在上一章节中(Hibernate(一)——入门),我们探讨了Hibernate执行最基本的增删改查操作。现在我们将情况复杂化:加入我们在查询用户信息的时候需要同时查询其登录日志,这样就涉及到一对多查询。那么一对多查询要怎么实现么?

2. jar包准备

在本节中,除了上一章节中用到的jar包,我还需要用log4j.jar来将Hibernate的查询语句输出到控制台。log4j.properties的配置如下:

1 log4j.rootLogger=info,console
  2 log4j.appender.console=org.apache.log4j.ConsoleAppender
  3 log4j.appender.console.layout=org.apache.log4j.PatternLayout
  4 log4j.appender.console.layout.ConversionPattern=%d %p [%c] - %m%n

 log4j的使用方法可查阅:Mybatis之一级缓存(七)中,log4j的学习和使用部分。

3. 数据库准备

我们需要新建立日志表tbLog,并产生部分的测试数据。代码如下

1 CREATE TABLE tbLog (
2     logID VARCHAR(50),
3     userID VARCHAR(50),
4     loginDate DATETIME
5 )
1 TRUNCATE TABLE tbUser
  2 TRUNCATE TABLE tbLog
  3 
  4 DECLARE @userID1 VARCHAR(50)
  5 DECLARE @userID2 VARCHAR(50)
  6 SET @userID1 = NEWID();
  7 SET @userID2 = NEWID();
  8 
  9 INSERT INTO tbUser(userID, loginName, userName, passWord)
 10 SELECT @userID1,'luych','卢艳超','12333' UNION ALL
 11 SELECT @userID2,'guest','游客','12333'
 12 
 13 INSERT INTO tbLog(logID, userID, loginDate)
 14 SELECT NEWID(), @userID1, '2016-04-01' UNION ALL
 15 SELECT NEWID(), @userID1, '2016-04-02' UNION ALL
 16 SELECT NEWID(), @userID1, '2016-04-05' UNION ALL
 17 SELECT NEWID(), @userID1, '2016-04-08' UNION ALL
 18 
 19 SELECT NEWID(), @userID2, '2016-04-11' UNION ALL
 20 SELECT NEWID(), @userID2, '2016-04-22'
 21 
 22 SELECT * FROM tbUser;
 23 SELECT * FROM tbLog;

4. 准备JAVA对象

(1)建立与数据表tbLog相对应的JAVA对象,代码如下:

1 package com.luych.hibernate.study.entity;
  2 
  3 import java.util.Date;
  4 
  5 import javax.persistence.Entity;
  6 import javax.persistence.Id;
  7 import javax.persistence.Table;
  8 
  9 
 10 @Entity
 11 @Table(name="tbLog")
 12 public class LogEntity {
 13 
 14     @Id
 15     private String logID;
 16     private String userID;
 17     private Date loginDate;
 18 
 19     public String getLogID() {
 20         return logID;
 21     }
 22     public void setLogID(String logID) {
 23         this.logID = logID;
 24     }
 25     public String getUserID() {
 26         return userID;
 27     }
 28     public void setUserID(String userID) {
 29         this.userID = userID;
 30     }
 31     public Date getLoginDate() {
 32         return loginDate;
 33     }
 34     public void setLoginDate(Date loginDate) {
 35         this.loginDate = loginDate;
 36     }
 40 }

当然,我们同时也要在Hibernate的xml中增加相应的配置

1 <mapping class="com.luych.hibernate.study.entity.LogEntity"/>

(2)调整UserEntity对象,建立其与LogEntity的一对多关系。

1 package com.luych.hibernate.study.entity;
  2 
  3 import java.text.SimpleDateFormat;
  4 import java.util.Set;
  5 
  6 import javax.persistence.CascadeType;
  7 import javax.persistence.Entity;
  8 import javax.persistence.Id;
  9 import javax.persistence.JoinColumn;
 10 import javax.persistence.OneToMany;
 11 import javax.persistence.Table;
 12 
 13 @Entity
 14 @Table(name="tbUser")
 15 public class UserEntity {
 16 
 17     @Id
 18     private String userID;
 19     private String loginName;
 20     private String userName;
 21     private String passWord;
 22     @OneToMany(cascade=CascadeType.ALL)
 23     @JoinColumn(name="userID")
 24     private Set<LogEntity> logs;
 25 
 26     public String getUserID() {
 27         return userID;
 28     }
 29     public void setUserID(String userID) {
 30         this.userID = userID;
 31     }
 32     public String getLoginName() {
 33         return loginName;
 34     }
 35     public void setLoginName(String loginName) {
 36         this.loginName = loginName;
 37     }
 38     public String getUserName() {
 39         return userName;
 40     }
 41     public void setUserName(String userName) {
 42         this.userName = userName;
 43     }
 44     public String getPassWord() {
 45         return passWord;
 46     }
 47     public void setPassWord(String passWord) {
 48         this.passWord = passWord;
 49     }
 50     public Set<LogEntity> getLogs() {
 51         return logs;
 52     }
 53     public void setLogs(Set<LogEntity> logs) {
 54         this.logs = logs;
 55     }
 56     @Override
 57     public String toString() {
 58         String str = loginName+", "+userName+", "+passWord+", "+userID+" 登录日志:\n";
 59         for (LogEntity log: logs) {
 60             str = str+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(log.getLoginDate())+"\n";
 61         }
 62         return str;
 63     }
 64 }
 65

 其中,

@OneToMany的cascade可取值为:

CascadeType.PERSIST:级联新建,本例中即生成User的时候同时生成Log。
CascadeType.REMOVE : 级联删除,本例中即删除User的时候同时删除Log。
CascadeType.REFRESH:级联刷新,本例中即查询User的时候同时查询Log。
CascadeType.MERGE  :级联更新,本例中即修改User的时候同时修改Log。
CascadeType.ALL    :以上全部四项,即上面四个全都执行。

@JoinColumn的name取值为:LogEntity中的userID属性。

5. 调整Hibernate配置文件

1 <?xml version="1.0" encoding="UTF-8"?>
  2 <!DOCTYPE hibernate-configuration PUBLIC
  3         "-//Hibernate/Hibernate Configuration DTD 5.0//EN"
  4         "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
  5 <hibernate-configuration>
  6     <session-factory>
  7         <!-- 设置数据库驱动 -->
  8         <property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
  9         <!-- 设置数据库URL -->
 10         <property name="hibernate.connection.url">jdbc:sqlserver://192.168.9.23:14433;databaseName=tempdb</property>
 11         <!-- 数据库用户名 -->
 12         <property name="hibernate.connection.username">sa</property>
 13         <!-- 数据库密码 -->
 14         <property name="hibernate.connection.password">123@abcd</property>
 15         <!-- 打印sql -->
 16         <property name="show_sql">true</property>
 17         <property name="format_sql">false</property>
 18         <!-- beans -->
 19         <mapping class="com.luych.hibernate.study.entity.UserEntity"/>
 20         <mapping class="com.luych.hibernate.study.entity.LogEntity"/>
 21     </session-factory>
 22 </hibernate-configuration>

 我们增加了针对show_sql和format_sql的配置,加上这两个配置后,Hibernate会输出执行的SQL脚本。

  • show_sql:true,输出SQL脚本。false,不输出。
  • format_sql:true,格式化SQL脚本。false,不格式化。

 本例中,并没有将format_sql设置为true,是因为格式化的SQL在控制台中显示很占篇幅,不利于我们后面看测试结果,所以关闭了。

6. 测试运行结果

1 package com.luych.hibernate.study.main;
  2 
  3 import java.util.Date;
  4 import java.util.HashSet;
  5 import java.util.List;
  6 import java.util.Set;
  7 import java.util.UUID;
  8 
  9 import org.hibernate.Query;
 10 import org.hibernate.Session;
 11 import org.hibernate.SessionFactory;
 12 import org.hibernate.cfg.Configuration;
 13 import org.junit.After;
 14 import org.junit.Before;
 15 import org.junit.Test;
 16 
 17 import com.luych.hibernate.study.entity.LogEntity;
 18 import com.luych.hibernate.study.entity.UserEntity;
 19 
 20 @SuppressWarnings("unchecked")
 21 public class TestMain {
 22 
 23     private Session session;
 24 
 25     @Before
 26     public void getSession(){
 27         Configuration config = new Configuration().configure("hibernate-config.xml");
 28         SessionFactory sessionFactory = config.buildSessionFactory();
 29         session = sessionFactory.openSession();
 30     }
 31 
 32     @After
 33     public void freeSession(){
 34         session.close();
 35     }
 36 
 37     public void sel() {
 38         Query query = session.createQuery("FROM UserEntity WHERE 1=1");
 39         List<UserEntity> userList = query.list();
 40         for (UserEntity userEntity : userList) {
 41             System.out.println(userEntity.toString());
 42         }
 43     }
 44 
 45     public void add() {
 46         session.beginTransaction();
 47         String userID = UUID.randomUUID().toString();
 48         UserEntity user  = new UserEntity();
 49         user.setLoginName("admin");
 50         user.setUserName("系统管理员");
 51         user.setPassWord("12333");
 52         user.setUserID(userID);
 53         LogEntity log1 = new LogEntity();
 54         log1.setLogID(UUID.randomUUID().toString());
 55         log1.setUserID(userID);
 56         log1.setLoginDate(new Date());
 57         LogEntity log2 = new LogEntity();
 58         log2.setLogID(UUID.randomUUID().toString());
 59         log2.setUserID(userID);
 60         log2.setLoginDate(new Date());
 61         Set<LogEntity> logs = new HashSet<LogEntity>();
 62         logs.add(log1);
 63         logs.add(log2);
 64         user.setLogs(logs);
 65         session.save(user);
 66         session.getTransaction().commit();
 67     }
 68 
 69     public void edt(){
 70         session.beginTransaction();
 71         Query query = session.createQuery("FROM UserEntity WHERE 1=1");
 72         List<UserEntity> userList = query.list();
 73         for (UserEntity userEntity : userList) {
 74             userEntity.setPassWord("45666");
 75             LogEntity log = new LogEntity();
 76             log.setLogID(UUID.randomUUID().toString());
 77             log.setUserID(userEntity.getUserID());
 78             log.setLoginDate(new Date());
 79             userEntity.getLogs().add(log);
 80             session.update(userEntity);
 81         }
 82         session.getTransaction().commit();
 83     }
 84 
 85     public void del(){
 86         session.beginTransaction();
 87         Query query = session.createQuery("FROM UserEntity WHERE 1=1");
 88         List<UserEntity> userList = query.list();
 89         for (UserEntity userEntity : userList) {
 90             session.delete(userEntity);
 91         }
 92         session.getTransaction().commit();
 93     }
 94 
 95     @Test
 96     public void test(){
 97         System.out.println("\n----------现有用户:");
 98         sel();
 99         System.out.println("\n----------开始增加用户:");
100         add();
101         System.out.println("\n----------增加用户后:");
102         sel();
103         System.out.println("\n----------开始修改用户:");
104         edt();
105         System.out.println("\n----------修改用户后:");
106         sel();
107         System.out.println("\n----------开始删除用户:");
108         del();
109         System.out.println("\n----------删除用户后:");
110         sel();
111     }
112 }

getSession和freeSession和上一章节中相同,不再赘述。

add方法,新建了一个用户并设定了两条登录日志,然后保存。edt方法,将所有用户的密码改为45666,并为所有的用户增加一条登录日志。del方法,删除所有的用户。sel方法,查询所有用户信息并输出到控制台。

右键,Run As JUnit Test后,控制台输出结果为:

1 
  2 ----------现有用户:
  3 Hibernate: select userentity0_.userID as userID1_1_, userentity0_.loginName as loginNam2_1_,
userentity0_.passWord as passWord3_1_, userentity0_.userName as userName4_1_
from tbUser userentity0_ where 1=1
  4 Hibernate: select logs0_.userID as userID3_0_0_, logs0_.logID as logID1_0_0_,
logs0_.logID as logID1_0_1_, logs0_.loginDate as loginDat2_0_1_, logs0_.userID as userID3_0_1_
from tbLog logs0_ where logs0_.userID=?
  5 
  6 luych, 卢艳超, 12333, CB6172E3-8750-4718-BEF6-EE0917015FA9 登录日志:
  7 2016-04-01 00:00:00
  8 2016-04-08 00:00:00
  9 2016-04-05 00:00:00
 10 2016-04-02 00:00:00
 11 
 12 Hibernate: select logs0_.userID as userID3_0_0_, logs0_.logID as logID1_0_0_,
logs0_.logID as logID1_0_1_, logs0_.loginDate as loginDat2_0_1_, logs0_.userID as userID3_0_1_
from tbLog logs0_ where logs0_.userID=?
 13 
 14 guest, 游客, 12333, 21539577-A3D1-4A1F-8D10-6ED0540A46A0 登录日志:
 15 2016-04-11 00:00:00
 16 2016-04-22 00:00:00
 17 
 18 
 19 ----------开始增加用户:
 20 Hibernate: select logentity_.logID, logentity_.loginDate as loginDat2_0_, logentity_.userID as userID3_0_
from tbLog logentity_ where logentity_.logID=?
 21 Hibernate: select logentity_.logID, logentity_.loginDate as loginDat2_0_, logentity_.userID as userID3_0_
from tbLog logentity_ where logentity_.logID=?
 22 Hibernate: insert into tbUser (loginName, passWord, userName, userID) values (?, ?, ?, ?)
 23 Hibernate: insert into tbLog (loginDate, userID, logID) values (?, ?, ?)
 24 Hibernate: insert into tbLog (loginDate, userID, logID) values (?, ?, ?)
 25 Hibernate: update tbLog set userID=? where logID=?
 26 Hibernate: update tbLog set userID=? where logID=?
 27 
 28 ----------增加用户后:
 29 Hibernate: select userentity0_.userID as userID1_1_, userentity0_.loginName as loginNam2_1_,
userentity0_.passWord as passWord3_1_, userentity0_.userName as userName4_1_
from tbUser userentity0_ where 1=1
 30 
 31 luych, 卢艳超, 12333, CB6172E3-8750-4718-BEF6-EE0917015FA9 登录日志:
 32 2016-04-01 00:00:00
 33 2016-04-08 00:00:00
 34 2016-04-05 00:00:00
 35 2016-04-02 00:00:00
 36 
 37 
 38 guest, 游客, 12333, 21539577-A3D1-4A1F-8D10-6ED0540A46A0 登录日志:
 39 2016-04-11 00:00:00
 40 2016-04-22 00:00:00
 41 
 42 
 43 admin, 系统管理员, 12333, 99d5d264-9d02-4e45-a8c5-f710cc14107e 登录日志:
 44 2016-04-26 17:06:00
 45 2016-04-26 17:06:00
 46 
 47 
 48 ----------开始修改用户:
 49 Hibernate: select userentity0_.userID as userID1_1_, userentity0_.loginName as loginNam2_1_,
userentity0_.passWord as passWord3_1_, userentity0_.userName as userName4_1_
from tbUser userentity0_ where 1=1
 50 Hibernate: select logentity_.logID, logentity_.loginDate as loginDat2_0_,
logentity_.userID as userID3_0_
from tbLog logentity_ where logentity_.logID=?
 51 Hibernate: select logentity_.logID, logentity_.loginDate as loginDat2_0_,
logentity_.userID as userID3_0_
from tbLog logentity_ where logentity_.logID=?
 52 Hibernate: select logentity_.logID, logentity_.loginDate as loginDat2_0_,
logentity_.userID as userID3_0_
from tbLog logentity_ where logentity_.logID=?
 53 Hibernate: insert into tbLog (loginDate, userID, logID) values (?, ?, ?)
 54 Hibernate: insert into tbLog (loginDate, userID, logID) values (?, ?, ?)
 55 Hibernate: insert into tbLog (loginDate, userID, logID) values (?, ?, ?)
 56 Hibernate: update tbUser set loginName=?, passWord=?, userName=? where userID=?
 57 Hibernate: update tbUser set loginName=?, passWord=?, userName=? where userID=?
 58 Hibernate: update tbUser set loginName=?, passWord=?, userName=? where userID=?
 59 Hibernate: update tbLog set userID=? where logID=?
 60 Hibernate: update tbLog set userID=? where logID=?
 61 Hibernate: update tbLog set userID=? where logID=?
 62 
 63 ----------修改用户后:
 64 Hibernate: select userentity0_.userID as userID1_1_, userentity0_.loginName as loginNam2_1_,
userentity0_.passWord as passWord3_1_, userentity0_.userName as userName4_1_
from tbUser userentity0_ where 1=1
 65 
 66 luych, 卢艳超, 45666, CB6172E3-8750-4718-BEF6-EE0917015FA9 登录日志:
 67 2016-04-01 00:00:00
 68 2016-04-08 00:00:00
 69 2016-04-05 00:00:00
 70 2016-04-02 00:00:00
 71 2016-04-26 17:06:00
 72 
 73 
 74 guest, 游客, 45666, 21539577-A3D1-4A1F-8D10-6ED0540A46A0 登录日志:
 75 2016-04-11 00:00:00
 76 2016-04-22 00:00:00
 77 2016-04-26 17:06:00
 78 
 79 
 80 admin, 系统管理员, 45666, 99d5d264-9d02-4e45-a8c5-f710cc14107e 登录日志:
 81 2016-04-26 17:06:00
 82 2016-04-26 17:06:00
 83 2016-04-26 17:06:00
 84 
 85 
 86 ----------开始删除用户:
 87 Hibernate: select userentity0_.userID as userID1_1_, userentity0_.loginName as loginNam2_1_,
userentity0_.passWord as passWord3_1_, userentity0_.userName as userName4_1_
from tbUser userentity0_ where 1=1
 88 Hibernate: update tbLog set userID=null where userID=?
 89 Hibernate: update tbLog set userID=null where userID=?
 90 Hibernate: update tbLog set userID=null where userID=?
 91 Hibernate: delete from tbLog where logID=?
 92 Hibernate: delete from tbLog where logID=?
 93 Hibernate: delete from tbLog where logID=?
 94 Hibernate: delete from tbLog where logID=?
 95 Hibernate: delete from tbLog where logID=?
 96 Hibernate: delete from tbUser where userID=?
 97 Hibernate: delete from tbLog where logID=?
 98 Hibernate: delete from tbLog where logID=?
 99 Hibernate: delete from tbLog where logID=?
100 Hibernate: delete from tbUser where userID=?
101 Hibernate: delete from tbLog where logID=?
102 Hibernate: delete from tbLog where logID=?
103 Hibernate: delete from tbLog where logID=?
104 Hibernate: delete from tbUser where userID=?
105 
106 ----------删除用户后:
107 Hibernate: select userentity0_.userID as userID1_1_, userentity0_.loginName as loginNam2_1_,
userentity0_.passWord as passWord3_1_, userentity0_.userName as userName4_1_
from tbUser userentity0_ where 1=1
108

从打印结果中,我们可以看到,新增、编辑、删除用户信息的时候,Hibernate都帮我们完成登录日志的新增、删除、操作。查询的时候也如此。

但是需要提点的是:在Hibernate第一次查询中,我们看到它先查询了tbUser表,然后针对tbUser表的每一个记录都又查询了下tbLog表,这就是经典的N+1查询问题,所以效率嘛…

以上就是Hibernate中一对多的查询关联,其他关联情况将在后续的博文中讲解。