在哪种情况下使用 JPA@JoinTable 注释?

在哪种情况下使用 JPA@JoinTable注释?

246227 次浏览

这是映射 ManyTomany 关联的唯一解决方案: 您需要两个实体表之间的连接表来映射关联。

它还用于 OneTomany (通常是单向的)关联,当您不想在多边的表中添加外键,从而使其独立于一边时。

休眠文档中搜索@JoinTable 以获得解释和示例。

EDIT 2017-04-29 : 正如一些评论者所指出的,JoinTable示例不需要 mappedBy注释属性。事实上,最新版本的 Hibernate 拒绝通过输出以下错误来启动:

org.hibernate.AnnotationException:
Associations marked as mappedBy must not define database mappings
like @JoinTable or @JoinColumn

假设您有一个名为 Project的实体和另一个名为 Task的实体,每个项目可以有许多任务。

可以通过两种方式设计此方案的数据库架构。

第一种解决方案是创建一个名为 Project的表和另一个名为 Task的表,并向任务表 project_id添加一个外键列:

Project      Task
-------      ----
id           id
name         name
project_id

这样,就可以确定任务表中每一行的项目。如果使用这种方法,在实体类中就不需要连接表:

@Entity
public class Project {


@OneToMany(mappedBy = "project")
private Collection<Task> tasks;


}


@Entity
public class Task {


@ManyToOne
private Project project;


}

另一种解决方案是使用第三个表,例如 Project_Tasks,并在该表中存储项目和任务之间的关系:

Project      Task      Project_Tasks
-------      ----      -------------
id           id        project_id
name         name      task_id

Project_Tasks表称为“联接表”。要在 JPA 中实现第二个解决方案,需要使用 @JoinTable注释。例如,为了实现单向的一对多关联,我们可以这样定义我们的实体:

Project实体:

@Entity
public class Project {


@Id
@GeneratedValue
private Long pid;


private String name;


@JoinTable
@OneToMany
private List<Task> tasks;


public Long getPid() {
return pid;
}


public void setPid(Long pid) {
this.pid = pid;
}


public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


public List<Task> getTasks() {
return tasks;
}


public void setTasks(List<Task> tasks) {
this.tasks = tasks;
}
}

Task实体:

@Entity
public class Task {


@Id
@GeneratedValue
private Long tid;


private String name;


public Long getTid() {
return tid;
}


public void setTid(Long tid) {
this.tid = tid;
}


public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


}

这将创建以下数据库结构:

ER Diagram 1

@JoinTable注释还允许您自定义联接表的各个方面。例如,如果我们像下面这样注释 tasks属性:

@JoinTable(
name = "MY_JT",
joinColumns = @JoinColumn(
name = "PROJ_ID",
referencedColumnName = "PID"
),
inverseJoinColumns = @JoinColumn(
name = "TASK_ID",
referencedColumnName = "TID"
)
)
@OneToMany
private List<Task> tasks;

由此产生的数据库将成为:

ER Diagram 2

最后,如果希望为多对多关联创建模式,使用联接表是唯一可用的解决方案。

如果一个实体可以是与不同类型父母的多个父母/子女关系中的子女,那么使用 @JoinTable也会更干净。为了跟进 Behrang 的示例,假设一个任务可以是项目、人员、部门、研究和过程的子任务。

task表应该有5个 nullable外键字段吗? 我认为不应该..。

它可以让你处理多对多的关系。例如:

Table 1: post


post has following columns
____________________
|  ID     |  DATE   |
|_________|_________|
|         |         |
|_________|_________|


Table 2: user


user has the following columns:


____________________
|     ID  |NAME     |
|_________|_________|
|         |         |
|_________|_________|

联接表允许您使用以下方法创建映射:

@JoinTable(
name="USER_POST",
joinColumns=@JoinColumn(name="USER_ID", referencedColumnName="ID"),
inverseJoinColumns=@JoinColumn(name="POST_ID", referencedColumnName="ID"))

将创建一个表:

____________________
|  USER_ID| POST_ID |
|_________|_________|
|         |         |
|_________|_________|

@ManyToMany协会

通常,您需要使用 @JoinTable注释来指定多对多表关系的映射:

  • 链接表的名称和
  • 两个外键列

因此,假设您有以下数据库表:

Many-to-many table relationship

Post实体中,映射这种关系,如下所示:

@ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(
name = "post_tag",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();

@JoinTable注释用于通过 name属性指定表名,以及引用 post表(例如,joinColumns)的 Foreign Key 列和通过 inverseJoinColumns属性引用 Tag实体的 post_tag链接表中的 Foreign Key 列。

请注意,@ManyToMany注释的级联属性被设置为 PERSISTMERGE,这仅仅是因为级联 REMOVE是一个坏主意,因为 DELETE 语句将针对另一个父记录(在我们的例子中是 tag)发出,而不是针对 post_tag记录。

单向 @OneToMany协会

缺乏 @JoinColumn映射的单向 @OneToMany关联的行为类似于多对多表关系,而不是一对多关系。

因此,假设您有以下实体映射:

@Entity(name = "Post")
@Table(name = "post")
public class Post {
 

@Id
@GeneratedValue
private Long id;
 

private String title;
 

@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
 

//Constructors, getters and setters removed for brevity
}
 

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
 

@Id
@GeneratedValue
private Long id;
 

private String review;
 

//Constructors, getters and setters removed for brevity
}

Hibernate 将为上面的实体映射采用以下数据库模式:

Unidirectional @OneToMany JPA association database tables

如前所述,单向 @OneToMany JPA 映射的行为类似于多对多关联。

要自定义链接表,还可以使用 @JoinTable注释:

@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
@JoinTable(
name = "post_comment_ref",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "post_comment_id")
)
private List<PostComment> comments = new ArrayList<>();

现在,链接表将被称为 post_comment_ref,对于 post表,外键列为 post_id,对于 post_comment表,外键列为 post_comment_id

单向 @OneToMany关联效率不高,因此最好使用双向 @OneToMany关联或仅使用 @ManyToOne端。