(四)关联关系一对多映射
所有项目导入对应的hibernate的jar包、mysql的jar包和添加每次都需要用到的HibernateUtil.java
第一节:班级学生一对多映射实现(单向)
这里的关系是很多学生对应着一个班级,我们首先看着是单向的。
例子:
Class.java
1 package com.wishwzp.model; 2 3 public class Class { 4 5 private long id; 6 private String name; 7 8 public long getId() { 9 return id; 10 } 11 public void setId(long id) { 12 this.id = id; 13 } 14 public String getName() { 15 return name; 16 } 17 public void setName(String name) { 18 this.name = name; 19 } 20 21 22 }
Class.hbm.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC 3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 5 6 <hibernate-mapping package="com.wishwzp.model"> 7 8 <class name="Class" table="t_class"> 9 <id name="id" column="classId"> 10 <generator class="native"></generator> 11 </id> 12 13 <property name="name" column="className"></property> 14 </class> 15 16 </hibernate-mapping>
Student.java
1 package com.wishwzp.model; 2 3 public class Student { 4 5 private long id; 6 private String name; 7 private Class c; 8 9 public long getId() { 10 return id; 11 } 12 public void setId(long id) { 13 this.id = id; 14 } 15 public String getName() { 16 return name; 17 } 18 public void setName(String name) { 19 this.name = name; 20 } 21 22 23 public Class getC() { 24 return c; 25 } 26 public void setC(Class c) { 27 this.c = c; 28 } 29 @Override 30 public String toString() { 31 return "Student [id=" + id + ", name=" + name + "]"; 32 } 33 34 35 }
Student.hbm.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC 3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 5 6 <hibernate-mapping package="com.wishwzp.model"> 7 8 <class name="Student" table="t_student"> 9 <id name="id" column="stuId"> 10 <generator class="native"></generator> 11 </id> 12 13 <property name="name" column="stuName"></property> 14 15 <!-- 学生端是多对一,也就是是多位学生对应一个班级 --> 16 <!-- 这里的name是Student.java中的getC()返回的c --> 17 <!-- 这里的column就是你映射的数据库的表的外键的的名字,就是student表的外键对class表的主键 --> 18 <!-- 这里的class就是多对一,对着那一个的表 --> 19 <many-to-one name="c" column="classId" class="com.wishwzp.model.Class"></many-to-one> 20 </class> 21 22 </hibernate-mapping>
StudentTest.java
1 package com.wishwzp.service; 2 3 import org.hibernate.Session; 4 import org.hibernate.SessionFactory; 5 6 import com.wishwzp.model.Class; 7 import com.wishwzp.model.Student; 8 import com.wishwzp.util.HibernateUtil; 9 10 public class StudentTest { 11 12 public static void main(String[] args) { 13 SessionFactory sessionFactory=HibernateUtil.getSessionFactory(); 14 Session session=sessionFactory.openSession(); // 生成一个session 15 session.beginTransaction(); // 开启事务 16 17 Class c=new Class(); 18 c.setName("08计本"); 19 session.save(c); 20 21 Student s1=new Student(); 22 s1.setName("张三"); 23 s1.setC(c); 24 25 Student s2=new Student(); 26 s2.setName("李四"); 27 s2.setC(c); 28 29 session.save(s1); 30 session.save(s2); 31 32 session.getTransaction().commit(); // 提交事务 33 session.close(); // 关闭session 34 } 35 }
结果显示:
t_class表:
t_student表:
第二节:Junit4 方法详解
第(二)课的第四节我们提到过junit4,这里我们稍微将一下,首先得导入junit4的jar包
百度云:http://pan.baidu.com/s/1dFBbhhZ
密码:3z1i
setUpBeforeClass() 类初始化前调用;
tearDownAfterClass() 类初始化后调用;
setUp() 在测试方法前调用;
tearDown() 在测试方法后调用;
例子:
创建方法:右击-new-junit Test Case
StudentTest2.java
1 package com.wishwzp.service; 2 3 import org.junit.After; 4 import org.junit.AfterClass; 5 import org.junit.Before; 6 import org.junit.BeforeClass; 7 import org.junit.Test; 8 9 public class StudentTest2 { 10 11 @BeforeClass 12 public static void setUpBeforeClass() throws Exception { 13 System.out.println("类初始化前调用..."); 14 } 15 16 @AfterClass 17 public static void tearDownAfterClass() throws Exception { 18 System.out.println("类初始化后调用..."); 19 } 20 21 @Before 22 public void setUp() throws Exception { 23 System.out.println("在测试方法前调用..."); 24 } 25 26 @After 27 public void tearDown() throws Exception { 28 System.out.println("在测试方法后调用..."); 29 } 30 31 @Test 32 public void test() { 33 System.out.println("测试方法"); 34 } 35 36 }
运行方式:右击-Run As-JUnit Test
结果显示:
这里我们用“第一节:班级学生一对多映射实现(单向)”的例子来举例:
第一节:班级学生一对多映射实现(单向)例子的代码都一样不需要更改时(除主类main()除外)
StudentTest3.java
1 package com.wishwzp.service; 2 3 import static org.junit.Assert.*; 4 5 import org.hibernate.Session; 6 import org.hibernate.SessionFactory; 7 import org.junit.After; 8 import org.junit.AfterClass; 9 import org.junit.Before; 10 import org.junit.BeforeClass; 11 import org.junit.Test; 12 13 import com.wishwzp.model.Class; 14 import com.wishwzp.model.Student; 15 import com.wishwzp.util.HibernateUtil; 16 17 public class StudentTest3 { 18 19 private SessionFactory sessionFactory=HibernateUtil.getSessionFactory(); 20 private Session session; 21 22 @Before 23 public void setUp() throws Exception { 24 session=sessionFactory.openSession(); // 生成一个session 25 session.beginTransaction(); // 开启事务 26 } 27 28 @After 29 public void tearDown() throws Exception { 30 session.getTransaction().commit(); // 提交事务 31 session.close(); // 关闭session 32 } 33 34 @Test 35 public void testSaveClassAndStudent() { 36 Class c=new Class(); 37 c.setName("08计本"); 38 session.save(c); 39 40 Student s1=new Student(); 41 s1.setName("张三"); 42 s1.setC(c); 43 44 Student s2=new Student(); 45 s2.setName("李四"); 46 s2.setC(c); 47 48 session.save(s1); 49 session.save(s2); 50 } 51 52 }
我们发现和第一节是一样的。
第三节:级联保存更新
在<many-to-one>这端,cascade 默认是”none”,假如我们希望在持久化多的一端的时候,自动级联保存和更新一的一端,我们可以把cascade 设置成”save-update”;
例子:
第一节:班级学生一对多映射实现(单向)例子的代码都一样不需要更改时(除主类main()除外):
StudentTest4.java
1 package com.wishwzp.service; 2 3 import static org.junit.Assert.*; 4 5 import org.hibernate.Session; 6 import org.hibernate.SessionFactory; 7 import org.junit.After; 8 import org.junit.AfterClass; 9 import org.junit.Before; 10 import org.junit.BeforeClass; 11 import org.junit.Test; 12 13 import com.wishwzp.model.Class; 14 import com.wishwzp.model.Student; 15 import com.wishwzp.util.HibernateUtil; 16 17 public class StudentTest4 { 18 19 private SessionFactory sessionFactory=HibernateUtil.getSessionFactory(); 20 private Session session; 21 22 @Before 23 public void setUp() throws Exception { 24 session=sessionFactory.openSession(); // 生成一个session 25 session.beginTransaction(); // 开启事务 26 } 27 28 @After 29 public void tearDown() throws Exception { 30 session.getTransaction().commit(); // 提交事务 31 session.close(); // 关闭session 32 } 33 34 @Test 35 public void testSaveClassAndStudentWithCascade() { 36 Class c=new Class(); 37 c.setName("08计本"); 38 39 Student s1=new Student(); 40 s1.setName("张三"); 41 s1.setC(c); 42 43 Student s2=new Student(); 44 s2.setName("李四"); 45 s2.setC(c); 46 47 session.save(s1); 48 session.save(s2); 49 } 50 51 }
我们发现Run As-Junit Test 时会发生错误,这是因为我们需要在<many-to-one>这端,cascade 默认是”none”,我们可以把cascade 设置成”save-update”;
错误信息:
Student.hbm.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC 3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 5 6 <hibernate-mapping package="com.wishwzp.model"> 7 8 <class name="Student" table="t_student"> 9 <id name="id" column="stuId"> 10 <generator class="native"></generator> 11 </id> 12 13 <property name="name" column="stuName"></property> 14 15 <!-- 学生端是多对一,也就是是多位学生对应一个班级 --> 16 <!-- 这里的name是Student.java中的getC()返回的c --> 17 <!-- 这里的column就是你映射的数据库的表的外键的的名字,就是student表的外键对class表的主键 --> 18 <!-- 这里的class就是多对一,对着那一个的表 --> 19 <many-to-one name="c" column="classId" class="com.wishwzp.model.Class" cascade="save-update"></many-to-one> 20 </class> 21 22 </hibernate-mapping>
我们再次运行:
结果显示:
t_class表:
t_student表:
第四节:班级学生一对多映射实现(双向)
例子:
hibernate.cfg.xml
1 <?xml version='1.0' encoding='utf-8'?> 2 <!DOCTYPE hibernate-configuration PUBLIC 3 "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> 5 6 <hibernate-configuration> 7 8 <session-factory> 9 10 <!--数据库连接设置 --> 11 <property name="connection.driver_class">com.mysql.jdbc.Driver</property> 12 <property name="connection.url">jdbc:mysql://localhost:3306/hibernate</property> 13 <property name="connection.username">root</property> 14 <property name="connection.password">123456</property> 15 16 17 <!-- 方言 --> 18 <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property> 19 20 <!-- 控制台显示SQL --> 21 <property name="show_sql">true</property> 22 23 <!-- 自动更新表结构 --> 24 <property name="hbm2ddl.auto">update</property> 25 26 <mapping resource="com/wishwzp/model/Student.hbm.xml"/> 27 28 <mapping resource="com/wishwzp/model/Class.hbm.xml"/> 29 30 31 </session-factory> 32 33 </hibernate-configuration>
Class.java
1 package com.wishwzp.model; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 public class Class { 7 8 private long id; 9 private String name; 10 private Set<Student> students=new HashSet<Student>(); 11 12 public long getId() { 13 return id; 14 } 15 public void setId(long id) { 16 this.id = id; 17 } 18 public String getName() { 19 return name; 20 } 21 public void setName(String name) { 22 this.name = name; 23 } 24 public Set<Student> getStudents() { 25 return students; 26 } 27 public void setStudents(Set<Student> students) { 28 this.students = students; 29 } 30 31 32 }
Class.hbm.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC 3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 5 6 <hibernate-mapping package="com.wishwzp.model"> 7 8 <class name="Class" table="t_class"> 9 <id name="id" column="classId"> 10 <generator class="native"></generator> 11 </id> 12 13 <property name="name" column="className"></property> 14 15 <set name="students" cascade="save-update"> 16 <key column="classId"></key> 17 <one-to-many class="com.wishwzp.model.Student"/> 18 </set> 19 </class> 20 21 </hibernate-mapping>
Student.java
1 package com.wishwzp.model; 2 3 public class Student { 4 5 private long id; 6 private String name; 7 private Class c; 8 9 public long getId() { 10 return id; 11 } 12 public void setId(long id) { 13 this.id = id; 14 } 15 public String getName() { 16 return name; 17 } 18 public void setName(String name) { 19 this.name = name; 20 } 21 22 23 public Class getC() { 24 return c; 25 } 26 public void setC(Class c) { 27 this.c = c; 28 } 29 @Override 30 public String toString() { 31 return "Student [id=" + id + ", name=" + name + "]"; 32 } 33 34 35 }
Student.hbm.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC 3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 5 6 <hibernate-mapping package="com.wishwzp.model"> 7 8 <class name="Student" table="t_student"> 9 <id name="id" column="stuId"> 10 <generator class="native"></generator> 11 </id> 12 13 <property name="name" column="stuName"></property> 14 15 <many-to-one name="c" column="classId" class="com.wishwzp.model.Class" cascade="save-update"></many-to-one> 16 </class> 17 18 </hibernate-mapping>
StudentTest.java
1 package com.wishwzp.service; 2 3 import java.util.Iterator; 4 import java.util.Set; 5 6 import org.hibernate.Session; 7 import org.hibernate.SessionFactory; 8 import org.junit.After; 9 import org.junit.Before; 10 import org.junit.Test; 11 12 import com.wishwzp.model.Class; 13 import com.wishwzp.model.Student; 14 import com.wishwzp.util.HibernateUtil; 15 16 public class StudentTest { 17 18 private SessionFactory sessionFactory=HibernateUtil.getSessionFactory(); 19 private Session session; 20 21 @Before 22 public void setUp() throws Exception { 23 session=sessionFactory.openSession(); // 生成一个session 24 session.beginTransaction(); // 开启事务 25 } 26 27 @After 28 public void tearDown() throws Exception { 29 session.getTransaction().commit(); // 提交事务 30 session.close(); // 关闭session 31 } 32 33 @Test 34 public void testSaveClassAndStudent() { 35 Class c=new Class(); 36 c.setName("08计本"); 37 38 Student s1=new Student(); 39 s1.setName("张三"); 40 41 Student s2=new Student(); 42 s2.setName("李四"); 43 44 c.getStudents().add(s1); 45 c.getStudents().add(s2); 46 47 session.save(c); 48 49 } 50 51 }
结果显示:
t_student表:
t_class表:
现在我们要测试通过班级端来查找学生端,这是我们关键的实现目的。来实现双向
StudentTest.java
1 package com.wishwzp.service; 2 3 import java.util.Iterator; 4 import java.util.Set; 5 6 import org.hibernate.Session; 7 import org.hibernate.SessionFactory; 8 import org.junit.After; 9 import org.junit.Before; 10 import org.junit.Test; 11 12 import com.wishwzp.model.Class; 13 import com.wishwzp.model.Student; 14 import com.wishwzp.util.HibernateUtil; 15 16 public class StudentTest { 17 18 private SessionFactory sessionFactory=HibernateUtil.getSessionFactory(); 19 private Session session; 20 21 @Before 22 public void setUp() throws Exception { 23 session=sessionFactory.openSession(); // 生成一个session 24 session.beginTransaction(); // 开启事务 25 } 26 27 @After 28 public void tearDown() throws Exception { 29 session.getTransaction().commit(); // 提交事务 30 session.close(); // 关闭session 31 } 32 33 34 @Test 35 public void getStudentsByClass(){ 36 Class c=(Class)session.get(Class.class, Long.valueOf(1)); 37 Set<Student> students=c.getStudents(); 38 Iterator it=students.iterator(); 39 while(it.hasNext()){ 40 Student s=(Student)it.next(); 41 System.out.println(s); 42 } 43 } 44 45 46 }
结果显示:
我们能从一的这一端去获取多的这一端的信息;也能从一的这一端保存多的一端。这就是我们多对一的好处(这就是实现双向的好处)
第五节:inverse 属性
StudentTest.java
package com.wishwzp.service; import java.util.Iterator; import java.util.Set; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.wishwzp.model.Class; import com.wishwzp.model.Student; import com.wishwzp.util.HibernateUtil; public class StudentTest { private SessionFactory sessionFactory=HibernateUtil.getSessionFactory(); private Session session; @Before public void setUp() throws Exception { session=sessionFactory.openSession(); // 生成一个session session.beginTransaction(); // 开启事务 } @After public void tearDown() throws Exception { session.getTransaction().commit(); // 提交事务 session.close(); // 关闭session } @Test public void testAdd(){ Class c=new Class(); c.setName("09计本"); Student s1=new Student(); s1.setName("王五"); session.save(c); session.save(s1); } }
结果显示:
t_student表:
t_class表:
我们发现这两个表根本没有任何关系,现在我们要将这两个表关联起来。
StudentTest.java
package com.wishwzp.service; import java.util.Iterator; import java.util.Set; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.wishwzp.model.Class; import com.wishwzp.model.Student; import com.wishwzp.util.HibernateUtil; public class StudentTest { private SessionFactory sessionFactory=HibernateUtil.getSessionFactory(); private Session session; @Before public void setUp() throws Exception { session=sessionFactory.openSession(); // 生成一个session session.beginTransaction(); // 开启事务 } @After public void tearDown() throws Exception { session.getTransaction().commit(); // 提交事务 session.close(); // 关闭session } @Test public void testInverse(){ Class c=(Class)session.get(Class.class, Long.valueOf(1)); Student s=(Student)session.get(Student.class, Long.valueOf(1)); s.setC(c);//学生设置班级 c.getStudents().add(s);//班级设置学生 } }
结果显示:
我们发现这两个表有关系了。
这里我们使用inverse属性来实现一下效果:
数据库的效果是一样的,两个数据库相关联了。
但是我们发现更新的sql语句不一样的。
第六节:级联删除
StudentTest.java
package com.wishwzp.service; import java.util.Iterator; import java.util.Set; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.wishwzp.model.Class; import com.wishwzp.model.Student; import com.wishwzp.util.HibernateUtil; public class StudentTest { private SessionFactory sessionFactory=HibernateUtil.getSessionFactory(); private Session session; @Before public void setUp() throws Exception { session=sessionFactory.openSession(); // 生成一个session session.beginTransaction(); // 开启事务 } @After public void tearDown() throws Exception { session.getTransaction().commit(); // 提交事务 session.close(); // 关闭session } @Test public void testDeleteClassCascade(){ Class c=(Class)session.get(Class.class, Long.valueOf(1)); session.delete(c); } }
这里直接运行的话就会出错的。
我们必须要改一下
Class.hbm.xml的<set>中的cascade="delete"才可以。
这样删除班级这张表的数据,学生表里面的数据也会跟着删除。
第七节:一对多双向自身关联关系映射
例子:
Node.java
1 package com.wishwzp.model; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 public class Node { 7 8 private long id; 9 private String name; 10 11 private Node parentNode; 12 13 private Set<Node> childNodes=new HashSet<Node>(); 14 15 public long getId() { 16 return id; 17 } 18 19 public void setId(long id) { 20 this.id = id; 21 } 22 23 public String getName() { 24 return name; 25 } 26 27 public void setName(String name) { 28 this.name = name; 29 } 30 31 public Node getParentNode() { 32 return parentNode; 33 } 34 35 public void setParentNode(Node parentNode) { 36 this.parentNode = parentNode; 37 } 38 39 public Set<Node> getChildNodes() { 40 return childNodes; 41 } 42 43 public void setChildNodes(Set<Node> childNodes) { 44 this.childNodes = childNodes; 45 } 46 47 48 49 }
Node.hbm.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC 3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 5 6 <hibernate-mapping package="com.wishwzp.model"> 7 8 <class name="Node" table="t_node"> 9 <id name="id" column="nodeId"> 10 <generator class="native"></generator> 11 </id> 12 13 <property name="name" column="nodeName"></property> 14 15 <many-to-one name="parentNode" column="parentId" class="com.wishwzp.model.Node" cascade="save-update"></many-to-one> 16 17 <set name="childNodes" inverse="true"> 18 <key column="parentId"></key> 19 <one-to-many class="com.wishwzp.model.Node"/> 20 </set> 21 </class> 22 23 </hibernate-mapping>
NodeTest.java
1 package com.wishwzp.service; 2 3 import org.hibernate.Session; 4 import org.hibernate.SessionFactory; 5 import org.junit.After; 6 import org.junit.Before; 7 import org.junit.Test; 8 9 import com.wishwzp.model.Node; 10 import com.wishwzp.util.HibernateUtil; 11 12 public class NodeTest { 13 14 private SessionFactory sessionFactory=HibernateUtil.getSessionFactory(); 15 private Session session; 16 17 @Before 18 public void setUp() throws Exception { 19 session=sessionFactory.openSession(); // 生成一个session 20 session.beginTransaction(); // 开启事务 21 } 22 23 @After 24 public void tearDown() throws Exception { 25 session.getTransaction().commit(); // 提交事务 26 session.close(); // 关闭session 27 } 28 29 @Test 30 public void testSaveMenu() { 31 Node node=new Node(); 32 node.setName("根节点"); 33 34 Node subNode1=new Node(); 35 subNode1.setName("子节点1"); 36 37 Node subNode2=new Node(); 38 subNode2.setName("子节点2"); 39 40 subNode1.setParentNode(node); 41 subNode2.setParentNode(node); 42 43 session.save(subNode1); 44 session.save(subNode2); 45 } 46 47 48 }
结果显示:
我们发现根节点是老大,已经没有上面的节点了,二子节点1和子节点2它们分别有根节点的。