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 } 37 38 39 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+" 登录日志: "; 59 for (LogEntity log: logs) { 60 str = str+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(log.getLoginDate())+" "; 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(" ----------现有用户:"); 98 sel(); 99 System.out.println(" ----------开始增加用户:"); 100 add(); 101 System.out.println(" ----------增加用户后:"); 102 sel(); 103 System.out.println(" ----------开始修改用户:"); 104 edt(); 105 System.out.println(" ----------修改用户后:"); 106 sel(); 107 System.out.println(" ----------开始删除用户:"); 108 del(); 109 System.out.println(" ----------删除用户后:"); 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中一对多的查询关联,其他关联情况将在后续的博文中讲解。