本文可作为北京尚学堂马士兵hibernate课程的学习笔记。
hibernate的映射,主要分为一对一,一对多,多对一,多对多,同时还要单向与双向的区别。
OK,不要纠结于名字,我们开始看例子。

一对一单向

老公是一个实体,老婆也是一个实体。


一个老公只有一个老婆,同时一个老婆也只有一个老公。


上面的关系就叫做一对一。


什么叫单向呢。


看代码:


package com.bjsxt.hibernate;

@Entity
public class Husband {
private int id;
private String name;
private Wife wife;

@Id
@GeneratedValue
public int getId() {
return id;
}

@OneToOne
@JoinColumn(name="wifeId")
public Wife getWife() {
return wife;
}
//省略get set
}


package com.bjsxt.hibernate;

@Entity
public class Wife {
private int id;
private String name;

@Id
@GeneratedValue
public int getId() {
return id;
}
//省略get set
}



看上面的代码,老公里面有老婆的引用,而老婆里面并没有老婆的引用。


换句话说在代码层次上,根据老公我们可以获得他的老婆,但是根据老婆无法获得老公。(这就是单向)



我们看看它运行的代码


package com.bjsxt.hibernate;

import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.tool.hbm2ddl.SchemaExport;

public class Test {
public static void main(String[] args) {
new SchemaExport(new AnnotationConfiguration().configure()).create(false, true);
}
}

在hibernate的配置文件里


加上


<property name="hbm2ddl.auto">update</property>

结果


