浅谈JavaEE中的Hibernate中的四种关系映射(三)

今天我们一起来探讨一下有关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中表的映射关系就讲这么多了。

时间: 2024-08-26 12:18:13

浅谈JavaEE中的Hibernate中的四种关系映射(三)的相关文章

浅谈UML类图中类之间的5种关系

什么是UML类图? 类图显示了一组类.接口.协作以及他们之间的关系.在UML中问题域最终要被逐步转化,通过类来建模,通过编程语言构建这些类从而实现系统.类加上他们之间的关系就构成了类图,类图中还可以包含接口.包等元素,也可以包括对象.链等实例. 类与类之间的关系 类与类之间的关系可以根据关系的强度依次分为以下五种: 依赖关系(Dependency)---关联关系(Association)---聚合(Aggregation)---组合(Composition)---泛化(Generalizatio

Hibernate基础学习(四)&mdash;对象-关系映射(上)

一.映射对象标识符      Java语言按内存地址来识别或区分同一个类的不同对象,而关系数据库按主键值来识别或区分同一个表的不同记录.Hibernate使用对象标识符(OID)来建立内存中的对象和数据库表中的记录的对应关系,对象的OID和数据库表的主键对应,为了保证OID的唯一性和不可变性,应该让Hibernate,而不是应用程序来为OID赋值.     Hibernate推荐在数据表中使用代理主键,即不具备业务含义的字段.代理主键通常为整型,因为整型比字符串要节省更多数据库空间.     在

浅谈设计模式的学习(中)

在<浅谈设计模式的学习(上)>中我说到了设计模式的基石-----抽象思维.为什么需要抽象思维呢?因为越抽象就越不容易出错,就像有些领导人说话:坚持改革开放.但怎么算坚持改革开放呢,没有具体的标准,因事而异,所以就不容易违背这个坚持改革开放的原则了. 3.学习设计模式,要保持抽象的思维     什么是抽象思维呢?真的不好说,抽象的东西往往难以说明白,听了也难以搞明白,还是通过具体的例子来说吧 有这么一个学生请假的场景,如果请假时间一天以内则有班长批准就可以了,三天以内则需要老师批准,超过三天就得

转:浅谈CSS在前端优化中一些值得注意的关键点

前端优化工作中要考虑的元素多种多样,而合理地使用CSS脚本可以在很大程度上优化页面的加载性能,以下我们就来浅谈CSS在前端优化中一些值得注意的关键点: 当谈到Web的“高性能”时,很多人想到的是页面加载时间,但其实性能不仅仅是指加载时间,还包括浏览器性能.网络性能.开发效率.在Web前端开发中,性能是一个非常重要的需要考虑的点.本文将介绍一些开发原则和性能准则,这些都是提高Web前端性能的基础. 1. 开发原则 1.1 编写符合当代浏览器性能的代码如果想提高前端性能,就必须理解浏览器的工作原理,

浅谈logo在PPT设计中的运用

在工业设计范畴,特别是产品设计中常常会提到“形式跟随功用”,也就是说产品的外型是树立在产品功用的根底之上的,同样道理,在PPT设计中则演化为“形式跟随内容”,就是说页面的美化设计是为了更好的将内容向观众传达. 为此我们总结了PPT设计的三个原则,即“图示化”,“图标化”,“图表化” 以“图标化”为例,所谓图标,就是具有指代意义的图形符号,具有高度浓缩并快捷传达信息.便于记忆的特性.应用范围很广,软硬件网页社交场所公共场所无所不在,例如各种交通标志…… 在用户界面设计范畴中则为图标的形式,包括程序

浅谈加速因子在策略中的意义

他站链接:浅谈加速因子在策略中的意义 NO:01没有完美的交易系统,但是却有完美的交易哲学.交易哲学.交易策略和资金管理三者缺一不可,才能构成正期望的交易系统.投机依赖价格的移动获得盈利(低买高卖或高买更高卖).在上升或下降趋势中,价格虽然在整体上朝着一个方向移动,但中间也会有短暂的反方向移动.而在横盘过程中,价格的移动方向则显得相对"随机"一些. NO:02关于价格的移动,可以类比物理学中的运动.其中包括:位移距离.时间.速度等.价格的位移相对于时间的比率就是价格的速度.除了速度之外

【转】【ASP.NET MVC系列】浅谈ASP.NET 页面之间传值的几种方式

ASP.NET MVC系列文章 原文地址:https://www.cnblogs.com/wangjiming/p/6275854.html [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作篇)(下) [04]浅谈ASP.NET框架 [05]浅谈ASP.NET MVC运行过程 [06]浅谈ASP.NET MVC 控制器 [07]浅谈ASP.NET MVC 路由 [08]浅谈AS

C#中方法的参数的四种类型

C#中方法的参数有四种类型: 1. 值参数类型  (不加任何修饰符,是默认的类型) 2. 引用型参数  (以ref 修饰符声明) 3. 输出型参数  (以out 修饰符声明) 4. 数组型参数  (以params 修饰符声明) =================================================== 1. 值传递: 值类型是方法默认的参数类型,采用的是值拷贝的方式.也就是说,如果使用的是值类型,则可以在方法中更改该值,但当控制传递回调用过程时,不会保留更改的值.使用

【转】Java中字符串中子串的查找共有四种方法(indexof())

原文网址:http://wfly2004.blog.163.com/blog/static/1176427201032692927349/ Java中字符串中子串的查找共有四种方法,如下:1.int indexOf(String str) :返回第一次出现的指定子字符串在此字符串中的索引. 2.int indexOf(String str, int startIndex):从指定的索引处开始,返回第一次出现的指定子字符串在此字符串中的索引. 3.int lastIndexOf(String st