JPA“@JoinTable”注释

时间:2011-03-29 20:10:36

标签: java hibernate jpa

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

5 个答案:

答案 0 :(得分:303)

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

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

答案 1 :(得分:13)

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

当你不想在多方面的表中添加外键时,它也用于OneToMany(通常是单向)关联,因此保持它与一方无关。

hibernate documentation中搜索@JoinTable以获取解释和示例。

答案 2 :(得分:13)

当一个实体可能是具有不同类型的父母的几个父/子关系中的孩子时,使用@JoinTable也更清晰。为了跟进Behrang的例子,想象一个任务可以是项目,人员,部门,研究和过程的孩子。

task表是否有5个nullable个外键字段?我想不是......

答案 3 :(得分:4)

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

Table 1: post

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

Table 2: user

user has the following columns:

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

Join Table允许您使用以下方法创建映射:

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

将创建一个表:

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

答案 4 :(得分:3)

@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)来指定表名。 )和post_tag链接表中的外键列,该列通过Tag属性引用了inverseJoinColumns实体。

请注意,@ManyToMany批注的级联属性设置为PERSISTMERGE只是因为级联REMOVE是一个坏主意,因为我们将发出DELETE语句对于另一个父记录,在我们的情况下为tag,而不是post_tag记录。有关此主题的更多详细信息,请查看this article

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

Unidirectional <code>@OneToMany</code> 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_id表,外键列将为post,对于post_comment_id,外键列将为post_comment@OneToMany表。

单向@OneToMany关联效率不高,因此最好使用双向@ManyToOne关联或仅使用std::queue<Event*> events; // ... while(hasUnhandledEvents()) { Event e = pollEvent(); // ... handle event } 端。请查看this article,以获取有关此主题的更多详细信息。