19:41:04,229 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
create table Husband (
id integer not null auto_increment,
name varchar(255),
wifeId integer,
primary key (id)
)
19:41:04,549 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
create table Wife (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
19:41:04,880 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
alter table Husband
add index FKAEEA401B109E78ED (wifeId),
add constraint FKAEEA401B109E78ED
foreign key (wifeId)
references Wife (id)


先给husband加了一个字段,wifeId,然后让他作为外键引用wife表的id字段。


大家可以看到,@JoinColumn(name="wifeId")这个元标签指示了husband表内部那那个关联字段的名字。


如果没有这个元标签呢?


那么关联字段就是是:wife_id。即tableName_primarykey。


如果把 @onetoone写到Wife里(husband里没有onotoone的标签),那么我们就能从wife获得husband了(在代码层次)。




一对一双向

如果我既想从husband里取得wife,也想从wife里取得husband。那咋办?


把husband里的标签复制一遍就OK了嘛。


package com.bjsxt.hibernate;

@Entity
public class Wife {
private int id;
private String name;
private Husband husband;

@Id
@GeneratedValue
public int getId() {
return id;
}

@OneToOne
@JoinColumn(name="husbandId")
public Husband getHusband() {
return husband;
}

}


OK,上面的代码里,wife也可以取到husband了。


我们看看hibernate生成的sql


create table Husband (
id integer not null auto_increment,
name varchar(255),
wifeId integer,
primary key (id)
)
19:53:20,487 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
create table Wife (
id integer not null auto_increment,
name varchar(255),
husbandId integer,
primary key (id)
)
19:53:20,824 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
alter table Husband
add index FKAEEA401B109E78ED (wifeId),
add constraint FKAEEA401B109E78ED
foreign key (wifeId)
references Wife (id)
19:53:21,421 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
alter table Wife
add index FK292331CE01A6E1 (husbandId),
add constraint FK292331CE01A6E1
foreign key (husbandId)
references Husband (id)

看见了吧,wife里面有外键,husband里面也有外键!


这不是冗余了嘛。


把wife类改成下面的样子


@OneToOne(mappedBy="wife")
public Husband getHusband() {
return husband;
}

这个mappedBy的意思是说,我(Wife这个类)和husband这个类是一对一关联的,但是关联的外键由husband类的getWife方法控制。


运行一下。


20:03:18,611 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
create table Husband (
id integer not null auto_increment,
name varchar(255),
wifeId integer,
primary key (id)
)
20:03:18,618 ERROR org.hibernate.tool.hbm2ddl.SchemaExport:348 - Unsuccessful: create table Husband (id integer not null auto_increment, name varchar(255), wifeId integer, primary key (id))
20:03:18,618 ERROR org.hibernate.tool.hbm2ddl.SchemaExport:349 - Table 'husband' already exists
20:03:18,619 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
create table Wife (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
20:03:18,933 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
alter table Husband
add index FKAEEA401B109E78ED (wifeId),
add constraint FKAEEA401B109E78ED
foreign key (wifeId)
references Wife (id)

数据库里,wife表里没有外键了。


但是,在代码层次我们依然可以从Wife类里获得Husband。



注意上面的 @OneToOne(mappedBy="wife")

只有是双向关联,最好就都写上mappedBy,两张表有一个外键就够了。





多对一单向关联

每个人都会有很多个梦想,但是每一个梦想只属于某一个具体的人。


我们先不管java代码上如何实现,在数据库里。


上面的两张表,一个是person,里面就是id,name


再就是dream表,一个id,一个description。


那么它们的外键放在哪张表里呢?


放在dream里,换言之,dream表里有一个字段叫personId。


这个的原因,不用解释。


所有一对多,多对一的关系,在数据库里,外键都是放在多的一方里的。


为什么。自己想。




看代码


package com.bjsxt.hibernate;

@Entity
public class Dream {
private int id;
private String description;
private Person person;

@Id
@GeneratedValue
public int getId() {
return id;
}
@ManyToOne
@JoinColumn(name="personId")
public Person getPerson() {
return person;
}
//省略get set
}


package com.bjsxt.hibernate;

@Entity
public class Person {
private int id;
private String name;

@Id
@GeneratedValue
public int getId() {
return id;
}
//省略get set
}

从代码层次上看"多对一单向"


就是我可以从多的一方(dream)里获得一的那一方(person)。


因为是单向,所以不能从person获得他所有的dream。


OK,生成的表里,dream里有personid。


20:20:21,970 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
create table Dream (
id integer not null auto_increment,
description varchar(255),
personId integer,
primary key (id)
)
20:20:22,264 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
create table Person (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
20:20:22,765 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
alter table Dream
add index FK3F397E3C1409475 (personId),
add constraint FK3F397E3C1409475
foreign key (personId)
references Person (id)




一对多单向关联

还是人与梦想的例子。


我想在代码层次,通过人获得他的所有的梦想,怎么办?


首先在dream类里删除person的引用


package com.bjsxt.hibernate;

@Entity
public class Person {
private int id;
private String name;
private Set<Dream> dreams=new HashSet<>();

@Id
@GeneratedValue
public int getId() {
return id;
}

@OneToMany
@JoinColumn(name="psrsonId")
public Set<Dream> getDreams() {
return dreams;
}
}



OK,大功告成。


建表语句和上面的一样。


装dream的集合类为什么是set?list行不,map行不?


都行。


但是记着,数据库里面的记录是无序且不相等的。


你说用哪个?




不管是oneToMany还是manyToMany,加的 @JoinColumn(name="abc")

都是在多的一方加入指向少的一方的名叫abc的外键。




一对多双向关联(多对一双向关联)

下面的例子,大家猜也猜到了,我既想从dream获得它所属的person,还想从person获得他所有的dream。


我们看代码:


package com.bjsxt.hibernate;

@Entity
public class Dream {
private int id;
private String description;

private Person person;

@Id
@GeneratedValue
public int getId() {
return id;
}

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

@ManyToOne
@JoinColumn(name="personId")
public Person getPerson() {
return person;
}
}

package com.bjsxt.hibernate;

@Entity
public class Person {
private int id;
private String name;
private Set<Dream> dreams=new HashSet<>();

@Id
@GeneratedValue
public int getId() {
return id;
}

@OneToMany(mappedBy="person")
public Set<Dream> getDreams() {
return dreams;
}
}

ok,我们可以在代码里,通过dream获得person也可以通过person获得dream





多对多单向关联

例子很简单,一个老师可以教多个学生,一个学生可以被多个老师教。


单向是说,要么老师能"知道"自己的学生,要么学生能知道自己的老师。


我们先看老师能"知道"自己学生的代码。



package com.bjsxt.hibernate;

@Entity
public class Student {
private int id;
private String name;

@Id
@GeneratedValue
public int getId() {
return id;
}
}
OK,学生里面没有老师的引用。
package com.bjsxt.hibernate;

@Entity
public class Teacher {
private int id;
private String name;
private Set<Student> students = new HashSet<Student>();
@Id
@GeneratedValue
public int getId() {
return id;
}

@ManyToMany
@JoinTable(name="t_s",
joinColumns={@JoinColumn(name="teacher_id")},
inverseJoinColumns={@JoinColumn(name="student_id")}
)
public Set<Student> getStudents() {
return students;
}
}

看结果:


21:10:35,854 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
create table Student (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
21:10:36,192 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
create table Teacher (
id integer not null auto_increment,
name varchar(255),
primary key (id)
)
21:10:36,643 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
create table t_s (
teacher_id integer not null,
student_id integer not null,
primary key (teacher_id, student_id)
)
21:10:36,947 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
alter table t_s
add index FK1BF68BF77BA8A (teacher_id),
add constraint FK1BF68BF77BA8A
foreign key (teacher_id)
references Teacher (id)
21:10:37,588 DEBUG org.hibernate.tool.hbm2ddl.SchemaExport:377 -
alter table t_s
add index FK1BF68AEDC6FEA (student_id),
add constraint FK1BF68AEDC6FEA
foreign key (student_id)
references Student (id)
21:10:38,263 INFO org.hibernate.tool.hbm2ddl.SchemaExport:268 - schema export complete


我们看看生成的t_s表和里面的字段名称应该就能明白 @JoinTable的作用


它就是在manytomany时,指定第三张表的表名和字段。


joinColumns 指的是指向自己这个类的字段名


inverseJoinColumns inverse是反向的意思 所以这个标签就是执行另外那个类的字段


如果没有 @JoinTable这个标签,会是什么样子呢?大家自己试一下。


另外,上面的代码是从老师到学生,大家再试试从学生到老师。




多对多双向关联

在上面的例子里
改变student,给它加上teacher的引用。

package com.bjsxt.hibernate;


@Entity
public class Student {
private int id;
private String name;
private Set<Teacher> teachers=new HashSet<>();

@Id
@GeneratedValue
public int getId() {
return id;
}

//记着 双向的时候 就加上mappedBy
@ManyToMany(mappedBy="students")
public Set<Teacher> getTeachers() {
return teachers;
}

}


glt likes dlf

dlf likes glt

oneToOne