Hibernate关联关系

  • 创建丈夫和妻子的实体类

  • 丈夫的实体类

@Entity
@Table(name="husband")
public class Husband{
	private int id;
	private String name;
	private int age;
	@Id
	@GeneratedValue   //主键生成策略,自增长
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@Column(length=20)   //设置长度为20
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
	@Override
	public String toString(){
		return "Husband [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
}
  • 妻子的实体类
@Entity
@Table(name="wife")
public class Wife{
	private int id;
	private String name;
	private int age;
	@Id
	@GeneratedValue   //主键生成策略,自增长
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@Column(length=20)   //设置长度为20
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
	@Override
	public String toString(){
		return "Husband [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
}
个

单向外键关联

  • 单向外键关联简单的说就是只能通过一张表访问到另外一张表的数据,不能也从另外一张表访问到这张表的数据。

  • 比如:我们可以通过丈夫的信息访问到妻子的信息,那么在丈夫的表中就必须有妻子的外键。同样的,我们也可以通过妻子的信息访问到丈夫的信息,那么在妻子的表中必须有丈夫的外键。

  • 简单的说就是只能单向访问,要么是通过妻子访问丈夫,要么是通过丈夫访问妻子

通过丈夫访问妻子

  • 根据上面的需求,那么此时的 妻子的主键将作为丈夫的外键 ,这样才可以通过丈夫访问到妻子的信息,其实的sql语句是这样的,如下: select * from husband h join wife w on h.wife_id=w.id;

  • 妻子的主键作为丈夫的外键,那么这个是表中的关系,在实体类中的关系就是妻子的对象作为丈夫的实体类的属性,这样丈夫才可以访问到妻子的信息。

  • 完整的Husband实体类的代码

    • 如果使用自动生成表的话,那么默认生成的外键名称为 类名小写_id ,但是我们可以使用 @JoinColumn(name="") 改变外键的名称
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity   //指定实体类
@Table(name="husband")   //指定对应数据库的表名为husband
public class Husband{
	private int id;
	private String name;
	private int age;
	private Wife Wife;
	@Id
	@GeneratedValue   //主键生成策略,自增长
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@OneToOne   //设置wife的主键为Husband的外键,默认的对应表中的字段为wife_id
	@JoinColumn(name="wifeid") // 默认外键的名字为wife_id.我们使用这个注解改变外键的名字为wifeid
	public Wife getWife(){
		return Wife;
	}
	public void setWife(Wife wife){
		Wife = wife;
	}
	@Column(length=20)   //设置长度为20
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
	@Override
	public String toString(){
		return "Husband [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
}
  • 实体类Wife的代码不用改变

  • 在核心配置文件 hibernate.cfg.xml 添加实体类的映射即可

<mappingclass="cn.tedu.bean.Husband"></mapping>
<mappingclass="cn.tedu.bean.Wife"></mapping>
  • 启动服务器我们将会看到Hibernate已经为我们创建了两张表husband和wife,其中wife的主键设置为husband的外键了(wifeid)

  • 测试方法

    • 我们知道妻子是作为丈夫的外键,因此这里需要先添加指定的wife数据,才可以添加对应的husband数据,所以下面的测试方法先保存了wife对象。但是在后面讲到级联操作,那么就可以直接保存husband对象便可以一起保存了wife对象数据到数据库中
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import cn.tedu.bean.Husband;
import cn.tedu.bean.Teacher;
import cn.tedu.bean.Wife;
import cn.tedu.utils.HibernateUntil;

public class TestOneToOne{
	/**
	 * 添加数据到husband中
	 */
	@Test
	public void TestAdd(){
		Session session = null;
		Transaction transaction = null;
		try {
			// 创建session
			session = HibernateUntil.getSession();
			// 开始事务
			transaction = session.beginTransaction();

			//创建wife对象,并且设置属性值,由于主键是自增长的,因此这里不需要自己设置
			Wife wife=new Wife();
			wife.setAge(22);
			wife.setName("Marry");

			//新建husband对象
			Husband husband=new Husband();
			husband.setAge(22);
			husband.setName("陈加兵");
			//将Wife的对象添加到Husband中,如果这里设置了级联操作,那么只需要保存husband对象即可完成wife的数据保存
			husband.setWife(wife);
			//由于没有设置级联操作,因此这里需要先保存wife对象,否则将不能在数据库中添加成功
			session.save(wife);
			//保存丈夫的信息
			session.save(husband);
			// 提交事务
			transaction.commit();
		} catch (Exception exception) {
			transaction.rollback(); // 事务回滚
		} finally {
			if (session!=null) {
				session.close();
			}
		}
	}

