Почему @ManyToMany не работает со столбцами, не являющимися первичными ключами?

У меня есть 2 объекта - Пользователь и Роль, которые имеют следующие отношения: Пользователь имеет многоточечное отношение к себе и многоточечное отношение с сущностью Роль.

@Entity
public class UserEntity implements Serializable {

    @Id
    @Column(length = 12, columnDefinition = "BINARY(12)", name = "Id", unique = true)
    private byte[] id;

    @Column(name = "Login", unique = true, nullable = false)
    private String login;

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(name = "User_Role",
    joinColumns = { @JoinColumn(name = "UserLogin", referencedColumnName = "Login") },
    inverseJoinColumns = { @JoinColumn(name = "RoleId", referencedColumnName = "Id") })
    private Set<RoleEntity> roles;

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "User_User",
    joinColumns = { @JoinColumn(name = "UserParent") },
    inverseJoinColumns = { @JoinColumn(name = "UserChild") })
    private Collection<UserEntity> children;

...
}

и роль:

public class RoleEntity implements Serializable{

    @Id
    @Column(name = "Id", unique = true, nullable = false)
    private String id;

    ...
}

Странная вещь в настройке БД заключается в том, что отношение User_User основано на двоичных ключах Id.

create table if not exists User_User (
    UserParent binary,
    UserChild binary
);

а роль пользователя основана на varchars

create table if not exists KNUser_UserRole (
    UserLogin varchar, 
    RoleId varchar,
);

Теперь, когда он работает, отношения между пользователями работают хорошо. Однако, когда я пытаюсь получить доступ к коллекции, возвращаемой для ролей, я получаю ClassCastException:

java.lang.ClassCastException: **.entity.UserEntity cannot be cast to [B
    at org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor.extractHashCode(PrimitiveByteArrayTypeDescriptor.java:41)
    at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:201)
    at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:205)
    at org.hibernate.engine.spi.EntityKey.generateHashCode(EntityKey.java:114)
    at org.hibernate.engine.spi.EntityKey.<init>(EntityKey.java:79)
    at org.hibernate.internal.AbstractSessionImpl.generateEntityKey(AbstractSessionImpl.java:240)
    at org.hibernate.engine.internal.StatefulPersistenceContext.getCollectionOwner(StatefulPersistenceContext.java:740)
    at org.hibernate.loader.Loader.readCollectionElement(Loader.java:1181)
    at org.hibernate.loader.Loader.readCollectionElements(Loader.java:800)
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:651)
    at org.hibernate.loader.Loader.doQuery(Loader.java:856)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:289)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
    at org.hibernate.loader.Loader.loadCollection(Loader.java:2175)
    at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:61)
    at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:622)
    at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:82)
    at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:1606)
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:379)
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:112)
    at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:180)

Похоже, что UserEntity преобразуется в какую-то двоичную (?) вещь. Однако первое отношение между самими пользователями работает нормально, а отношение с другой таблицей — неправильно.

Я использую разные столбцы разных типов для объединения таблиц. Разрешено ли это делать таким образом?

Еще одна странность заключается в том, что когда я переключаю аннотацию @Id на поле login, роли работают нормально, без проблем, но затем, конечно, PersistentBag самоподключается key — это логин, а не идентификатор, что разрывает связь, и результаты не извлекаются. Но преобразование из UserEntity в "[B" не выполняется.

Кроме того, если я оставлю все как в примере и изменю тип Id на String (и БД на varchar), он также начнет работать (конечно, не согласуется с таблицей User_User).

Что я делаю неправильно? В чем причина получения исключения classcastException в этом случае? Почему это работает, когда я меняю byte[] на String? Пожалуйста, дайте мне знать, если у вас есть идеи. Я не хочу менять дизайн БД, потому что это приведет к большому количеству проблем с миграцией и совместимостью для клиентов, уже использующих БД.

Просто примечание: @Id должен быть в двоичном поле Id, иначе я не смогу выполнить самосоединение (я не смог дважды указать столбец, не являющийся первичным ключом, см.: Возможно ли самосоединение Hibernate ManyToMany для неключевых столбцов ? получение MappingException).

Привет Адам


person Adam Soliński    schedule 05.09.2014    source источник


Ответы (1)


указанный столбец в вашей таблице соединений должен быть уникальной записью, здесь, если вы поместите @Id в поле входа в систему, тогда он будет работать нормально, но когда вы измените его на другой, отличный от столбца @Id, вы не можете быть уверены, что записи будут уникальными. что вы можете сделать это,

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "User_Role",
joinColumns = { @JoinColumn(name = "UserLogin", referencedColumnName = "Id") },
inverseJoinColumns = { @JoinColumn(name = "RoleId", referencedColumnName = "Id") })
private Set<RoleEntity> roles; 

Я думаю, это должно сработать.

person Prateek Singh    schedule 05.09.2014
comment
нет, как я уже писал, UserLogin referencedColumnName должен быть Login - Id является двоичным, а Login - varchar. Для этого отношения мне нужен varchar. Я знаю, что это плохой дизайн, но я ищу решение этой проблемы. - person Adam Soliński; 06.09.2014