今天我们一起来探讨一下有关Hibernate四种关系映射,大家可能会有疑惑,关系映射不就是只有三种(一对一,一对多,多对多)吗?这里我们再多说一个继承映射关系,虽然它使用频繁度没有前三个那么多,但是它在一些特定情况下,还是非常有用的,至于有什么用下面马上就要讲。这些关系映射实际上就是对应着关系型数据库中的几种关系的类型。我们都知道Hibernate的作用是将面向关系数据操作,转换成面向对象的操作,其中关系映射文件就是实现这个转换的核心。我们都知道如果我们仅仅使用SQL语句建表并且去维护表与表之间一对一,一对多,多对多关系,这样单纯SQL操作是很简单的。我们通过上篇博客就知道我们要实现的是写好Bean类,然后对应配置好相应的映射文件,运行测试类后就可以实现自动在数据库建立数据表。而我们现在就要实现,如何利用Bean类和映射文件自动建立多个表,并且还要体现出多个表之间的一对一,一对多,多对多的关系。
说白的一点,就是要通过编写多个不同的Bean类(一个Bean类就是对应一张表),和配置映射文件,实现自动创建带有关系(一对一、一对多、多对多关系)的数据表。那我们开始第一个。
实际上通过我们上篇博客我们不难发现,实现表的建立主要体现两个方面,一个是Bean类(两个Bean类之间首先要区分:主表类和从表类),另一个则是Bean类对应的映射文件(首先需要区分:主方和从方),所以接下来的每一种映射关系的讨论也将围绕着这几个方面来展开。
第一种关系:如何去自动建立一个一对一的关系数据表:
注意:在建立一对一映射关系的数据表,实际上有两种方法来实现:一种是通过主键来映射关系,另一种则是通过外键来映射关系。
第一种实现方式:通过主键的方式映射关系:实际上就是通过主表的主键来生成从表的主键,这就是所谓主键生成方式,也就是从表的主键生成机制是有限制,必须要参照主表的中的主键,不可能出现主表中不存在的主键。然后通过主表的主键来寻找从表的主键,由于主键是不可能重复,所以也就形成通过主键的映射方式来实现和维护了两个表之间的一对一的关系。
1、在Bean类中的体现:假如有Person类和Card类两个Bean类,来自动创建两张具有一对一关系的数据表。Person类为主方,Card类为从方
这里面还分为单向维护和双向维护,单向维护就是在主方类中保存一个从方类中的引用或者在从方类中保存一个主方类的引用。为了讲解全面点,我们全都以双向维护为例。
可能有人会有疑问,什么是单向维护什么又是双向维护,单向维护可以通过维护的主动方来找到被维护的一方;而双向维护则是双方都能互相找到。
Person类
package com.mikyou.day05.one2one; /** * @Mikyou * @Person主方类 * 注意: * 1、在编写Person类时,不要把从方类的引用写入到构造器中 * 2、两个构造器:一个无参数构造器,另一个则是有参数的构造器 * 3、在重写toString方法的时候,注意不要把从方表引用写进去。 * */ public class Person { private Long id; private String name; private Card card;//保存着从方类Card的一个引用 public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Card getCard() { return card; } public void setCard(Card card) { this.card = card; } public Person(Long id, String name) { super(); this.id = id; this.name = name; } public Person() { // TODO Auto-generated constructor stub } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", card=" + card + "]"; } }
Card类:
package com.mikyou.day05.one2one; /** * @Mikyou * @Card从方类 * 注意: * 1、在编写Card类时,不要把主方类的引用写入到构造器中 * 2、两个构造器:一个无参数构造器,另一个则是有参数的构造器 * 3、在重写toString方法的时候,注意不要把主方表引用写进去。 * */ public class Card { private Long id; private String num;//身份证号 private String address;//地址 private Person person;//保存主方类的一个引用 public Card(Long id, String num, String address) { super(); this.id = id; this.num = num; this.address = address; } public Card() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNum() { return num; } public void setNum(String num) { this.num = num; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
2、在映射文件中的体现:
主方表映射文件person.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.mikyou.day05.one2one"> <class name="Person" table="tbl_person"> <id name="id" column="id"> <generator class="increment" /> </id> <property name="name" /> <!-- 在主方一对一 配置one-to-one--> <one-to-one name="card" class="Card" /> </class> </hibernate-mapping>
从方类的映射文件card.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.mikyou.day05.one2one"> <class name="Card" table="tbl_card"> <id name="id" column="id" > <generator class="foreign"><!-- 以外键的形式来产生主键 --> <param name="property">person</param> </generator> </id> <property name="num" /> <property name="address" /> <one-to-one name="person" class="Person" constrained="true"></one-to-one><!-- constrained="true"表示从表中的主键必须依赖主表中的主键 --> </class> </hibernate-mapping>
注意:在配置主键关系来维护一对一的关系的时候,在映射文件中的表现,首先双方都通过<one-to-one>来维护关系。在主方person.hbm.xml映射文件中
配置<one-to-one name="card" class="Card" />其中的name对应着Person类中保存的card引用,然后对应card引用的Class,Card类。在从方card.hbm.xml映射文件中也是配置
<one-to-one>但是需要加上constrained="true"属性,表示从表card主键必须依赖主表person的主键,此外在card.hbm.xml中它的主键生成方式不再是increment自增的方式了,而是
<id name="id" column="id" ><generator class="foreign"><!-- 以外键的形式来产生主键 --><param name="property">person</param><!-- --></generator></id>
从表中的主键引用了主表中的主键,所以需要在主键生成方式中设置成foreign,并将参数传进去,告诉hibernate应该使用哪一个主键。在配置上,双方都为one-to-one
第二种实现方式:通过外键来映射关系:(注意:只要是通过外键来映射关系的,必须在主表和从表中都要有体现)
这第二种方式可以说是维护一对一关系常用的方式,第一种通过主键来映射的方式使用的情况不是很多。通过外键来映射关系主要是通过映射文件来体现的,它的Bean类表现形式和第一种情况一致。它的映射文件主要怎么体现呢?它是在主方表中配置一个<one-to-one>并且要添加一个属性<propery-ref>,在从表中配置一个<many-to-one>,为什么要配置一个<many-to-one>这不是一对多吗?我们知道一对一实际上就是一对多的一个特例,所以我们可以将一个一对多的情况转换成一个一对一的情况只需要在从表中的<many-to-one>中配置一个unique属性设为true即可并且还得指定一个属性作为外键通过column属性来指定,但这种方式下的主键生成方式没有要求就采取自增方式即可。
person.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.mikyou.day05.one2one"> <class name="Person" table="tbl_person"> <id name="id" column="id"> <generator class="increment" /> </id> <property name="name" /> <!-- 在主方一对一,因为在主表和从表都要有体现,使用property-ref="person" 因为Card类中参考的是person属性,通过从表中的外键去匹配主表中主键--> <one-to-one name="card" class="Card" property-ref="person" /> </class> </hibernate-mapping>
card.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.mikyou.day05.one2one"> <class name="Card" table="tbl_card"> <id name="id" column="id"> <generator class="increment" /> </id> <property name="num" /> <property name="address" /> <!-- 配置特殊的多对一,实际上是一对一--> <many-to-one name="person" class="Person" unique="true" column="p_id"></many-to-one> </class> </hibernate-mapping>
注意:注意:
unique="true" 是在使用hibernate的自动建表的时候才用到,用来指定表中的外键唯一,即给表中的外键添加唯一性约束,保证是一对一的关系
property-ref="" 用来指定关联类的属性名,这个属性将会和本类中的主键相对应,如果没有指定,默认使用关联类的主键和本类中的主键相对应。
在一对一关系中,如果没有在<one-to-one>中指定property-ref="person",当从主方对象寻找从方对象的时候将会使用自身的主键和关联类的主键匹配,这样就会出问题!如果指定后,将会用自身的主键和关联类的外键匹配。property-ref=""中的值为属性。
第二种关系:如何去自动建立一个一对多的关系数据表(以一个顾客对应多个订单为例):
1、在Bean类中的表现:
在主类Customer中的表现:在Customer中保存一个Set<Oder>集合表示一对多在从类Order中保存一个Customer类的引用
Customer类:
package com.mikyou.day05.one2many; import java.io.Serializable; import java.util.Date; import java.util.HashSet; import java.util.Set; public class Customer implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private Long id; private String name; private String telephone; private String address; private String pwd; private Set<Order> orders=new HashSet<Order>();//维护多个订单。注意这里一定要初始化,因为集合中数据为空和 //集合不存在是两个概念,集合中数据为空表示此时顾客中没有订单,当下次来了订单直接放入到集合中即可 //集合不存在就不行,如果下次只有来了订单,而此时集合不存在,会造成集合中不能放入订单对象,造成空指针 public Customer(Long id, String name,String pwd, String telephone, String address) { super(); this.id = id; this.name = name; this.telephone = telephone; this.address = address; this.pwd = pwd; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public Customer() { // TODO Auto-generated constructor stub } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTelephone() { return telephone; } public void setTelephone(String telephone) { this.telephone = telephone; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Set<Order> getOrders() { return orders; } public void setOrders(Set<Order> orders) { this.orders = orders; } }
Order类:
package com.mikyou.day05.one2many; import java.io.Serializable; import java.util.Date; public class Order implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private Long id; private String total; private Date orderDate; private Customer customer;//维护主表一方的引用 public Order(Long id, String total, Date orderDate) { super(); this.id = id; this.total = total; this.orderDate = orderDate; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTotal() { return total; } public void setTotal(String total) { this.total = total; } public Date getOrderDate() { return orderDate; } public void setOrderDate(Date orderDate) { this.orderDate = orderDate; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public static long getSerialversionuid() { return serialVersionUID; } public Order() { super(); } @Override public String toString() { return "Order [id=" + id + ", total=" + total + ", orderDate=" + orderDate + "]"; } }
2、在映射文件中的表现:
customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.mikyou.day05.one2many"> <class name="Customer" table="tbl_customer"> <id name="id" column="id"> <generator class="increment" /> </id> <property name="name" /> <property name="telephone" /> <property name="pwd"></property> <property name="address" /> <!-- 关联映射 --> <set name="orders" lazy="true" cascade="delete"><!-- lazy:true延迟加载 默认 cascade="delete"级联删除--> <key column="c_id"></key><!-- 在Order多的一方配置外键 --> <one-to-many class="Order"/> </set> </class> </hibernate-mapping>
order.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.mikyou.day05.one2many"> <class name="Order" table="tbl_order"> <id name="id" column="id"> <generator class="increment" /> </id> <property name="total" /> <property name="orderDate" /> <!-- 关联映射,column是与生成表中的一列 --> <many-to-one name="customer" class="Customer" column="c_id"></many-to-one> </class> </hibernate-mapping>
映射一对多关联
数据库表中的关系:一个顾客对应多个订单 |
|
主表:d_customer( Id name ); |
从表: d_order( Id name c_id references d_customer(id) ); 从表的主键引用主表的主键 |
类中的关系: 双向维护 |
|
主类 Customer{ Id name Set<Order> orders = new HashSet<>() } 一个顾客对应多个订单,所以在对象中需要用一个集合来维护,后期可以通过getter方法获取属于该顾客的所有订单 |
从类:Order{ Id name Customer customer } 一个订单只能属于一个顾客,所以需要维护一个顾客对象,后期通过getter方法获取该订单的所属者。 |
映射关系上: |
|
主方: customer.hbm.xml <set name="orders"> <key column="c_id"></key> <one-to-many class="Order"/> </set> |
从方:order.hbm.xml <many-to-one name="customer" class="Customer" column="c_id" > |
注意: 在映射文件中,双方都维护了外键。主方通过<set>集合中的<key>来维护,从方通过 <many-to-one> 中的column来维护。 |
第三种关系:如何去自动建立一个多对多的关系数据表(以多个学生选择多门课程为例):
1、在Bean类中的表现:
在各自类中互相保存着对方一个集合类的空对象,而不是引用,一定要初始化。因为当下次有个学生要选课了,此时课程集合没有new操作,就直接调用集合的add方法,自然就抛出空指针异常,但是如果new操作得到一个空集合对象,下次只要有学生选课了,直接调用add方法就会把课程这个对象加入到集合中去。
Student类:
package com.mikyou.day05.many2many; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Student implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private Long id; private String name; private Integer age; private Set<Course> courses=new HashSet<Course>(); public Student(Long id, String name, Integer age) { super(); this.id = id; this.name = name; this.age = age; } public Student() { super(); } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Set<Course> getCourses() { return courses; } public void setCourses(Set<Course> courses) { this.courses = courses; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; } }
Course类:
package com.mikyou.day05.many2many; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Course implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private Long id; private String name; private Set<Student> students=new HashSet<Student>(); private Integer credit; public Course() { super(); } public Course(Long id, String name, Integer credit) { super(); this.id = id; this.name = name; this.credit = credit; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } public Integer getCredit() { return credit; } public void setCredit(Integer credit) { this.credit = credit; } @Override public String toString() { return "Course [id=" + id + ", name=" + name + ", credit=" + credit + "]"; } }
2、在映射关系中的表现:
student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.mikyou.day05.many2many"> <class name="Student" table="tbl_student"> <id name="id" column="id"> <generator class="increment" /> </id> <property name="name" /> <property name="age" /> <set name="courses" table="tbl_student_course"> <key column="s_id"></key><!--tbl_student_course表中的外键s_id参考着本表的tbl_student中的主键id --> <many-to-many class="Course" column="c_id"></many-to-many> </set> </class> </hibernate-mapping>
course.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.mikyou.day05.many2many"> <class name="Course" table="tbl_course"> <id name="id" column="id"> <generator class="increment" /> </id> <property name="name" /> <!-- 配置多对多 --> <set name="students" table="tbl_student_course"> <key column="c_id"></key><!--tbl_student_course表中的外键c_id参考着本表的tbl_course中的主键id --> <many-to-many class="Student" column="s_id"></many-to-many> </set> </class> </hibernate-mapping>
映射多对多关联
数据库表中的关系:一个学生选择多门课程,一门课程也可以有很多学生来选择 |
||
主表:d_student( Id name age ); |
从表: d_course( Id name ); |
|
桥表:d_student_course( student_id references d_student(id), course_id references d_course(id). unique(student_id,course_id) ); 多对多的关系是维护在桥表上的,桥表中维护了两个外键,分别指向两个表的主键,并且这两个外键为联合主键。 |
||
类中的关系: 双向维护 |
||
主类 Student{ Id name age Set<User> courses= new HashSet<>() } 一个学生对应多门课程,用一个集合来维护 |
从类:Course{ Id name Set<Student> students=new hashSet<>() } 一个门课程对应多个学生,需要用一个集合来维护 |
|
映射关系上: |
||
主方:student.hbm.xm l<set name="courses" table="tbl_student_course"> <key column="s_id"></key> <many-to-manyclass="Course" column="c_id"></many-to-many> </set> |
从方:course.hbm.xml <set name="students" table="tbl_student_course"> <key column="c_id"></key> <many-to-many class="Student" column="s_id"></many-to-many> </set> |
|
注意: 在映射元素上反应的是关联关系,由于多对多需要使用桥表来维护关联关系,所以需要在set元素中添加table元素用来指定桥表,key元素中的column属性表示本类在桥表中的外键。many-to-many表示对应的另外一方,其中的column属性表示对应的类在桥表中的外键。 |
第三种关系:多对多关系中的另一种常用形式:将一个多对多形式问题分解成两个一对多的形式的问题
这种转换后的好处就是我们控制桥表字段,也就是说我可以任意定制桥表;因为如果使用的是上一种方式的处理的话,桥表是系统自动产生的,并且这样的桥表只能有两个字段分别是两个表的主键,也就是桥表的两个外键。通常这样的桥表不能满足现实需求的,就比如上面的学生选课来说,就会自动产生一个选课关系表,现实需求中通常会在选课关系中添加一个分数或者老师评语,也就是某某选了某门课成绩多少,老师评语是什么?这样的一个需求很常见,但是如果使用系统自动产生通过many-to-many方式来生成表的话是无法满足现实需求,所以就出现这种将一个多对多的形式转换成两个一对多的形式的问题。这样的操作就需要我们自己手动的去创建桥表。学生选课关系就变成了:一个学生对应多种选课关系,一门课程对应多种选课关系。
1、在Bean类中的表现:
我们这里需要三个Bean类:一个是Student类,一个是Course类,另一个就是StuCou类(选课关系类)
在Student类中保存着一个Set<StuCou>集合,有多个选课关系,在Course类中也保存一个Set<StuCou>集合,有多个选课关系。在StuCou类中的分别保存着Student和Course的引用。
Student类:
package com.mikyou.day05.double_one2many; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Student implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private Long id; private String name; private Integer age; private Set<StuCou> stuCous=new HashSet<StuCou>();//保存着多个选课关系 public Student(Long id, String name, Integer age) { super(); this.id = id; this.name = name; this.age = age; } public Student() { super(); } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Set<StuCou> getStuCous() { return stuCous; } public void setStuCous(Set<StuCou> stuCous) { this.stuCous = stuCous; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; } }
Course类:
package com.mikyou.day05.double_one2many; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Course implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private Long id; private String name; private Set<StuCou> stuCous=new HashSet<StuCou>();//保存着多个选课关系 private Integer credit; public Course() { super(); } public Course(Long id, String name, Integer credit) { super(); this.id = id; this.name = name; this.credit = credit; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<StuCou> getStuCous() { return stuCous; } public void setStuCous(Set<StuCou> stuCous) { this.stuCous = stuCous; } public Integer getCredit() { return credit; } public void setCredit(Integer credit) { this.credit = credit; } @Override public String toString() { return "Course [id=" + id + ", name=" + name + ", credit=" + credit + "]"; } }
StuCou类:
package com.mikyou.day05.double_one2many; //学生选课关系,抽取一个选课关系,让原来的一个多对多的关系直接转换成两个一对多的关系 public class StuCou { private Long id; private Double grade;//新增扩展属性grade分数 private String comment;//新增扩展属性comment评语 // private Student student; private Course course; public StuCou(Long id, Double grade, String comment) { super(); this.id = id; this.grade = grade; this.comment = comment; } public StuCou() { // TODO Auto-generated constructor stub } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Double getGrade() { return grade; } public void setGrade(Double grade) { this.grade = grade; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } public Course getCourse() { return course; } public void setCourse(Course course) { this.course = course; } }
2、在映射文件中的表现:
在映射文件配置实际上和一对多配置是一样的,只不过这里是配置两个一对多的关系,就是需要在桥表StuCou中的配置两个<many-to-one>
student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.mikyou.day05.double_one2many"> <class name="Student" table="tbl_student"> <id name="id" column="id"> <generator class="increment" /> </id> <property name="name" /> <property name="age" /> <set name="stuCous"> <key column="s_id"></key><!--tbl_student_course表中的外键s_id参考着本表的tbl_student中的主键id --> <one-to-many class="StuCou" /> </set> </class> </hibernate-mapping>
course.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.mikyou.day05.double_one2many"> <class name="Course" table="tbl_course"> <id name="id" column="id"> <generator class="increment" /> </id> <property name="name" /> <property name="credit" /> <!-- 配置多对多 --> <set name="stuCous"> <key column="c_id"></key><!--tbl_student_course表中的外键c_id参考着本表的tbl_course中的主键id --> <one-to-many class="StuCou" /> </set> </class> </hibernate-mapping>
桥表映射文件stucou.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.mikyou.day05.double_one2many"> <class name="StuCou" table="tbl_student_course"> <id name="id" column="id"> <generator class="increment" /> </id> <property name="grade" /> <property name="comment" /> <!-- 配置多对多 --> <many-to-one name="student" class="Student" column="s_id"></many-to-one> <many-to-one name="course" class="Course" column="c_id"></many-to-one> </class> </hibernate-mapping>
注意:实际上这种方式在现实需求用的更多一些,所以这种情况使用就是当你的桥表中需要新增适应需求的属性这时候就需要考虑将一个多对多的关系转换成两个一对多的关系
第四种关系:如何去自动建立一个具有继承关系数据表(基类:Animal 两个子类:Dog、Bird)
所谓继承映射实际上对应着就是面向对象中的继承机制,这种继承关系映射在现实需求其实用处也蛮大的:就比如说我们设想一个需求,在一个项目中或者多个项目的我们可能多次都需要用到上传附件或者上传图片的功能,实际上这操作是和项目没有多大关系,可以说它是通用的。那既然这样的话为什么不能抽取一个这样的操作单独用一个表处理,然后需要使用这些操作的表只需要继承这个基类表不就行了吗?这样就大大重构了我们的项目代码。
1、在Bean类中的表现:
先写一个Animal类,然后写一个Dog类和一个Bird类分别都去继承这个Animal类
Animal
package com.mikyou.day05.inherit; import java.io.Serializable; public class Animal implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Animal(Long id, String name) { super(); this.id = id; this.name = name; } public Animal() { super(); } @Override public String toString() { return "Animal [id=" + id + ", name=" + name + "]"; } }
Dog类:
package com.mikyou.day05.inherit; public class Dog extends Animal{ /** * */ private static final long serialVersionUID = 1L; private String color; public String getColor() { return color; } public void setColor(String color) { this.color = color; } @Override public String toString() { return "Dog [color=" + color + "]"; } }
Bird类:
package com.mikyou.day05.inherit; public class Bird extends Animal{ /** * */ private static final long serialVersionUID = 1L; private String type;//品种 public String getType() { return type; } public void setType(String type) { this.type = type; } }
映射文件:
映射文件生成表有两种:一种是将所有的子父类放在一张表中,另一种就是一个类就是一张表(个人认为这种方式更好)
第一种是将所有的子父类放在一张表中:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.briup.day5.inherit"> <class name="Animal" table="tbl_animal"> <id name="id" column="id"> <generator class="uuid.hex"/> </id> <property name="name"/> <joined-subclass name="Dog" table="tbl_dog"> <key column="id"/> <property name="color"/> </joined-subclass> <joined-subclass name="Bird" table="tbl_bird"> <key column="id"/> <property name="type"/> </joined-subclass> </class> </hibernate-mapping>
第二种就是一个类就是一张表:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.briup.day5.inherit"> <class name="Animal" abstract="true"> <id name="id" column="id"> <generator class="uuid.hex"/> </id> <property name="name"/> <union-subclass name="Dog" table="tbl_dog"> <property name="color"/> </union-subclass> <union-subclass name="Bird" table="tbl_bird"> <property name="type"/> </union-subclass> </class> </hibernate-mapping>
继承映射
1每个类一张表
在表中的关系--子类的主键引用父类的主键,保证父子类的id一致 |
||
父类表--动物 animal( id, name ); |
子类表---狗 Dog( id references animal(id), type ); |
子类表---鸟 Bird( id references animal(id), type ); |
在类中的关系 |
||
Animal{ id, name } |
Dog extends Animal{ type } |
Bird extends Animal{ color } |
在映射文件中的关系,配置在同一个映射文件中 |
||
<class name="Animal" table="d_animal"> <id name="id" column="id"> <generator class="increment"/> </id> <property name="name"/> <!-- 以上是配置父类的基本属性,需要指定父类使用的表名--> <!-- 以上是配置子类的配置,需要指定子类使用的表名,使用joined-subclass来和父类关联,key值继承父类的,column指定子类在表中的主键;property为子类特有的属性--> <joined-subclass name="Dog" table="h_dog"> <key column="id"></key> <property name="type"/> </joined-subclass> <joined-subclass name="Bird" table="h_bird"> <key column="id"></key> <property name="color"></property> </joined-subclass> </class> |
2一个类域一张表
在表中的关系--父子类共用一张表,这张表中包含了父子类的所有字段 |
||
Allclass( id name type color a_value --用来表示该条记录是属于哪个类的 ); |
||
在类中的关系 |
||
Animal{ id, name } |
Dog extends Animal{ type } |
Bird extends Animal{ color } |
在映射文件中的关系,配置在同一个映射文件中 |
||
<class name="Animal" table="h_allclass"> <id name="id" column="id"> <generator class="increment"> </generator> </id> <!-- discriminator用来指定标识列 --> <discriminator column="a_value"/> <property name="name"/> <!-- discriminator-value用来指定标识列中应该填入的值,用这个值来区分这条记录是属于哪个类的,property用来指明子类特有的属性 <subclass name="Dog" discriminator-value="dog"> <property name="type"/> </subclass> <subclass name="Bird" discriminator-value="bird"> <property name="color"/> </subclass> </class> |
3每个子类一张表
在表中的关系--父子类共用一张表,这张表中包含了父子类的所有字段 |
||
在类中的关系 |
||
Animal{ id, name } |
Dog extends Animal{ type } |
Bird extends Animal{ color } |
在映射文件中的关系,配置在同一个映射文件中 |
||
<class name="Animal"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="color"/> <union-subclass name="Dog" table="dog"> <property name="callType"></property> </union-subclass> <union-subclass name="Bird" table="bird"> <property name="type"></property> </union-subclass> </class> |
||
主键生成方式不能使用Increment,因为Animal类并没有对应的表,可以使用序列 |
到这里Hibernate中表的映射关系就讲这么多了。