ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] # 简介 一个对象就对应着表中的一条记录,而表中的字段对应着类中的属性。 数据库中表与表之间存在着三种关系,也就是系统设计中的三种实体关系 ## 一对一 原则有两种: 1. 唯一外键对应:在任意一方添加外键来描述对应关系 2. 主键对应:一方的主键作为另一方的主键 ~~~ Class Employee { Private Archives archives; } Class Archives { Private Employee employee; } ~~~ ## 一对多,多对一 客户与订单之间一对多关系(多对一) 建表原则:在多的一方添加外键来描述关联关系 ~~~ Class Customer { Private Set<Order> orders; } Class Order { Private Customer c; } ~~~ ## 多对多 例如学生与老师 建表原则:通过一张中间表来描述其对应关系 中间表分别指向多方的主键 ~~~ Class Student{ Set<Teacher> ts; } Class Teacher{ Set<Student> ss; } ~~~ # Hibernate关系映射--(一对多,多对一) 类创建 订单 ~~~ //订单---多的一方 public class Order { private Integer id; private Double money; private String receiverInfo; //收货地址 //订单与客户关联 private Customer c; //描述订单属于某一个客户 } ~~~ 客户 ~~~ public class Customer { private Integer id; //主键 private String name; //姓名 //描述客户可以有多个订单 private Set<Order> orders = new HashSet<Order>(); } ~~~ 映射文件编写 Order.hbm.xml ~~~ <!-- 多对一 --> <many-to-one name="c" class="cn.jdxia.one ToMany.Customer" column="c_customer_id"> </many-to-one> <!-- name属性它描述的是 Order类中的一的一方的属性名称 Customer c; class代表一的一方的类型 column描述的是一对多,在多的一方产生的外键的名称 c_customer_id --> ~~~ Customer.hbm.xml ~~~ <!-- 一个客户关联多个订单 --> <set name="orders"> <key column="c customer id"/> <one-to-many class="cn.jdxia.oneToManyOrder" /> </set> <!-- 使用set来描述在一的一方中关联的多Set<Order>,它的name属性就是set集合的名称 key:它主要描述关联的多的一方产生的外键名称,注意要与多的一方定义的外键名称相同 one-to-many描述集合中的类型 --> ~~~ ## 测试保存 ~~~ //2.3 建立关系 //2.3.1 订单关联客户 o1.setC(c); o2.setC(c); //2.3.2客户关联订单 c.getOrders().add(o1); c.getOrders().add(o2); session.save(o1); session.save(o2); session.save(c); ~~~ 上面操作是一种双向关联 问题:我们可不可以只保存订单或只保存客户完成保存操作? ## 测试单向关联保存 ~~~ //2.3建立关系 //2.3.1订单关联客户 o1.setC(c); o2.setC(c); session.save(o1); //o1是一个持久化对象 session.save(o2); //o2是一个持久化对象 ~~~ ~~~ org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing ~~~ 这个异常代表提一个持久化对象关联了一个瞬时对象。 我们可以使用级联操作来解决上述的问题. **我们现在要做的是保存订单时保存客户,需要在订单的hbm配置文件中修改** ~~~ <!-- 多对一 --> <!-- name属性:因为在联系人实体类使用customer对象表示,写customer名称c class属性: customer全路径 column属性: 外键名称 --> <many-to-one name="c" class="cn.jdxia.oneToMany.Customer" column="c_customer_ id" cascade="save-update"></many-to-one> ~~~ **设置`cascade= save-update`那么在保存订单时就可以自动将客户保存, 把客户放到订单里面就可以** 如果要加上级联删除就这样 ~~~ <many-to-one name="c" class="cn.jdxia.oneToMany.Customer" column="c_customer_ id" cascade="save-update,delete"></many-to-one> ~~~ 如果我们要完成保存客户时,保存订单 ~~~ <!-- 一个客户关联多个订单 --> <set name="orders" cascade="save-update"> <key column="c_customer_id"/> <one-to-many class="cn.jdxia.oneToMany.Order"/> </set> ~~~ ## 双向关联维护 我们在开发中要配置双向关联配置---------可以通过任意一方来操作对方 在操作代码,尽量来要进行单向关联------可以尽量资源浪费。 **在双向关联中,会存在多余的update语句 ** 我们可以使用inverse属性来设置,双向关联时由哪一方来维护表与表之间的关系。 一般一的一方,放弃 ~~~ <!--一个客户关联多个订单 <set name="orders" inverse="true"> <key column="c_customer_id" /> <one-to-many class="cn.jdxia.oneToMany.Order" /> </set> ~~~ inverse为true,代表对方维护主键 inverse为false,由自己维护主键,默认 关于inverse取值: 外键在哪个表中,我们就让一方维护外键 # 级联删除 我们在删除客户时,也要删除订单,如果没有做级联,那么这个操作是不允许。 为了维护数据完整性 ~~~ //操作-删除订单时,不需要删除客户,当我们删除一个客户时,应该将客户对应订单也删除 Customer c = session.get(Customer.class, 1); session.delete(c); //删除客户,订单是否删除 ~~~ 想要完成操作:我们可以在客户中添加`cascade="delete";` ~~~ <!-- 一个客户关联多个订单 --> <set name="orders" inverse="true" cascade="delete"> <key column="c_customer_id" /> <one-to-many class="cn.jdxia.oneToMany.Order" /> </set> ~~~ **delete-orphan用法** ~~~ //2. 操作 //得到客户 Customer c = session.get(Customer class, 2); //得到客户的订单 Order o = session. get(Order.class, 6); c.getOrders(). remove(o); ~~~ ~~~ <!-- 一个客户关联多个订单 --> <set name="orders" inverse=true" cascade="delete-orphan"> <key column="c_customer_id" /> <one-to-many class="cn.jdxia.oneToMany.Order" /> </set> ~~~ # cascade总结 使用cascade可以完成级联操作 它可常用取值: none这是一个默认值 `save-update`,当我们配置它时,底层使用`save update`或`save-update`完成操作,级联保存临时对象,如果是游离对象,会执行update. delete 级联删除 delete-ophan 删除与当前对象解除关系的对象。 all 它包含了`save-update delete`操作 `all-delete-orphan` 它包信了delete-orphan与all操作 # cascade与inverse有什么区别? ~~~ cascade它是完成级联操作 Inverse它只有在双向关联情况下有作用,它来指定由哪一方维护外键 ~~~ # 多对多 我们使用注解完成多对多配置. 描述学生与老师. 使用@ManyToMany来配置多对多,只需要在一端配置中间表,另一端使用mappedBy表示放置外键维护权。 创建PO类 Teacher类中 ~~~ @Entity @Table(name ="t_teacher") public class Teacher { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private string name; @ManyToMany(targetEntity = Student.class, mappedBy=" teachers") //代表由对方来维护外键 private Set<Student> students = new HashSet<Student>(); } ~~~ Student类中 ~~~ @Table(name ="t_student") public class Student { @GeneratedValue (strategy = GenerationType.IDENTITY) private Integer id; private String name; @ManyToMany(targetEntity = Teacher.class) //使用JoinTabl来猫述中间表,并描述中间表中外键与Student,Teacher的映射关系 //joinColumns它是用来描述Student与中间表中的映射关系 //inverseJoinColums它是用来描述Teacher与中间表中的映射关系 @JoinTable(name="s_t", joinColumns = { @JoinColumn(name = "c_student_id") }, inverseJoinColumns = { @JoinColumn(name ="c_teacher_id")}) private Set<Teacher> teachers = new HashSet<Teacher>(); } ~~~ --- 如果是xml的话 ~~~ <!-- 在用户里面表示所有角色,使用set标签 name属性: 角色set集合名称 table属性: 第三张表名词 --> <set name="setRole" table="user_role"> <!-- key标签里面配置,当前映射文件在第三张表外键名称 --> <key column="userid"></key> <!-- class:角色实体类全路径, column: 角色在第三张表外键名称 --> <many-to-many class="cn.jdxia.manytomany.Role" column="roleid"></many-to-many> </set> ~~~ ## 级联保存操作测试 因为我们将外键的维护权利由Student来维护,我们演示保存学生时,将都也级联保存。 ~~~ //学生关联老师 s1.getTeachers().add(t1); s1.getTeachers().add(t2); s2.getTeachers().add(t1); s2.getTeachers().add(t2); //保存学生 session.save(s1); session.save(s2); ~~~ 在Student类中配置了级联 ~~~ @JoinTable(name="s_t", joinColumns = { @JoinColumn(name = "c_student_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "c_teacher_id", referencedColumnName = "id") }) @Cascade(CascadeType.SAVE_UPDATE) private Set<Teacher> teachers = new HashSet<Teacher>(); ~~~ # 一对一 以人与身份证号为例 一对一操作有两种映射方式: 1. 1. 在任意一方添加外键 2. 2. 主键映射 ## 外键映射 创建实体 User类 ~~~ @Entity @Table(name = "t_user") public class User { @Id @GenericGenerator(strategy = "uuid", name = "myuuid") @GeneratedValue(generator = "myuuid") private String id; private String name; @OneToOne(targetEntity = IDCard.class, mappedBy = "user") private IDCard idCard; } ~~~ 上述配置,t\_user表放弃对外键的维护权利 IDCard类 ~~~ @Entity @Table(name = "t_idcard") public class IDCard { @Id @GenericGenerator(strategy = "uuid", name = "myuuid") @GeneratedValue(generator = "myuuid") private String id; private String cardNum; @OneToOne(targetEntity = User.class) @JoinColumn(name = "c_user_id") @Cascade(CascadeType.SAVE_UPDATE) private User user; } ~~~ joinColumn指定外键列名称,当前配置外键是在t_idcard表中 ~~~ //创建一个人 User user = new User(); user.setName("abc"); //创建身份证号 IDCard idcard = new IDCard(); idcard.setCardNum("12345"); //身份证关联人 idcard.setUser(user); //存储身份证号 session.save(idcard); ~~~