	/**
	 * 查询丈夫和对应妻子的信息
	 * 根据id查询,只要查询到丈夫的对象,那么妻子的信息就会保存在Husband的属性Wife对象中,因此可以通过访问其中的wife属性来获取对应妻子的信息
	 * 原理:使用session.get(class<T> cls,id),其实发出的sql语句是外连接语句:
	 * 		select * from husband h left join wife w on h.wifeid=w.id where h.id=?
	 * 		如果能够查找到对应的妻子信息就将其添加到Husband中的wife属性中,如果没有查找到那么设置wife属性为null即可,这个就是外连接
	 */
	@Test
	public void TestGet(){
		Session session = null;
		Transaction transaction = null;
		try {
			// 创建session
			session = HibernateUntil.getSession();
			// 开始事务
			transaction = session.beginTransaction();
			//查询id=1的husband信息
			Husband husband=session.get(Husband.class, 1);
			//获取对应的妻子对象
			Wife wife=husband.getWife();
			//输出
			System.out.println(husband);
			System.out.println(wife);
			// 提交事务
			transaction.commit();
		} catch (Exception exception) {
			transaction.rollback(); // 事务回滚
		} finally {
			if (session!=null) {
				session.close();
			}
		}
	}

	/**
	 * 测试修改操作: 这里我们修改id=1的Husband对应的妻子的信息为id=2,当然前提是id=2的wife信息要存在,否则将不会成功
	 * 想要修改妻子的数据,直接修改Husband中的wife属性即可
	 */
	@Test
	public void TestUpdate(){
		Session session = null;
		Transaction transaction = null;
		try {
			// 创建session
			session = HibernateUntil.getSession();
			// 开始事务
			transaction = session.beginTransaction();
			//查询id=1的husband信息
			Husband husband=session.get(Husband.class, 1);
			//查询wife的id=2的对象
			Wife wife=session.get(Wife.class, 2);
			//如果这个对象查询到
			if (wife!=null) {
				husband.setWife(wife);  //修改Husband对象中的wife属性值即可
			}
			session.update(husband);   //执行更新操作
			//获取对应的妻子对象
			// 提交事务
			transaction.commit();
		} catch (Exception exception) {
			transaction.rollback(); // 事务回滚
		} finally {
			if (session!=null) {
				session.close();
			}
		}
	}

	/**
	 * 测试删除wife表中的数据
	 * 原理: 如果设置了外键关联,那么我们想要删除wife的数据,必须先要删除其中与之外键关联的丈夫的信息,或者设置Husband表中的外键为其他的wife数据
	 * 两种解决办法:
	 * 			1. 先删除对应的丈夫的数据
	 * 			2. 直接将丈夫对应的表的wifeId设置为其他或者为空即可
	 *
	 * 下面我们使用的是设置丈夫对应的wifeId为空,那么就可以删除其对应的妻子的数据
	 */
	@Test
	public void TestDelete(){
		Session session = null;
		Transaction transaction = null;
		try {
			// 创建session
			session = HibernateUntil.getSession();
			// 开始事务
			transaction = session.beginTransaction();

			//查询到id=2的wife数据
			Wife wife=session.get(Wife.class,2);
			//查询其对应的丈夫,这里还没有讲到其他的查询条件,所以我们默认id=2就是wife的id=2的对应的丈夫
			Husband husband=session.get(Husband.class, 2);
			//将wife设置null,表示将wifeId外键设置空,因此就断了外键关联
			husband.setWife(null);
			//删除wife
			session.delete(wife);
			// 提交事务
			transaction.commit();
		} catch (Exception exception) {
			transaction.rollback(); // 事务回滚
		} finally {
			if (session!=null) {
				session.close();
			}
		}
	}
}

通过妻子访问丈夫

