一:单向1-1关联
以老夫老妻为例子来说明一下,在中国以前是以丈夫主导地位,所以我这里以t_husband作为主表,t_wife作为从表
1.1:无连接表的单向1-1
(1)首先创建实体类,然后使用注解表示出来两者的关联关系
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 = "t_husband")
public class Husband {
@Id @Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer hid;
private String hname;
public Husband() {
super();
}
public Husband(String hname) {
this.hname = hname;
}
public Integer getHid() {
return hid;
}
public void setHid(Integer hid) {
this.hid = hid;
}
public String getHname() {
return hname;
}
public void setHname(String hname) {
this.hname = hname;
}
}
@Entity
@Table(name = "t_wife")
public class Wife {
@Id @Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer wid;
private String wname;
@OneToOne(targetEntity = Husband.class,cascade = CascadeType.ALL)
@JoinColumn(name = "hid",referencedColumnName = "hid")
private Husband husband;
public Wife() {
super();
}
public Integer getWid() {
return wid;
}
public void setWid(Integer wid) {
this.wid = wid;
}
public String getWname() {
return wname;
}
public void setWname(String wname) {
this.wname = wname;
}
public Husband getHusband() {
return husband;
}
public void setHusband(Husband husband) {
this.husband = husband;
}
}
(2)然后和xml配置的方式相同,使用mapping标签引入到hibernate的核心配置中去,只不过如果是xml配置的方式是resource属性,而注解的方式是class属性而已
<!-- <mapping resource="cn/itcast/manyTomany/Teacher.hbm.xml"/> -->
<mapping class="cn.itcast.entity.Husband"></mapping>
<mapping class="cn.itcast.entity.Wife"></mapping>
(3)然后执行
public class SingleAssociationDemo {
private Transaction tx;
private Session session;
private SessionFactory sessionFactory;
@Before
public void before() {
Configuration cfg = new Configuration().configure();
sessionFactory = cfg.buildSessionFactory();
session = sessionFactory.openSession();
tx = session.beginTransaction();
}
@After
public void after() {
tx.commit();
session.close();
sessionFactory.close();
}
@Test
public void testOneToOne() {
//创建一个瞬时态的Husband对象
Husband husband = new Husband("张三");
//创建一个瞬时态的Wife对象
Wife wife = new Wife();
wife.setWname("西施");
wife.setHusband(husband);
session.save(wife);
}
}
(4)最终数据库中生成的表的sql语句: 使用show create table 表名
CREATE TABLE `t_husband` (
`hid` int(11) NOT NULL AUTO_INCREMENT,
`hname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`hid`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
CREATE TABLE `t_wife` (
`wid` int(11) NOT NULL AUTO_INCREMENT,
`wname` varchar(255) DEFAULT NULL,
`hid` int(11) DEFAULT NULL,
PRIMARY KEY (`wid`),
KEY `FK14fb3xipo2gwpwku60lkhqsqy` (`hid`),
CONSTRAINT `FK14fb3xipo2gwpwku60lkhqsqy` FOREIGN KEY (`hid`) REFERENCES `t_husband` (`hid`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
(5)表之间的结构图
(6)大概解释一下注解的含义
1.2:有连接表的单向1-1
注:非常少见,此处不再介绍!
二:单向1-N关联
以经典的部门和员工的关系来说明一下,一个部门有多个员工,所以在Deparement类中需要关联Employee类,因为是单向关系,所以在Employee类中不需要关联Department类,在hibernate中表示这种多的关系,推荐使用Set集合。
2.1:无连接表的单向1-N
(1)首先创建实体类,然后使用注解表示出来两者的关联关系
@Entity
@Table(name="t_department")
public class Department {
@Id @Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer did;
private String dname;
@OneToMany(targetEntity = Employee.class, cascade = CascadeType.ALL)
@JoinColumn(name="did", referencedColumnName = "did")
private Set<Employee> employees = new HashSet<Employee>();
//省了get/set方法
}
@Entity
@Table(name="t_employee")
public class Employee {
@Id @Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer eid;
private String ename;
//省了get/set方法
}
(2)引入到核心配置文件:hibernate.cfg.xml
<mapping class="cn.itcast.entity.Department"></mapping>
<mapping class="cn.itcast.entity.Employee"></mapping>
(3)然后测试执行:是基于在SingleAssociationDemo 类中执行的
@Test
public void testOneToMany() {
//创建瞬时态的Employee对象
Employee employee1 = new Employee();
employee1.setEname("wzj");
Employee employee2 = new Employee();
employee2.setEname("sbt");
//创建瞬时态的Department对象
Department department = new Department();
department.setDname("技术部");
department.getEmployees().add(employee1);
department.getEmployees().add(employee2);
session.save(department);
}
(4)生成表的sql语句
CREATE TABLE `t_department` (
`did` int(11) NOT NULL AUTO_INCREMENT,
`dname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`did`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
CREATE TABLE `t_employee` (
`eid` int(11) NOT NULL AUTO_INCREMENT,
`ename` varchar(255) DEFAULT NULL,
`did` int(11) DEFAULT NULL,
PRIMARY KEY (`eid`),
KEY `FK32y5pu17i9yt51r6365lh0o2c` (`did`),
CONSTRAINT `FK32y5pu17i9yt51r6365lh0o2c` FOREIGN KEY (`did`) REFERENCES `t_department` (`did`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
(5)表之间的结构图
(6)大概解释下注解的含义
2.2:有连接表的单向1-N
有连接的1-N就说明会产生中间表(连接表)
还是以部门和员工为例子:
(1)首先创建实体类,然后使用注解表示出来两者的关联关系
@Entity
@Table(name="t_department")
public class Department {
@Id @Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer did;
private String dname;
@OneToMany(targetEntity = Employee.class)
/**
* a.name为连接表的表名
* b.joinColumns中name="deptno", referencedColumnName = "did"
* 表示当前实体Department,在连接表中的外键列名为deptno,引用当前实体的主键did
* c.inverseJoinColumns中name="empno", referencedColumnName="eid", unique=true
* 表示当前实体的关联实体,在连接表中的外键列名为empno, 引用关联实体的主键eid,unique=true就说明empno外键值唯一
*/
@JoinTable(name = "dept_emp_middle",
joinColumns = @JoinColumn(name="deptno", referencedColumnName = "did"),
inverseJoinColumns = @JoinColumn(name="empno", referencedColumnName="eid", unique=true)
)
private Set<Employee> employees = new HashSet<Employee>();
public Integer getDid() {
return did;
}
public void setDid(Integer did) {
this.did = did;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public Set<Employee> getEmployees() {
return employees;
}
public void setEmployees(Set<Employee> employees) {
this.employees = employees;
}
}
因为是单向的,所以Employee还是与普通的实体类一样没有什么变化!
(2)引入到核心配置文件
(3)然后测试执行:是基于在SingleAssociationDemo 类中执行的
@Test
public void testOneToManyAndJoinTable() {
//创建瞬时态的Employee对象
Employee employee1 = new Employee();
employee1.setEname("wzj");
Employee employee2 = new Employee();
employee2.setEname("sbt");
//org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: cn.itcast.entity.Employee
//刚开始报错,看了一下报错信息,然后手动将Employee持久化,就好了,暂时不知道为啥
session.persist(employee1);
session.persist(employee2);
//创建瞬时态的Department对象
Department department = new Department();
department.setDname("技术部");
department.getEmployees().add(employee1);
department.getEmployees().add(employee2);
session.save(department);
}
(4)生成表的sql语句
CREATE TABLE `t_department` (
`did` int(11) NOT NULL AUTO_INCREMENT,
`dname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`did`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
CREATE TABLE `t_employee` (
`eid` int(11) NOT NULL AUTO_INCREMENT,
`ename` varchar(255) DEFAULT NULL,
PRIMARY KEY (`eid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
CREATE TABLE `dept_emp_middle` (
`deptno` int(11) NOT NULL,
`empno` int(11) NOT NULL,
PRIMARY KEY (`deptno`,`empno`),
UNIQUE KEY `UK_1o2i9petja7s7tvwpc5ulqdh9` (`empno`),
CONSTRAINT `FK8te09d5x1sulwl9vkpec2b1qc` FOREIGN KEY (`deptno`) REFERENCES `t_department` (`did`),
CONSTRAINT `FKaudnndo58fbfmomdh4fal1cia` FOREIGN KEY (`empno`) REFERENCES `t_employee` (`eid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
(5)表之间的结构图
由于采用连接表的单向1-N关联,两个实体对应的数据表都无须增加外键列
因此不存在主从表关系,程序完全可以想先持久化哪个实体,就先持久化哪个实体,
无连接的一般是使用@JoinColumn:作为外键列
有连接的一般是使用@JoinTable:作为中间表
三:单向N-N关联
多对多没有有连接表,无连接表之说,多对多的关系,本来就需要第三张表
单向多对多和单向一对多很相似,就是不用inverseJoinColumns 中添加unique=true即可
(1)首先创建实体类
@Entity
@Table(name = "t_teacher")
public class Teacher {
@Id @Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer tid;
private String tname;
@ManyToMany(targetEntity = Student.class)
@JoinTable(name = "stu_teach_middle",
joinColumns = @JoinColumn(name="teacherId", referencedColumnName="tid"),
inverseJoinColumns = @JoinColumn(name="stuId", referencedColumnName="sid")
)
private Set<Student> students = new HashSet<Student>();
//省了get/set
}
@Entity
@Table(name = "t_student")
public class Student {
@Id @Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer sid;
private String sname;
//省了get/set
}
(2)引入到核心配置文件
(3)然后测试执行:是基于在SingleAssociationDemo 类中执行的
@Test
public void testManyToManyAndJoinTable() {
//创建瞬时态的Student对象
Student s1 = new Student("张三");
Student s2 = new Student("李四");
//org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: cn.itcast.entity.Employee
//刚开始报错,看了一下报错信息,然后手动将Student持久化,就好了,暂时不知道为啥
session.persist(s1);
session.persist(s2);
//创建瞬时态的Teacher对象
Teacher t1 = new Teacher();
t1.setTname("王老师");
t1.getStudents().add(s1);
t1.getStudents().add(s2);
Teacher t2 = new Teacher();
t2.setTname("崔老师");
t2.getStudents().add(s1);
t2.getStudents().add(s2);
session.save(t1);
session.save(t2);
}
(4)生成表的sql语句
CREATE TABLE `t_teacher` (
`tid` int(11) NOT NULL AUTO_INCREMENT,
`tname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`tid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
CREATE TABLE `t_student` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`sname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`sid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
CREATE TABLE `stu_teach_middle` (
`teacherId` int(11) NOT NULL,
`stuId` int(11) NOT NULL,
PRIMARY KEY (`teacherId`,`stuId`),
KEY `FKb25u37kv24u0o0e5hrm1q3g1w` (`stuId`),
CONSTRAINT `FKb25u37kv24u0o0e5hrm1q3g1w` FOREIGN KEY (`stuId`) REFERENCES `t_student` (`sid`),
CONSTRAINT `FKt4k47wtuu528b2hgm5tg35uhr` FOREIGN KEY (`teacherId`) REFERENCES `t_teacher` (`tid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
(5)表之间的结构图
来自:虽然帅,但是菜的cxy