ChatGPT解决这个技术问题 Extra ChatGPT

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

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


B
Behrang

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;
    }

}

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

https://i.stack.imgur.com/TDcoI.png

@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;

生成的数据库将变为:

https://i.stack.imgur.com/WhC82.png

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


使用第一种方法,我的项目填充了我的任务,并且每个任务在合并之前都填充了父项目并且可以工作,但是我的所有条目都是根据我的任务数量重复的。具有两个任务的项目在我的数据库中保存了两次。为什么 ?
更新我的数据库中没有重复条目,休眠选择左外连接,我不知道为什么..
我相信 @JoinTable/@JoinColumn 可以在与 mappedBy 相同的字段上进行注释。因此,正确的示例应该是将 mappedBy 保留在 Project 中,并将 @JoinColumn 移动到 Task.project(反之亦然)
好的!但是我还有一个问题:如果连接表Project_Tasks也需要Taskname,变成三列:project_idtask_idtask_name,如何实现呢?
我认为您不应该在第二个用法示例中使用 mappedBy 来防止此错误Caused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
R
RonU

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

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

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


嗨,这不是多对多关联的唯一解决方案。您可以创建具有两个双向 @OneToMany 关联的 join entity
V
Vlad Mihalcea

@ManyToMany 关联

大多数情况下,您需要使用 @JoinTable 注释来指定多对多表关系的映射:

链接表的名称和

两个外键列

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

https://i.stack.imgur.com/PsEDG.png

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

请注意,@ManyToMany 注释的级联属性设置为 PERSIST 和 MERGE 只是因为级联 REMOVE 是一个坏主意,因为我们将为另一个父记录发出 DELETE 语句,在我们的例子中是标记,而不是 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 将为上述实体映射假定以下数据库模式:

https://i.stack.imgur.com/u52Ur.png

如前所述,单向 @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 端。


嗨@Vlad,如果连接表没有额外的列,使用 @JoinTable 而不是连接实体会更好吗? @JoinTable 与连接实体相比有什么优势? (或相反亦然)
查看 thisthis 文章以获得您问题的详细答案。
我以前读过这些文章;他们都是伟大的。但我的错,我错过了 second article结论 部分。那部分是我的答案。谢谢@Vlad。
如有疑问,请前往 Vlad Mihalcea Dot Com。这就是答案所在。
R
RonU

当一个实体可能是与不同类型的父级的多个父/子关系中的子级时,使用 @JoinTable 也更简洁。继续 Behrang 的示例,假设 Task 可以是 Project、Person、Department、Study 和 Process 的子项。

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


s
slal

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

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

问题:如果我已经有这个附加表怎么办? JoinTable 不会覆盖existign 吗?
@TheWandererr 您找到问题的答案了吗?我已经有一个连接表
就我而言,它在拥有的边表中创建了一个冗余列。例如。 POST 中的 POST_ID。你能建议为什么会这样吗?