  • 那么根据需求,此时就是丈夫的主键作为妻子的外键,那么只需要在WIfe的类中添加一个Husband对象属性即可

  • Wife

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity
@Table(name="wife")
public class Wife{
	private int id;
	private String name;
	private int age;
	private Husband husband;
	@Id
	@GeneratedValue   //主键生成策略,自增长
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@OneToOne   //设置丈夫的主键为妻子外键
	@JoinColumn(name="husbandId") // 外键名称为husbandId
	public Husband getHusband(){
		return husband;
	}
	public void setHusband(Husband husband){
		this.husband = husband;
	}
	@Column(length=20)   //设置长度为20
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
	@Override
	public String toString(){
		return "Husband [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
}
  • Husband的类不用改变,还是如第一个的样子

总结

  • 单向连接就是只能通过一个对象访问另一个对象的属性,只需要在一个实体类中添加另外一个类的对象为成员变量即可,并且在该对象的get方法上添加 OneToOne 注解即可,就表示这个对象的主键会作为该实体类的外键

双向外键关联(@OneToOne(mappedBy=””)

  • 所谓的双向的外键关联,就是两个实体类可以互相访问对方的属性,那么此时就需要在两个实体类中都要添加对方的对象为成员变量

问题

  • 在两个实体类中都添加对方的对象作为自己的成员变量,那么我们此时就需要在两个实体类中都要使用 OneToOne 注解,但是我们使用了OneToOne就会在两张表中都会将对方的主键作为自己的外键,显然是没有必要的,冗余。

解决办法

  • 我们在不想作为外键的属性的get方法上添加 mappedBy ,或者在想要成为对方的外键的类中的对方的对象的get方法中添加即可。

  • 但是我们需要注意的是: mappedBy=”“ ,其中的值一定要和该类对象对方类中属性的字段相同

实现

  • 我们让Wife作为Husband的外键,所以 mappedBy 添加到Wife类中的Husband对象的get方法头上即可

  • Husband实体类

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity   //指定实体类
@Table(name="husband")   //指定对应数据库的表名为husband
public class Husband{
	private int id;
	private String name;
	private int age;
	private Wife Wife;   //Wife对象
	@Id
	@GeneratedValue   //主键生成策略,自增长
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@OneToOne   //设置wife的主键为Husband的外键,默认的对应表中的字段为wife_id
	@JoinColumn(name="wifeid") // 默认外键的名字为wife_id.我们使用这个注解改变外键的名字为wifeid
	public Wife getWife(){
		return Wife;
	}
	public void setWife(Wife wife){
		Wife = wife;
	}
	@Column(length=20)   //设置长度为20
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
	@Override
	public String toString(){
		return "Husband [id=" + id + ", name=" + name + ", age=" + age
				+ ", Wife=" + Wife + "]";
	}

}
  • Wife类(添加@oneToOne(mappedBy=”wife”))
    • 将维护权交给了wife,表示wife作为husband的外键
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name="wife")
public class Wife{
	private int id;
	private String name;
	private int age;
	private Husband husband;   //Husband对象
	@Id
	@GeneratedValue   //主键生成策略,自增长
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}

