ManyToMany (с дополнительными столбцами) с использованием @ElementCollection и java.утиль.Карта?
я пытаюсь оптимизировать мой @Entity
классы. Возьмем общий случай a User <-> Group
отношения. Я также хочу сохранить дату, когда было установлено отношение. Расположение таблицы:
USER GROUP_USER GROUP
------ ------------ -------
id user_id id
name group_id name
createdAt
со следующими 3 классами:
-
User
С@OneToMany List<GroupUser>
-
GroupUser
С@ManyToOne User
и@ManyToOne Group
-
Group
С@OneToMany List<GroupUser>
это работает, но это отстой для общих случаев использования. Например: Дайте мне все группы для User user
:
List<Group> groups;
for(GroupUser gu : user.getGroupUsers) {
groups.add(gu.getGroup());
}
тогда идея использования Map
пришло мне на ум. Поэтому я создал следующие 3 класса:
@Entity
class User {
@Column(name="id")
UUID id;
@Column(name="name")
String name;
@ElementCollection
@CollectionTable(name="Group_User")
Map<Group, GroupUserRelationData> groups;
}
@Entity
class Group {
@Column(name="id")
UUID id;
@Column(name="name")
String name;
@ElementCollection
@CollectionTable(name="Group_User")
Map<User, GroupUserRelationData> users;
}
@Embeddable
class GroupUserRelationData {
@Column(name="createdAt")
DateTime createdAt;
}
это тут создайте 3 таблицы, как ожидалось, но столбцы внутри Group_User
таблицы странные:
USER GROUP_USER GROUP
------ ------------ -------
id User_id id
name users_KEY name
Group_id
group_KEY
createdAt
я уверен, что пропустил некоторые вещи... Похоже, мне нужно указать JoinColumn. Но каков правильный путь?
что разница между @CollectionTable(joinColumns=...)
и @MapKeyColumn
и @MapKeyJoinColumn
. Я должен все это установить?
или я должен использовать @ManyToMany
вместо @ElementCollection
?
я просто хочу получить тот же макет таблицы, что и вверху, используя java.util.Map
.
редактировать #1: кроме того, база данных должна иметь правильные ограничения для таблицы:
- Составной Первичный Ключ (Group_id, User_id)
At в тот момент, когда он создает составной PK (Group_id, users_KEY), который кажется мне довольно странным. И он также создает 4(!!) индексов, по одному для каждого столбца User_id
, Group_id
, users_KEY
и groups_KEY
.
EDIT #2: я нашел веб-сайт, который сообщает, когда использовать какие аннотации:http://kptek.wordpress.com/2012/06/26/collection-mapping/
плохо то, что я до сих пор не знаю почему...
устранение:
после недолгих попыток я придумал следующий код, который делает именно то, что я хочу. Я должен использовать все эти аннотации, иначе он создает дополнительные столбцы:
@Entity
class User {
@Column(name="id")
UUID id;
@Column(name="name")
String name;
@ElementCollection
@CollectionTable(name="Group_User",
joinColumns=@JoinColumn(name="User_Id"))
@MapKeyJoinColumn(name="Group_Id")
Map<Group, GroupUserRelationData> groups;
}
@Entity
class Group {
@Column(name="id")
UUID id;
@Column(name="name")
String name;
@ElementCollection
@CollectionTable(name="Group_User",
joinColumns=@JoinColumn(name="Group_Id"))
@MapKeyJoinColumn(name="User_Id")
Map<User, GroupUserRelationData> users;
}
@Embeddable
class GroupUserRelationData {
@Column(name="createdAt")
DateTime createdAt;
}
я все равно хотел бы увидеть объяснение, что именно делают эти аннотации и почему поведение по умолчанию создало четыре столбца и странный первичный ключ.
1 ответов
карта требовала как ключа один ко многим, так и ключа карты.
в вашем первом примере Hibernate выясняет, что ему нужен FK от Group.id
матч GroupUser.group_id
для загрузки связанных пользователей. Но поскольку вы не указываете ключ карты, он должен выяснить где его взять?
Итак, Hibernate предположил, что вы хотите @MapKeyColumn
и поскольку имя столбца не было указано, используется имя свойства, за которым следует символ подчеркивания и ключ (для пример users_KEY).
вот почему у вас были эти две дополнительные колонки. Когда вы поставили @MapKeyJoinColumn(name="User_Id")
, тогда карта знала, откуда взять ключ.