在哪种情况下,您使用JPA @JoinTable批注?


Answers:


350

编辑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图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图2

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


1
使用第一种方法,在合并和工作之前,我的项目充满了我的项目,每个任务充满了父项目,但是根据我的任务数量,我的所有条目都是重复的。一个有两个任务的项目在我的数据库中保存了两次。为什么呢
MaikoID

UPDATE有我的数据库中不重复的条目,休眠与左外连接选择,我不知道为什么..
MaikoID

2
我相信@JoinTable/@JoinColumn可以在与相同的字段中进行注释mappedBy。因此正确的例子应该是保留mappedByin Project,然后将其@JoinColumn移至Task.project (反之亦然)
Adrian Shum 2013年

2
真好!但我有一个进一步的问题:如果连接表Project_Tasks需要nameTask同时,成为三列:project_idtask_idtask_name,如何实现这一目标?
macemers

5
我认为您不应该在第二个用法示例中使用Caused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
mapbyBy

14

@JoinTable当实体可以是与不同类型的父级处于多个父级/子级关系中的子级时,使用实体也更清洁。为了跟上Behrang的例子,假设一个Task可以是Project,Person,Department,Study和Process的子级。

task表应该有5个nullable外键字段吗?我觉得不是...


14

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

当您不想在多边表中添加外键并因此使其独立于一侧时,它也用于OneToMany(通常是单向)关联。

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


4

它使您可以处理多对多关系。例:

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 |
|_________|_________|
|         |         |
|_________|_________|

1
问题:如果我已经有了这张额外的桌子怎么办?JoinTable不会覆盖存在的权利吗?
TheWandererr

@TheWandererr您是否找到问题的答案?我已经有一个
联接

就我而言,它在拥有的边表中创建了一个冗余列。例如 POST中的POST_ID。你能说明为什么会这样吗?
SPS

0

@ManyToMany 协会

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

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

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

多对多表关系

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)和外键列post_tag链接表引用Tag通过实体inverseJoinColumns属性。

请注意,@ManyToMany注释的级联属性设置为PERSIST,这MERGE仅是因为级联REMOVE是一个不好的主意,因为我们将为其他父记录(tag在我们的情况下,不是对post_tag记录)发出DELETE语句。有关此主题的更多详细信息,请参阅本文

单向 @OneToMany关联

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

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

@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将为上述实体映射采用以下数据库架构:

单向<code> @OneToMany </ code> JPA关联数据库表

如前所述,单向@OneToManyJPA映射的行为就像多对多关联。

要自定义链接表,您还可以使用@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_id,为post表,和post_comment_id,为post_comment桌。

单向@OneToMany关联效率不高,因此最好使用双向@OneToMany关联或仅使用@ManyToOne侧面关联。请查看本文以获取有关此主题的更多详细信息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.