	/**
	 * mappedBy="对方类中的该类的属性名字",注意这里的名字和一定要和对方类中的成员变量的字段一样
	 * 			表示将维护权交给对方类中的当前类的对象,就是表示当前类的主键将会作为外键
	 */
	@OneToOne(mappedBy="wife")  //设置关联,并且将维护权交给了对方类中的属性wife,因此这里的外键就是wifeId
	public Husband getHusband(){
		return husband;
	}
	public void setHusband(Husband husband){
		this.husband = husband;
	}
	@Column(length=20)   //设置长度为20
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
	@Override
	public String toString(){
		return "Wife [id=" + id + ", name=" + name + ", age=" + age
				+ ", husband=" + husband + "]";
	}

}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name="wife")
public class Wife{
	private int id;
	private String name;
	private int age;
	private Husband husband;   //Husband对象
	@Id
	@GeneratedValue   //主键生成策略,自增长
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}

	/**
	 * mappedBy="对方类中的该类的属性名字",注意这里的名字和一定要和对方类中的成员变量的字段一样
	 * 			表示将维护权交给对方类中的当前类的对象,就是表示当前类的主键将会作为外键
	 */
	@OneToOne(mappedBy="wife")  //设置关联,并且将维护权交给了对方类中的属性wife,因此这里的外键就是wifeId
	public Husband getHusband(){
		return husband;
	}
	public void setHusband(Husband husband){
		this.husband = husband;
	}
	@Column(length=20)   //设置长度为20
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
	@Override
	public String toString(){
		return "Wife [id=" + id + ", name=" + name + ", age=" + age
				+ ", husband=" + husband + "]";
	}

}

测试

  • 我们只要查询到Husband对象就可以访问到其中的Wife对象的数据,同样的只要查询到Wife对象就可以访问到其中的Husband对象的数据

  • 这里就不再测试了

一对多

  • 一个宿舍可以被多个学生住,这个就是一对多的关系,其中宿舍是One的一方,学生是Many的一方

准备

  • Student实体类
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="student")
public class Student{
	private int id;  //主键
	private String name;
	private int age;
	@Id
	@GeneratedValue
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@Column(length=10)
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
}
  • 宿舍的实体类
/**
 * 一个宿舍可以被多个学生住
 * 一个学生只能住在一个宿舍
 * 学生是One
 * 宿舍是Many
 */
public class Dormitory{
	 private int id;  //主键
	 private Long number;   //宿舍编号
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	public Long getNumber(){
		return number;
	}
	public void setNumber(Long number){
		this.number = number;
	}

}

前提须知

  • 我们知道无论是一对多还是多对一的关系,在创建表的关联关系的时候,外键总是在多的一方,即是一的一方的主键作为多的一方的外键

单向外键关联

  • 前面已经说过,单向外键关联是只能单向访问,只能一张表访问另外一张表,比如通过One的一方可以访问到Many的一方,也可以通过Many的一方访问到One的一方

从One的一方访问Many的一方(@OneToMany)

  • 即是通过学生查询到其所住的宿舍
  • 想要通过学生查询到所住的宿舍,那么必须在Student的实体类中必须有Dormitory的对象作为其成员变量

  • Dormitory类(One的一方,使用@OneToMany)

@Entity
@Table(name = "dormitory")
public class Dormitory{
	private int id; // 主键
	private Long number; // 宿舍编号
	private Set<Student> students;

	@Id
	@GeneratedValue
	public int getId(){
		return id;
	}
	@OneToMany  //Dormitory是One的一方,Student是Many的一方,因此这里使用OneToMany
	@JoinColumn(name="dormitory_id")  //必须指定外键的名称,否则将会自动创建第三张表来管理关联关系
	public Set<Student> getStudents(){
		return students;
	}

	public void setStudents(Set<Student> students){
		this.students = students;
	}

	public void setId(int id){
		this.id = id;
	}

	public Long getNumber(){
		return number;
	}

	public void setNumber(Long number){
		this.number = number;
	}

}
  • Student类(Many的一方,不变)

  • 测试

    • 添加: 在为Many的一方(Student)添加宿舍信息的时候,这个宿舍的信息一定是在数据库中的,因为添加外键相当于必须这个外键存在才能添加

    • 删除: 在删除的One的一方的时候,一定要确保Many的一方没有与其外键关联,否则将会删除失败,除非设置了级联删除,那么会连同外键关联的数据一起删除(以后再讲)

