Hibernate3入门之第三章Hibernate关联关系的映射
Hibernate3入门之第三章Hibernate关联关系的映射
实体之间的关系:
实体之间有三种关系:
-
一对多
一个用户,生成多个订单,每一个订单只能属于一个用户.
建表原则:
在多的一方创建一个字段,作为外键,指向一的一方的主键.
-
多对多
一个学生可以选择多门课程,一个课程可以被多个学生选择.
建表原则:
建第三张表,中间表至少有两个字段,分别作为外键指向多对多双方主键.
-
一对一(特殊.最少.)
一个公司只能有一个注册地址,一个注册地址,只能被一个公司使用.(否则将两个表建到一个表.)
建表原则
-
唯一外键:
一对一的双方,假设一方是多的关系.需要在多的一方创建一个字段,作为外键.指向一的一方的主键.但是在外键添加一个unique.
-
主键对应:
一对一的双方,通过主键进行关联.
-
一对多的配置
-
创建实体(分别为客户和订单的实体)
-
客户实体(Customer)
private Integer cid; private String cname; // 一个客户有多个订单. private Set<Order> orders = new HashSet<Order>(); //分别提供get/set方法
-
订单实体(Order)
private Integer oid; private String addr; // 订单属于某一个客户.放置一个客户的对象. private Customer customer; //分别提供get/set方法
-
-
建立映射
-
Customer.hbm.xml
<hibernate-mapping> <class name="com.syj.vo.Customer" table="customer" > <!-- 配置唯一标识 主键 --> <id name="cid" column="cid" > <!-- 主键生成策略--> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="cname" column="cname" length="20" /> <!-- 配置映射 --> <!-- 配置一个set集合的name是本类对象中关联对象属性的名称 || 那个集合--> <set name="orders" > <!--<key>标签中column属性:用来描述一对多多的一方的外键的名称 || 那个外键 --> <key column="cno" /> <!-- 配置一个<one-to-many>标签中class:为多的一方的全路径 || 对应那个类 --> <one-to-many class="com.syj.vo.Order" /> </set> </class> </hibernate-mapping>
-
Order.hbm.xml
<hibernate-mapping> <class name="com.syj.vo.Order" table="orders" > <!-- 配置唯一标识(主键)--> <id name="oid" column="oid" > <!-- 主键生成策略--> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="addr" column="addr" length="50" /> <!-- 配置映射 --> <!-- <many-to-one>标签 name :关联对象属性的名称 column :表中外键的名称 class :关联表对象的全路径 --> <many-to-one name="customer" column="cno" class="com.syj.vo.Customer"/> </class> </hibernate-mapping>
-
-
测试一对多的配置
-
原始的保存效果(分别添加客户和订单)
// 向客户表中插入一个客户,在订单表中插入两个订单 public void demo1() { Session session = Hibernate3Utils.openSession(); Transaction ts = session.beginTransaction(); // 创建一个客户 Customer customer = new Customer(); customer.setCname("孙悟空"); // 创建两个订单 Order order1 = new Order(); order1.setAddr("上海"); Order order2 = new Order(); order2.setAddr("北京"); // 给订单添加客户(订单属于客户) order1.setCustomer(customer); order2.setCustomer(customer); // 给客户添加订单(得到订单集合添加订单) customer.getOrders().add(order1); customer.getOrders().add(order2); // 插入数据 session.save(customer); session.save(order1); session.save(order2); ts.commit(); }
-
级联保存的效果(只添加一方就能将另外一方一起级联保存)
-
保存客户级联订单.
<set>集合是客户的关联订单对象的集合.所以在<set>上配置一个属性:cascade=“save-update”
-
保存订单级联客户.
在Order.hbm.xml中<many-to-one>配置cascade="save-update"属性:级联保存
-
-
级联删除的效果
-
当我们不添加任何配置的时候
默认的情况下,将外键置为null,删除数据记录.
-
添加配置
在Customer.hbm.xml的<set>标签上配置cascade=“delete”,将级联删除所关联表中的数据。
在删除中有一种特殊的情况(孤儿删除)
当订单所属的客户不存在时,此时存在的订单是没有意义的(孤儿),属于删除效果的一种
-
-
双向维护产生多余的SQL:
关于重复发送更新sql语句
双向维护:自动更新数据库,产生多余的SQL.
双方都有外键的维护能力.必须让其中一方放弃外键的维护权.(一般情况下都是一的放弃.)
举例说明: 一号客户,一号订单; 二号客户,二号订单 ;修改二号订单属于一号客户
Session session = Hibernate3Utils.openSession(); Transaction ts = session.beginTransaction(); // 持久化对象有自动更新数据库的能力 // 得到一号客户 Customer customer = (Customer) session.get(Customer.class, 1); // 得到二号订单 Order order = (Order) session.get(Order.class, 2); // 修改一号客户和二号订单之间的关系(产生多余的更新sql) customer.getOrders().add(order);//对外键进行维护 order.setCustomer(customer);//对外键进行维护 ts.commit();
配置inverse=”true”:在那一端配置.那么哪一端放弃了外键的维护权.
(国家主席和我们每一个人)由我们自己去进行维护最好
一般情况下,一的一方去放弃.
inverse:控制外键的维护.
-
一对多中的配置属性cascade和inverse
- Cascade属性的取值
取值 描述 none 忽略其他关联的对象,默认值。 save-update 当session通过save(),update(),saveOrUpdate()方法来保存或更新对象时,级联保存所有关联的新建的临时对象,并且级联更新所有关联的游离对象。 persist 当session通过persist()方法来保存当前对象时,会级联保存所有关联的新建的临时对象。 merge 通过Session的merge()方法来保存当前对象时,会级联融合所有关联的游离对象。 delete 通过delete()删除当前对象时,会级联删除所有关联的对象。 lock 通过lock()把当前游离对象加入session缓存时,会把所有的游离对象也加入Session缓存中。 replicate 通过replicate()复制当前对象时,会级联复制所有关联的对象。 evict 通过evict()清除session缓存中对象时,会级联清除所有关联的对象。 refresh 通过refresh()刷新当前对象时,会级联刷新所有关联的对象。(刷新是指同步更新session缓存中数据) delete-orphan 删除所有和当前对象时,解除关联行为的对象。 all-delete-orphan 通过delete()删除当前对象时,会级联删除所有关联的对象。 all save-update(),persist(),merge(),delete(),lock(),replicate(),evict()及refresh()的行为。 比较常见的:
none :不使用级联 dave-update :保存或更新的时候级联 delete :删除的时候级联 all :除了孤儿删除以外的所有级联. delete-orphan :孤儿删除(孤子删除). 仅限于一对多.只有一对多时候,才有父子存在.认为一的一方是父亲,多的一方是子方. 当一个客户与某个订单解除了关系.将外键置为null.订单没有了所属客户,相当于一个孩子没有了父亲.将这种记录就删除了. all-delete-orphan :包含了孤儿删除的所有的级联.
-
inverse属性的取值
inverse=“false”和inverse=“true”
-
Cascade属性和inverse属性的区别
举例说明:
// 区分cascade和inverse // 在Customer.hbm.xml中的<set>上配置 cascade="save-update" inverse="true" public void demo11(){ Session session = Hibernate3Utils.openSession(); Transaction tx = session.beginTransaction(); Customer customer = new Customer(); customer.setCname("张三"); Order order = new Order(); order.setAddr("上海"); customer.getOrders().add(order); // 客户是否存到数据库:存 // 订单是否存到数据库:存 cascade="save-update".外键是null. session.save(customer); tx.commit(); }
结论: 客户是否存到数据库:存
订单是否存到数据库:存 cascade=“save-update”.外键是null.
-
多对多的配置
-
创建实体类(分别为学生和课程的实体)
学生实体(Student)
private Integer sid; private String sname; // 一个学生选择多门课程: private Set<Course> courses = new HashSet<Course>(); //提供get/set方法
课程实体(Course)
private Integer cid; private String cname; // 一个课程被多个学生选择: private Set<Student> students = new HashSet<Student>(); //提供get/set方法
-
建立映射
Student.hbm.xml
<hibernate-mapping> <class name="cn.itcast.hibernate3.demo3.Student" table="student"> <!-- 配置唯一标识 --> <id name="sid" column="sid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="sname" column="sname" length="20"/> <!-- 配置关联映射 --> <!-- <set>标签 name:对应学生中的课程集合的名称 table:中间表名称. --> <set name="courses" table="stu_cour"> <!-- <key>中column写 当前类在中间表的外键.--> <key column="sno"></key> <!-- <many-to-many>中class:另一方类的全路径. column:另一方在中间表中外键名称--> <many-to-many class="cn.itcast.hibernate3.demo3.Course" column="cno"/> </set> </class> </hibernate-mapping>
Course.hbm.xml
<hibernate-mapping> <class name="cn.itcast.hibernate3.demo3.Course" table="course"> <!-- 配置唯一标识 --> <id name="cid" column="cid"> <generator class="native"/> </id> <!-- 配置普通属性 --> <property name="cname" column="cname" length="20"/> <!-- 配置与学生关联映射 --> <!-- <set>中name:对应当前类中的学生的集合的名称 table:中间表的名称--> <set name="students" table="stu_cour"> <!-- <key>中column:当前类在中间表中外键 --> <key column="cno"></key> <!-- <many-to-many>中class:另一方的类全路径. column:另一方在中间表中外键名称 --> <many-to-many class="cn.itcast.hibernate3.demo3.Student" column="sno"/> </set> </class> </hibernate-mapping>
-
测试多对多的配置
-
原始的保存效果(分别添加保存学生和课程)
// 张三选择java和python的课程 李四只选择java的课程 public void demo1() { Session session = Hibernate3Utils.openSession(); Transaction ts = session.beginTransaction(); // 创建学生张三和李四 Student student1 = new Student(); student1.setSname("张三"); Student student2 = new Student(); student2.setSname("李四"); // 创建java和Python课程 Course course1 = new Course(); course1.setCname("Java"); Course course2 = new Course(); course2.setCname("Python"); // 张三选择java和python的课程 student1.getCourses().add(course1); student1.getCourses().add(course2); // 李四选择python的课程 student2.getCourses().add(course1); // 添加 // 在双方都进行添加的时候会对中间表进行重复更新从而产生报错(一方放弃主键) // 一般会选择被动方放弃主键的维护 session.save(student1); session.save(student2); session.save(course1); session.save(course2); ts.commit(); }
-
级联保存
配置Cascade属性Cascade=“save-uodate”
-
级联删除
配置Cascade属性Cascade=“save-uodate,delete”
注意事项:
删除学生的同时级联课程一起删除(不合理),只能是删除学生方和中间表关于该课程的数据,而保留课程表本身的数据
// 级联删除 // 删除学生的同时只删除学生不删除课程 public void demo4() { Session session = Hibernate3Utils.openSession(); Transaction ts = session.beginTransaction(); // 查询二号学生 Student student = (Student) session.get(Student.class, 2); Course course = (Course) session.get(Course.class, 1); // 移除学生所选的课程 student.getCourses().remove(course); ts.commit(); }
-