В каком случае вы используете аннотацию JPA @JoinTable?


Ответы:


373

РЕДАКТИРОВАТЬ 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Таблица называется «Join Table». Чтобы реализовать это второе решение в 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

ОБНОВЛЕНИЕ В моей базе данных нет повторяющихся записей, спящий режим выбирается с помощью левого внешнего соединения, и я не знаю почему ..
MaikoID

2
Я считаю, что @JoinTable/@JoinColumnможно аннотировать в том же поле с mappedBy. Таким образом, правильный пример должен быть сохраняя mappedByин Project, и переместить @JoinColumnк Task.project (или наоборот)
Адриан Shum

2
Ницца! Но у меня есть еще один вопрос: если присоединиться к таблице Project_Tasksнуждается nameв Taskа, которая становится три колонки: project_id, task_id, task_name, как добиться этого?
macemers

5
Я думаю, вам не следовало использовать mappedBy во втором примере использования, чтобы предотвратить эту ошибкуCaused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
karthik m

14

Также проще использовать, @JoinTableкогда Entity может быть дочерним элементом в нескольких родительских / дочерних отношениях с разными типами родителей. Чтобы продолжить пример Бехранга, представьте, что задача может быть дочерним элементом Project, Person, Department, Study и Process.

Должна ли taskтаблица иметь 5 nullableполей внешнего ключа? Думаю, нет...


14

Это единственное решение для сопоставления ассоциации ManyToMany: вам нужна таблица соединений между двумя таблицами сущностей для сопоставления ассоциации.

Он также используется для OneToMany (обычно однонаправленных) ассоциаций, когда вы не хотите добавлять внешний ключ в таблицу со стороны «многие» и, таким образом, сохранять его независимым от одной стороны.

Найдите @JoinTable в документации по спящему режиму, чтобы найти объяснения и примеры.


6

Это позволяет вам управлять отношениями "многие ко многим". Пример:

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

2
Вопрос: что делать, если у меня уже есть эта дополнительная таблица? JoinTable не будет перезаписывать существующий, верно?
TheWandererr

@TheWandererr ты нашел ответ на свой вопрос? У меня уже есть объединенный стол
asgs

В моем случае он создает избыточный столбец в боковой таблице владельца. например, для POST_ID в POST. Вы можете подсказать, почему это происходит?
СПС

3

@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- плохая идея, так как оператор DELETE будет выдан для другой родительской записи, tagв нашем случае, а не для post_tagзаписи. Подробнее об этой теме читайте в этой статье .

Однонаправленные @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

Как уже объяснялось, однонаправленное @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боковые. Прочтите эту статью, чтобы узнать больше об этой теме.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.