@Test
public void TestGet(){
	Session session = null;
	Transaction transaction = null;
	try {
		// 创建session
		session = HibernateUntil.getSession();
		// 开始事务
		transaction = session.beginTransaction();
		Dormitory dormitory=new Dormitory();
		dormitory.setNumber(10011L);
		//创建一个Set集合存储Student对象
		Set<Student> students=new HashSet<Student>();
		 for(int i=0;i<5;i++){
			 Student student=new Student();
			 student.setAge(10*i);
			 student.setName("name_"+i);
			 session.save(student);
			 students.add(student); // 添加到集合中
		 }
		 dormitory.setStudents(students);  //将学生信息添加到宿舍对象中
		 session.save(dormitory);  //保存宿舍信息
		// 提交事务
		transaction.commit();
	} catch (Exception exception) {
		transaction.rollback(); // 事务回滚
	} finally {
		if (session != null) {
			session.close();
		}

	}
}

从Many的一方查询One的一方(@ManyToOne)

  • 即是通过学生对象查询到宿舍信息,因此需要在学生的实体类中添加宿舍的实体类对象

  • Student实体类(使用@ManyToOne)

@Entity
@Table(name="student")
public class Student{
	private int id;  //主键
	private String name;
	private int age;
	private Dormitory dormitory;   //添加Dormitory对象,因为是One的一方,因此不用Set集合存储
	@Id
	@GeneratedValue
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@ManyToOne   //使用@ManyToOne,因为Student是Many的一方
	@JoinColumn(name="dormitory_id")   //设置外键的字段值
	public Dormitory getDormitory(){
		return dormitory;
	}
	public void setDormitory(Dormitory dormitory){
		this.dormitory = dormitory;
	}
	@Column(length=10)
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
}
  • Dormitory实体类,不用改变,还是和前面的最初的实体类一样

双向外键关联

  • 即是通过One的一方可以访问到Many的一方,也可以通过Many的一方访问到One的一方。简单的说就是可以互相访问对方的数据。
  • 要想实现双向外键关联,必须是两个实体类对象互为对方的成员属性

问题并解决

  • 因为是双向关联,因此这里的要设置双向关联的主导对象(mappedBy),否则将会出现两张表的外键都是对方的主键,这显然是冗余的,因此我们需要设置一个主导的。我们这里应该选择多的一方为主导位置的,因此需要在一的这一方使用mppedBy指定主导对象。因此我们只需要在 @OneToMany 上加上 mappedBy 属性即可。

  • 由于无论是一对多还是多对一的关系,外加都是One一方的主键,因此要将维护权交给One的一方,因此只需要在 @OneToMany 这个注解中添加 mappedBy 这个属性即可

  • 由于外键是在One的一方添加的,即是外键在student的表中,因此只有在Student的实体类中可以使用 @JoinColumn() 设置外键的字段名

实现

  • Student实体类(Many的一方,因此使用@ManyToOne)
@Entity
@Table(name="student")
public class Student{
	private int id;  //主键
	private String name;
	private int age;
	private Dormitory dormitory;   //添加Dormitory对象,因为是One的一方,因此不用Set集合存储
	@Id
	@GeneratedValue
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@ManyToOne   //使用@ManyToOne,因为Student是Many的一方
	@JoinColumn(name="dormitory_id")   //设置外键的字段值,因为外键是在student表中添加的,因此只能在这个地方设置外键的字段名
	public Dormitory getDormitory(){
		return dormitory;
	}
	public void setDormitory(Dormitory dormitory){
		this.dormitory = dormitory;
	}
	@Column(length=10)
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
}
  • Dormitory实体类(One的一方,使用@OneToMany)
