一:关联关系
1.关联关系的方向可分为单向关联和双向关联
单向关联:假设存在两张表province表和City表,如果在应用的业务逻辑中,仅需要每个province实例能够查询得到其对应的City实例,而City实例并不需要查询得到其对应的province实例;或者反之。
双向关联:既需要每个province实例能够查询得到其对应的City实例,City实例也需要查询得到其对应的province实例。
2.关联的数量,根据拥有被关联对象的个数确定
多对一(many to one):如用户和组 学生和班级
一对多(one to many):如用户和电子邮件
多对多(many to many):如学生选课
一对一(one to one):如用户和身份证
二.单向多对一
单向多对一关联是最常见的单向关联关系,如:多个城市属于同一个省份。之所以叫他多对一而不是一对多,是因为他们之间的关系是多的一方来维护的
多个城市属于一个省份,我们用多的一方来维护,所以我们可以根据城市可以知道他在哪个省份,而不需要知道一个省份里有哪些城市,这就是所谓的单向的。
多对一映射原理:
在多的一端加入一个外键(FK)指向一的一端,它维护的关系是多指向一
一对多映射原理:
在多的一端加入一个外键(FK)指向一的一端,它维护的关系是一指向多
2.重要属性 - cascade(级联)
级联的意思是指定两个对象之间的操作联动关系,对一个对象执行了操作之后,对其指定的级联对象也需要执行相同的操作
总共可以取值为:all、none、save-update、delete
all-代表在所有的情况下都执行级联操作
none-在所有情况下都不执行级联操作
save-update-在保存和更新的时候执行级联操作
delete-在删除的时候执行级联操作
三.单向一对多
所谓单向一对多,就是实体之间的关系由“一” 的一端加载“多” 的一端,关系由“一”的一端来维护,在JavaBean中是在“一”的一端中持有“多”的一端的集合,Hibernate把这种关系反映到数据库的策略是在“多”的一端的表上加一个外键指向“一” 的一端的表的主键(PK)。比如Province(省份)和City(城市)之间是一对多的关系。一个省份里面有很多的城市,站在省份的角度上来看就是一个省份对应多个城市。在一的一端含有一个多的引用的集合(set),我们可以根据省份找到它有哪些城市,而不能根据城市找到他对应的省份
四:双向一对多关联
所谓双向一对多关联,同时配置单向一对多和单向多对一就成了双向一对多关联,上面两种都是单向的,但是在实际开发过程中,很多时候都是需要双向关联的,它在解决单向一对多维护关系的过程中存在的缺陷起了一定的修补作用
五.注意事项
1.一对多双向关联映:
在一的一端的集合上使用<key>,在对方表中加入一个外键指向一的一端
在多一端采用<many-to-one>
2.key标签指定的外键字段必须和<many-to-one>指定的外键字段一致,否则引用字段错误
3.如果在一的一端维护一对多关联关系,hibernate会发出多余的Update语句,多以我们一般在多的一端维护关联关系
4.关于inverse属性;
inverse主要用在一对多,多对对双向关联上,inverse可以设置到<set>集合上,
默认inverse为false,所以我们可以从一的一端和多的一端来维护关联关系,如果inverse为true,我们只能从多的一端来维护关联关系,注意:inverse属性,只影响存储(使存储方向转变),即持久
5.区分inverse和cascade
Inverse:负责控制关系,默认为false,也就是关系的两端都能控制,但这样会造成一些问题,更新的时候会因为两端都控制关系,于是重复更新。一般来说有一端要设为true。
Cascade:负责控制关联对象的级联操作,包括更新、删除等,也就是说对一个对象进行更新、删除时,其它对象也受影响,比如我删除一个对象,那么跟它是多对一关系的对象也全部被删除。
举例说明区别:删除“一”那一端一个对象O的时候,如果“多”的那一端的Inverse设为true,则把“多”的那一端所有与O相关联的对象外键清空;如果“多”的那一端的Cascade设为Delete,则把“多”的那一端所有与O相关联的对象全部删除。
持久化类多的一端City.java
1 持久化类多的一端City.java 2 3 package com.edu.many2one; 4 /** 5 * 持久化类 6 * @author Administrator 7 * 多的一端 8 */ 9 public class City { 10 11 private Integer id; 12 private String name; 13 14 //关联属性 15 private Province pro; //必须有 16 17 public Integer getId() { 18 return id; 19 } 20 21 public void setId(Integer id) { 22 this.id = id; 23 } 24 25 public String getName() { 26 return name; 27 } 28 29 public void setName(String name) { 30 this.name = name; 31 } 32 33 public Province getPro() { 34 return pro; 35 } 36 37 public void setPro(Province pro) { 38 this.pro = pro; 39 } 40 41 42 }
持久化类一的一端Province.java
1 持久化类一的一端Province.java 2 3 package com.edu.many2one; 4 5 import java.util.HashSet; 6 import java.util.Set; 7 8 /** 9 * 持久化类 10 * @author Administrator 11 * 一的一端 12 */ 13 public class Province { 14 15 private Integer id; 16 private String name; 17 18 //关联属性 19 private Set<City> cities = new HashSet<City>(); //多的一方放在集合中 20 21 public Integer getId() { 22 return id; 23 } 24 25 public void setId(Integer id) { 26 this.id = id; 27 } 28 29 public String getName() { 30 return name; 31 } 32 33 public void setName(String name) { 34 this.name = name; 35 } 36 37 public Set<City> getCities() { 38 return cities; 39 } 40 41 public void setCities(Set<City> cities) { 42 this.cities = cities; 43 } 44 45 46 }
各个实体类配置文件
City.hbm.xml
1 City配置文件 2 3 <?xml version="1.0" encoding="UTF-8"?> 4 <!DOCTYPE hibernate-mapping PUBLIC 5 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 6 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 7 <hibernate-mapping package="com.edu.many2one"> 8 <class name="City"> 9 <id name="id"> 10 <generator class="increment"/> 11 </id> 12 13 <property name="name"/> 14 15 <!-- 映射关联属性 --> 16 <!-- pro 和 Province类 多对一 17 name: 关联属性的名字 18 class: 关联类的类名 19 column: 数据库外键字段的名字 20 21 --> 22 <many-to-one name="pro" class="Province" column="pid" 23 cascade="save-update"/> 24 </class> 25 26 </hibernate-mapping>
Province.hbm.xml
1 Province配置文件 2 3 <?xml version="1.0" encoding="UTF-8"?> 4 <!DOCTYPE hibernate-mapping PUBLIC 5 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 6 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 7 <hibernate-mapping package="com.edu.many2one"> 8 <class name="Province"> 9 <id name="id"> 10 <generator class="increment"/> 11 </id> 12 13 <property name="name"/> 14 15 <!-- 关联属性 16 cascade属性:级联操作默认值为none 17 save-update:级联保存或更新 18 delete:级联删除 19 all 20 inverse="true":默认值为false。主控方 21 true 反转控制权,变为被控方,没有维护关联关系的权利。 22 --> 23 <!-- cities 和City类 一对多 --> 24 <set name="cities" cascade="delete" inverse="true"> 25 <key column="pid"/> <!-- 确定关联的外键列 --> 26 <one-to-many class="City"/> 27 </set> 28 </class> 29 30 31 </hibernate-mapping>
hibernate.cfg.xml
1 hibernate配置文件 2 3 <?xml version="1.0" encoding="UTF-8"?> 4 <!DOCTYPE hibernate-configuration PUBLIC 5 "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 6 "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> 7 <hibernate-configuration> 8 <session-factory> 9 <!-- 配置数据库连接的基本信息 --> 10 <property name="hibernate.connection.driver_class"> 11 oracle.jdbc.driver.OracleDriver 12 </property> 13 <property name="hibernate.connection.url"> 14 jdbc:oracle:thin:@localhost:1521:XE 15 </property> 16 <property name="hibernate.connection.username">lihengyu</property> 17 <property name="hibernate.connection.password">lihengyu</property> 18 19 <!-- 数据库的方言 使用的是哪种数据库 --> 20 <property name="hibernate.dialect"> 21 org.hibernate.dialect.Oracle10gDialect 22 </property> 23 <!-- 显示sql语句 --> 24 <property name="hibernate.show_sql">true</property> 25 <!-- 自动创建数据库表 26 none:默认值 有表就使用,没表不创建 27 create:每次都创建新表 28 update: 有表就使用,没表创建。如果持久化类及映射文件发生改变,则发生alter语句修改表结构。 29 --> 30 <property name="hibernate.hbm2ddl.auto">update</property> 31 32 <!-- 添加映射文件 --> 33 <mapping resource="com/edu/many2one/City.hbm.xml" /> 34 <mapping resource="com/edu/many2one/Province.hbm.xml" /> 35 36 </session-factory> 37 38 39 </hibernate-configuration>
代码测试
1 TestHibernate 测试 2 3 package com.edu.many2one; 4 5 import org.hibernate.SessionFactory; 6 import org.hibernate.Transaction; 7 import org.hibernate.cfg.Configuration; 8 import org.hibernate.classic.Session; 9 import org.junit.Test; 10 11 /** 12 * 使用单元测试工具,测试hibernate 的增删改查(CRUD)操作 13 * 14 * @author Administrator 15 * 16 */ 17 public class TestHibernate { 18 19 private static SessionFactory sf; 20 static { 21 // 1. 加载配置文件 22 Configuration cfg = new Configuration(); 23 cfg.configure("com/edu/many2one/hibernate.cfg.xml"); 24 25 // 2. 获得SessionFactory 26 sf = cfg.buildSessionFactory(); 27 } 28 29 30 /** 31 * 保存用户 32 * 1. 保存Province 不保存City 33 * 2. 保存City 不保存Province 34 * 3. 先保存Province,再保存City 35 * 4. 先保存City,再保存Province 36 */ 37 @Test 38 public void save() { 39 40 41 Session session = sf.openSession(); 42 Transaction tran = session.beginTransaction(); 43 44 Province p = new Province(); 45 p.setName("吉林省"); 46 47 City c = new City(); 48 c.setName("长春市"); 49 50 //维护关联关系 51 c.setPro(p); 52 p.getCities().add(c); 53 54 //保存对象 55 /* 56 * 1.保存Province 不保存City city对象是临时状态,不能操作数据库 57 * TransientObjectException: 临时对象异常 58 session.save(p); 59 */ 60 61 /* 62 * 2. 保存City 不保存Province 63 * TransientObjectException: 临时对象异常 64 */ 65 session.save(c); 66 67 /* 68 * 3. 先保存Province,再保存City 69 * 70 * 发送几条sql语句 71 * insert into Province (name, id) values (?, ?) 72 insert into City (name, pid, id) values (?, ?, ?) 73 update City set pid=? where id=? 74 session.save(p); 75 session.save(c); 76 */ 77 /* 78 * 4. 先保存City,再保存Province 79 * 80 * 发送4条sql语句 81 * insert into City (name, pid, id) values (?, ?, ?) 82 insert into Province (name, id) values (?, ?) 83 update City set name=?, pid=? where id=? 84 update City set pid=? where id=? 85 session.save(c); 86 session.save(p); 87 */ 88 89 tran.commit(); 90 session.close(); 91 } 92 /** 93 * 查询 94 * 1. 1号城市属于哪个省份 95 * 2. 1号省份关联的所有城市 96 */ 97 @Test 98 public void find() { 99 100 101 Session session = sf.openSession(); 102 Transaction tran = session.beginTransaction(); 103 104 //1 105 //City c = (City) session.get(City.class, 1); 106 //System.out.println(c.getPro().getName()); 107 108 //2 109 Province p = (Province) session.get(Province.class, 1); 110 111 for(City city:p.getCities()) { 112 System.out.println(city.getName()); 113 } 114 tran.commit(); 115 session.close(); 116 } 117 118 /** 119 * 删除 120 * 1. 删除5号城市信息 121 * 2. 删除4号省份信息 122 */ 123 @Test 124 public void delete() { 125 126 127 Session session = sf.openSession(); 128 Transaction tran = session.beginTransaction(); 129 130 // City c = (City) session.get(City.class, 5); 131 // session.delete(c); 132 133 Province p = (Province) session.get(Province.class, 4); 134 session.delete(p); 135 136 tran.commit(); 137 session.close(); 138 } 139 140 /* 141 * City变更 142 * 143 * 把4号城市变更为2号省份 144 * 145 */ 146 @Test 147 public void update() { 148 149 150 Session session = sf.openSession(); 151 Transaction tran = session.beginTransaction(); 152 153 //查询信息 154 City c4 = (City) session.get(City.class, 4); 155 Province p2 = (Province) session.get(Province.class, 2); 156 157 /* 158 * 变更关联关系 159 * 160 * 此时发送2条update语句。重复操作。 161 * 关联两端同时维护关系,对象的状态都发生了改变,所有会生成2条update语句 162 * update City set name=?, pid=? where id=? 163 update City set pid=? where id=? 164 165 * 只要执行一条update就可以更新数据库中的记录 166 * 只需要一端维护关系。 167 * 哪一端维护关联关系? 168 * 领导1 169 * 全国人民N 主控方 170 * 171 * 在一对多关联中,需要多的一端作为主控方,来维护关联关系 172 * 那么一的一端就应该交出控制权,作为被控方。 173 */ 174 c4.setPro(p2); 175 /* 176 * 虽然,此条删除语句不会影响到数据库中的记录。 177 * 但为了维护程序的健壮性,建议添加此条语句。 178 */ 179 p2.getCities().add(c4); 180 181 //更新 不需要调用update方法 182 // session.update(c4); 183 // session.update(p2); 184 185 186 tran.commit(); 187 session.close(); 188 } 189 }