ManyToMany (с дополнительными столбцами) с использованием @ElementCollection и java.утиль.Карта?

я пытаюсь оптимизировать мой @Entity классы. Возьмем общий случай a User <-> Group отношения. Я также хочу сохранить дату, когда было установлено отношение. Расположение таблицы:

 USER      GROUP_USER      GROUP
------    ------------    -------
 id        user_id         id
 name      group_id        name
           createdAt

со следующими 3 классами:

  1. User С @OneToMany List<GroupUser>
  2. GroupUser С @ManyToOne User и @ManyToOne Group
  3. 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"), тогда карта знала, откуда взять ключ.