@Entity
@Table(name = "dormitory")
public class Dormitory{
	private int id; // 主键
	private Long number; // 宿舍编号
	private Set<Student> students;
	@Id
	@GeneratedValue
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	//仍然需要注意的是mappedBy的值必须是对方类中该类对象的一样的字段
	@OneToMany(mappedBy="dormitory")  //添加注解,由于是双向外键关联,必须添加mappedBy,由于外键就是One的一方的主键,因此这里的只需要在OneToMany中添加即可
	public Set<Student> getStudents(){
		return students;
	}
	public void setStudents(Set<Student> students){
		this.students = students;
	}
	public Long getNumber(){
		return number;
	}
	public void setNumber(Long number){
		this.number = number;
	}
}

总结

  • 无论是一对多还是多对一的关系,在建立表的时候总是在Many的一方添加One的一方的外键

  • 在单向外键关联中,如果通过One的一方获取Many的一方数据,那么需要在One的实体类中添加Many的实体类的对象为其成员变量,同时在这个成员变量的get方法上方使用 @OneToMany 这个注解。如果想要通过Many的一方获取One的数据,那么需要在Many的实体类中添加One的实体类的对象为其成员变量,同时在这个成员变量的get方法上使用 @ManyToOne 这个注解

  • 在双向外键关联,那么我们在使用 @JoinColumn 改变外键的字段名,那么必须在One的实体类中使用,因为外键是设置在One的一方的表中

  • 双向外键关联必须使用 @OneToMany(mappedBy=) 设置主导地位的表,如果不设置这个mappedBy,那么就会出现双向外键,出现了冗余

多对一

  • 一对多和多对一是相对的,因此这里的使用和一对多是一样的,不再反复的讲述了

多对多

背景

  • 一个老师可以教多个学生,一个学生可以被多个老师教,那么老师和学生的关系就是多对多的关系

    准备

  • 老师的实体类(Teacher)

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="teacher")
public class Teacher{
	private int id;  //主键
	private String name;
	private int age;
	@Id
	@GeneratedValue
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@Column(length=10)
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
}
  • 学生的实体类(Student)
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="student")
public class Student{
	private int id;  //主键
	private String name;
	private int age;
	@Id
	@GeneratedValue
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@Column(length=10)
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
}

前提须知

  • 我们在处理多对多的关系,在建立表的时候使用的是第三张表来维护外键,如下:

    Hibernate关联关系

单向外键关联(@ManyToMany)

通过学生访问老师的信息

  • 根据需求我们必须在Student的类中将Teacher类的对象声明为成员变量,多对多的关系,因此使用的是Set集合来存储

  • Student的实体类

@Entity
@Table(name="student")
public class Student{
	private int id;  //主键
	private String name;
	private int age;
	private Set<Teacher> teachers;
	@Id
	@GeneratedValue
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@ManyToMany
	//指定第三张表的名称,如果默认的是student_teacher,joinColumns指定的是当前的实体类的外键名称,inverseJoinColumns指定的是另外一个实体类的外键名称
	//如果不指定外键的名称,那么默认的是student_id,和teacher_id
	@JoinTable(name="stu_tea",joinColumns=@JoinColumn(name="st_id"),inverseJoinColumns=@JoinColumn(name="t_id"))
	public Set<Teacher> getTeachers(){
		return teachers;
	}
	public void setTeachers(Set<Teacher> teachers){
		this.teachers = teachers;
	}
	@Column(length=10)
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
}

@JoinTable

  • 在多对多的关系中,默认创建第三张表的名称为 : 表名_表名 ,但是我们可以使用 @JoinTable 这个注解来修改第三张表的名称
  • 其中的 name 属性可以修改

@ManyToMany

  • 在多对多的关系中使用,在实体类对象的get方法上面使用
  • joinColumns 指定的是当前的实体类对应的外键名称,其中的值使用的 @JoinColumn 注解
  • inverseJoinColumns 指定的是另外一个实体类的外键名称,其中的值使用的是 @JoinColumn 注解

通过老师访问学生的信息

  • 那么需要在Teacher类中添加一个成员变量的类型为Student对象,并且在该成员变量的 get 方法上使用 @ManyToMany

  • 这个就不在演示了,和上面很相似

