提示:表与表之间的映射关系通过外键或中间表建立联系:
- 单向外键映射关系:这里主要是指在两个映射表对象类(一对一关系)之间只有一方包含另一方的引用,比如映射对象类A与映射表对象B之间,如果只有A中有一个属性是对B的引用 private B b;那么就称为单向外键关系。
- 双向外键映射关系;这里指双方都包含一方的引用,但如果是没有中间表,那么就要指定主控方与被控方。
上面所说的单向外键关系与映射外键关系虽然存在着引用的差别,但是(比如一对一或者多对多等)在建表的时候,表的结构是一样的,比如主控方是A被控方是B,两种情况下表结构一样,都是A对应的表中包含B的主键作为外键,不同的是,单向外键关系,从主控方的对象可以获取被控方对象,而双向外键关系则可以从任何一方获取另一方的对象。差别在于hibernate操作数据库的时候,是否连带关联操作被控方。
这里使用的是Hibernate5 而且里面有一个包与javaEE库有冲突 :在用@JoinColumn(name=”pid”)创建外键一直报错 是因为 javax.persistence.jar与 hibernate中的hibernate-jpa-2.1-api-1.0.0.Final.jar冲突,可以先删除javaEE类库然后在重新添加就可以了。
这里使用的是hibernate5
配置文件:
<?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>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.url">jdbc:mysql:///hibernate?useUnicode=true&characterEncoding=UTF8</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.username">root</property>
<property name="connection.password">12345</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<property name="hibernate.current_session_context_class">thread</property>
<!--
<mapping class="com.hibernate.environment.Students"/>
<mapping class="com.hibernate.environment.Person"/>
<mapping class="com.hibernate.environment.Dog"/>
-->
<mapping class="com.hibernate.relationship.oneTone.IdCardOneToOneFK"/>
<mapping class="com.hibernate.relationship.oneTone.PersonOneToOneFK"/>
</session-factory>
</hibernate-configuration>
1、一对一单向外键关系
主控方person类(含有IdCard类引用,并且数据库表中含有IdCard对应表字段作为外键)
package com.hibernate.relationship.oneTone;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
//一对一单向外键
@Entity
public class PersonOneToOneFK {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY ) //整型可以指定默认增长策略
private int wid;
private int age;
@OneToOne(cascade=CascadeType.ALL) //全级联关系
@JoinColumn(name="pid",unique=true) //指定外键名称为pid
private IdCardOneToOneFK idCard;
public PersonOneToOneFK() {
}
public PersonOneToOneFK( int age, IdCardOneToOneFK idCard) {
this.age = age;
this.idCard = idCard;
}
public int getWid() {
return wid;
}
public void setWid(int wid) {
this.wid = wid;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public IdCardOneToOneFK getIdCard() {
return idCard;
}
public void setIdCard(IdCardOneToOneFK idCard) {
this.idCard = idCard;
}
}
被控方IdCard类
package com.hibernate.relationship.oneTone;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
@Entity
public class IdCardOneToOneFK {
@Id
@GeneratedValue(generator="pid")
@GenericGenerator(name="pid",strategy="assigned")
@Column(length=18)
private String pid; //主键是字符串类型需要指定主键生成器以及生成策略,同时还要指定长度,否则默认255长度会报错不能作为主键长度
@Column(length=24)
private String name;
public IdCardOneToOneFK() {
super();
// TODO Auto-generated constructor stub
}
public IdCardOneToOneFK(String pid, String name) {
super();
this.pid = pid;
this.name = name;
}
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
生成数据表结构代码:
这里用的hibernate5与之前版本有点差别
@Test
public void createTable(){
Configuration configuration = new Configuration();
System.out.println(configuration);
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().configure().build();
MetadataImplementor metadata = (MetadataImplementor) new MetadataSources( serviceRegistry ).buildMetadata();
new SchemaExport(metadata).create(true, true);
}
生成数据表结构:
Person类对应数据表
IdCard类对应数据表
注意:在保存对象的时候,先保存IdCard类,然后再保存Person类,因为主控方包含被控方的引用,在数据库表中引用被控方字段作为外键,所以需要先有保存IdCard,用其字段值(这里是它的主键)作为外键。
2、一对一双向外键关系
Person类
package com.hibernate.relationship.oneTone;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
@Entity
public class PersonOneToOneBFK {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY )
private int wid;
private int age;
@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="pid",unique=true)
private IdCardOneToOneBFK Card;
public PersonOneToOneBFK() {
}
public PersonOneToOneBFK( int age, IdCardOneToOneBFK Card) {
this.age = age;
this.Card = Card;
}
public int getWid() {
return wid;
}
public void setWid(int wid) {
this.wid = wid;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public IdCardOneToOneBFK getCard() {
return Card;
}
public void setCard(IdCardOneToOneBFK card) {
Card = card;
}
}
IdCard类
package com.hibernate.relationship.oneTone;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import org.hibernate.annotations.GenericGenerator;
@Entity
public class IdCardOneToOneBFK {
private String pid;
private String name;
private PersonOneToOneBFK person;
public IdCardOneToOneBFK() {
super();
// TODO Auto-generated constructor stub
}
public IdCardOneToOneBFK(String pid, String name) {
super();
this.pid = pid;
this.name = name;
}
@Id
@GeneratedValue(generator="pid")
@GenericGenerator(name="pid",strategy="assigned")
@Column(length=18)
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
@Column(length=24)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@OneToOne(mappedBy="Card") //里面表示主控方里面对该类变量的引用名称
public PersonOneToOneBFK getPerson() {
return person;
}
public void setPerson(PersonOneToOneBFK person) {
this.person = person;
}
}
Person类对应的数据表
IdCard类对应的数据表
注意:单向外键与双向外键在建表结构的时候是一样的,区别是在获取其中一个对象能否从获取另一个对象(是否有引用),双向外键关系需要指定一方来管理之间的关系比如在IdCard类中@OneToOne(mappedBy=”Card”)用来指定控制管理权在Person类中。
3、一对多双向外键关系(多的一方持有外键)
Student类
package com.hibernate.realtionship.oneTmany;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import org.hibernate.annotations.GenericGenerator;
@Entity
public class Student {
private String sid;
private String sname;
private int age;
private ClassRoom classroom;
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(String sid, String sname, int age, ClassRoom classroom) {
super();
this.sid = sid;
this.sname = sname;
this.age = age;
this.classroom = classroom;
}
@Id
@GeneratedValue(generator="sid")
@GenericGenerator(name="sid",strategy="assigned")
@Column(length=10)
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
@Column(length=24)
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
@JoinColumn(name="cid")
public ClassRoom getClassroom() {
return classroom;
}
public void setClassroom(ClassRoom classroom) {
this.classroom = classroom;
}
}
Classroom类
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import org.hibernate.annotations.GenericGenerator;
@Entity
public class ClassRoom {
private String cid;
private String cname;
private Set<Student> stu;
public ClassRoom() {
super();
// TODO Auto-generated constructor stub
}
public ClassRoom(String cid, String cname, Set<Student> stu) {
super();
this.cid = cid;
this.cname = cname;
this.stu = stu;
}
@Id
@GeneratedValue(generator="cid")
@GenericGenerator(name="cid",strategy="assigned")
@Column(length=10)
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
@Column(length=40)
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinColumn(name="cid")
public Set<Student> getStu() {
return stu;
}
public void setStu(Set<Student> stu) {
this.stu = stu;
}
}
数据表结构:
Student类对应的数据库表结构
Classroom类对应的数据库表结构
注意:同样是双向外键关联,在 建造数据表的时候,只有一方持有外键,在数据表结构没差别,但在hibernate获取对象的时候有差别。这里相当于主控方是Students,Students里面用到@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
@JoinColumn(name=”cid”)使用这些指明级联关系,以及加载方式为积极加载,同时指明外键为cid,同样在Classroom中,对应用@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinColumn(name=”cid”)同样也是指明外键为cid(并不是指明Classroom在Student类中的引用名称)。
4、多对一的外键单向关联关系(多的一方持有外键关系)
Student类
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import org.hibernate.annotations.GenericGenerator;
@Entity
public class Student {
@Id
@GeneratedValue(generator="sid")
@GenericGenerator(name="sid",strategy="assigned")
@Column(length=8)
private String sid;
@Column(length=24)
private String sname;
private int age;
@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
@JoinColumn(name="cid",referencedColumnName="cId")
private Classroom classroom;
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(String sid, String sname, int age, Classroom classroom) {
super();
this.sid = sid;
this.sname = sname;
this.age = age;
this.classroom = classroom;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Classroom getClassroom() {
return classroom;
}
public void setClassroom(Classroom classroom) {
this.classroom = classroom;
}
}
Classroom类
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
@Entity
public class Classroom {
@Id
@GeneratedValue(generator="cid")
@GenericGenerator(name="cid",strategy="assigned")
@Column(length=8)
private String cid;
@Column(length=50)
private String cname;
public Classroom() {
super();
// TODO Auto-generated constructor stub
}
public Classroom(String cid, String cname) {
super();
this.cid = cid;
this.cname = cname;
}
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
}
数据库表结构:
Student类对应的数据库表结构:
Student类对应的数据表结构:
Classroom类对应的数据表结构:
注意:这里多对一和一对多无论是双向还是单向外键关系,但在建表结构上是一样的。这里是多的一方Student持有对方的引用,所以在保存对象的时候要先保存被控方,也就是被引用的一方。
5、多对多单向外键关系
Student类:
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import org.hibernate.annotations.GenericGenerator;
@Entity
public class Student {
private String sid;
private String sname;
private String major;
private Set<Teacher> teacher;
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(String sid, String sname, String major, Set<Teacher> teacher) {
super();
this.sid = sid;
this.sname = sname;
this.major = major;
this.teacher = teacher;
}
@Id
@GeneratedValue(generator="sid")
@GenericGenerator(name="sid",strategy="assigned")
@Column(length=10)
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
@Column(length=24)
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
@Column(length=40)
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
@ManyToMany
@JoinTable(name="student_teacher",
joinColumns={@JoinColumn(name="sid")},
inverseJoinColumns={@JoinColumn(name="tid")}
)
public Set<Teacher> getTeacher() {
return teacher;
}
public void setTeacher(Set<Teacher> teacher) {
this.teacher = teacher;
}
}
Teacher类
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
//单向是指只有一方持有另一方的引用 而双向指双方都有一方的引用
@Entity
public class Teacher {
private String tid;
private String tname;
private int age;
public Teacher() {
super();
// TODO Auto-generated constructor stub
}
public Teacher(String tid, String tname, int age) {
super();
this.tid = tid;
this.tname = tname;
this.age = age;
}
@Id
@GeneratedValue(generator="tid") //有指定数id生成策略 或者使用hibernate生成器
@GenericGenerator(name="tid",strategy="assigned")
@Column(length=10)
public String getTid() {
return tid;
}
public void setTid(String tid) {
this.tid = tid;
}
@Column(length=24)
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
数据库数据表结构:
Student类对应表数据结构
Teacher类对应的表数据结构
中间表student_teacher对应的数据表结构
注意:多对多关系不能单纯靠两个表外键关系来关联,而是需要一个中间表来建立之间的关系。在其中一方添加@ManyToMany
@JoinTable(name=”student_teacher”,
joinColumns={@JoinColumn(name=”sid”)},
inverseJoinColumns={@JoinColumn(name=”tid”)}
)来创建中间表并制定其中的属性名外键以及反转属性值,这样就可以通过中间表来建立多对多的外键关系。(name是创建数据库表的表名,后面指明创建表的主键值)
6、多对多双向外键关系
Student类
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import org.hibernate.annotations.GenericGenerator;
@Entity //单向引用只能先添加被引用 双向引用这都可以
public class StudentBFK {
private String sid;
private String sname;
private String major;
private Set<TeacherBFK> teacher;
public StudentBFK() {
super();
// TODO Auto-generated constructor stub
}
public StudentBFK(String sid, String sname, String major, Set<TeacherBFK> teacher) {
super();
this.sid = sid;
this.sname = sname;
this.major = major;
this.teacher = teacher;
}
@Id
@GeneratedValue(generator="sid")
@GenericGenerator(name="sid",strategy="assigned")
@Column(length=10)
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
@Column(length=24)
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
@Column(length=40)
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
@ManyToMany
@JoinTable(name="student_teacher",
joinColumns={@JoinColumn(name="sid")},
inverseJoinColumns={@JoinColumn(name="tid")}
)
public Set<TeacherBFK> getTeacher() {
return teacher;
}
public void setTeacher(Set<TeacherBFK> teacher) {
this.teacher = teacher;
}
}
Teacher类
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import org.hibernate.annotations.GenericGenerator;
//单向是指只有一方持有另一方的引用 而双向指双方都有一方的引用
@Entity
public class TeacherBFK {
private String tid;
private String tname;
private int age;
private Set<StudentBFK> student;
public TeacherBFK() {
super();
// TODO Auto-generated constructor stub
}
public TeacherBFK(String tid, String tname, int age,Set<StudentBFK> studnet) {
super();
this.tid = tid;
this.tname = tname;
this.age = age;
this.student=student;
}
@Id
@GeneratedValue(generator="tid") //有指定数id生成策略 或者使用hibernate生成器
@GenericGenerator(name="tid",strategy="assigned")
@Column(length=10)
public String getTid() {
return tid;
}
public void setTid(String tid) {
this.tid = tid;
}
@Column(length=24)
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@ManyToMany(mappedBy="teacher")
public Set<StudentBFK> getStudent() {
return student;
}
public void setStudent(Set<StudentBFK> student) {
this.student = student;
}
}
数据库数据表结构
Student类对应的数据表结构
Teacher类对应的数据表结构
Student_teacher数据表结构
注意:这里双向外键关系同样与单向外键关系之间的区别在于是否有引用,以及还要有相应的配置 ,区别在于 teacher类中也包含Student的引用Set集合,同时也要有相应的配置在Student属性上配置@ManyToMany(mappedBy=”teacher”)主控方在Student类。
总结:
》一对一关系:单向外键关系只有一方持有一方的引用;双向外键关系双方都持有对方的引用,则双方对等需要用@OneToOne(mappedBy=”该类在对方的类的属性引用”)指定主控方。
》一对多(多对一)关系:单向外键关系只有一方持有另一方的引用;双向外键关系双方都持有对方的引用,同时一方要有相应的配置:比如少的一方@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinColumn(name=”cid”)指明关系外,还要指明外键(否则会多一个表)这里的外键同样为少的一方的id,与多的一方相应的配置@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
@JoinColumn(name=”cid”)配置的外键名相同,这里并没有类似其他的指明主控方,但是可以按双方不对等来记忆不用onetoone、manytomany的 mappedBy直接指明主控方。
》多对多关系:单向外键关系只有一方持有另一方的引用,双向外键关系是双方都持有对方的引用,同时还要新增配置@ManyToMany(mappedBy=”teacher”)来指明主控方。