双向外键关联(@ManyToMany(mappedBy=””))

  • 如果老师想要知道自己教的学生的信息,学生也想知道老师的信息,那么就需要使用多对多双向关联,在两个实体类中都要定义对方的实体类的对象,因此这样就可以访问到对方的信息了。

  • 这个和前面说的一样,当使用双向外键联系的时候,一定要设置主导的实体类( mappedBy )否则的话就会出现冗余,因此一定要指定主导关系。

  • 下面我们的范例是指定学生的主导位置,因此要在老师的实体类中设置 mappedBy 属性

  • Student类

    • 由于指定Student为主导位置,因此设置外键的名称和第三张表名字只能在Student的类中设置
@Entity
@Table(name="student")
public class Student{
	private int id;  //主键
	private String name;
	private int age;
	private Set<Teacher> teachers;
	@Id
	@GeneratedValue
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@ManyToMany
	//指定第三张表的名称,如果默认的是student_teacher,joinColumns指定的是当前的实体类的外键名称,inverseJoinColumns指定的是另外一个实体类的外键名称
	//如果不指定外键的名称,那么默认的是student_id,和teacher_id
	@JoinTable(name="stu_tea",joinColumns=@JoinColumn(name="st_id"),inverseJoinColumns=@JoinColumn(name="t_id"))
	public Set<Teacher> getTeachers(){
		return teachers;
	}
	public void setTeachers(Set<Teacher> teachers){
		this.teachers = teachers;
	}
	@Column(length=10)
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
}
  • Teacher类(指定学生的主导位置,因此这里不能设置外键的名称和外键的字段名)
@Entity
@Table(name="teacher")
public class Teacher{
	private int id;  //主键
	private String name;
	private int age;
	private Set<Student> students;
	@Id
	@GeneratedValue
	public int getId(){
		return id;
	}
	public void setId(int id){
		this.id = id;
	}
	@ManyToMany(mappedBy="teachers") //将维护权交给teacher表,这里的teachers是Student类中的字段名,一定要一模一样的
	public Set<Student> getStudents(){
		return students;
	}
	public void setStudents(Set<Student> students){
		this.students = students;
	}
	@Column(length=10)
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age = age;
	}
}

完整核心配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>

		<!-- 必须要配置的5大参数,4大参数,一个方言 其中的四大参数是连接JDBC必须的参数 这里的方言也是必须的 -->
		<propertyname="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

		<propertyname="hibernate.connection.url">jdbc:mysql://localhost:3306/hirbernate</property>

		<propertyname="hibernate.connection.username">root</property>

		<propertyname="hibernate.connection.password">root</property>

		<!-- mysql的方言 -->
		<propertyname="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>


		<!-- 可选的配置文件 -->
		<!-- 输出所有的sql语句到控制台 -->
		<propertyname="hibernate.show_sql">true</property>

		<!-- 在控制台上打印出漂亮的sql语句 -->
		<propertyname="hibernate.format_sql">true</property>

		<!-- 配置如果这个表还没有创建,那么就会自动创建,如果已经创建了,那么会自动更新 -->
		<propertyname="hibernate.hbm2ddl.auto">update</property>

		<!-- 配置不生成Hibernate_sequence -->
		<propertyname="hibernate.id.new_generator_mappings">false</property>

		<!-- 直接指定这个Teacher实体类的全类名即可,即是完成了映射 -->
		<mappingclass="cn.tedu.bean.Student"></mapping>
		<mappingclass="cn.tedu.bean.Teacher"></mapping>
	</session-factory>
</hibernate-configuration>

总结

  1. 在双向外键关联的关系中,一定要使用 mappedBy 指定外键的维护权,否则将会出现数据冗余
  2. 在一对以和一对多,多对一的关系中,我们可以使用 @JoinColumn 这个注解来设置外键的字段名,但是在多对多的关系中,因为需要第三张表来维护,因此要使用 @JoinTable 这个注解来设置外键和第三张表的一些属性

原文 

https://chenjiabing666.github.io/2018/04/24/Hibernate关联关系/

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Hibernate关联关系

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址