简体   繁体   中英

Why is @ManyToMany not working with non-primary key columns?

I have 2 entities - User and Role which have following relations: User has a manytomany relation to itself and a manytomany relation with the Role entity.

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

...
}

and Role:

public class RoleEntity implements Serializable{

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

    ...
}

The strange thing about the setup of DB is that the User_User relation is based on the binary Id keys

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

and the user-role is based on varchars

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

Now, when it runs, the user-user relationship work well. However, when I try to access the collection returned for roles, I get a 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)

It looks like the UserEntity is being cast to some binary(?) thing. However, the first relation between users themselves works fine, but the one with another table is wrong.

I am using different columns of different types to join tables. Is it allowed to do it this way?

Another strange thing is that when I switch the @Id annotation to be on the login field, the roles work fine, no issue, but then of course the self-join PersistentBag key is the Login instead of Id, which breaks the relation and no results are retrieved. But the conversion from UserEntity to the "[B" is not done.

Also if I leave things as in example and change the Id type to String (and the DB to varchar) it also starts working (of course not consistently with the User_User table).

What am I doing wrong? What is the reason for getting the classcastexception in this case? Why it work when I change the byte[] to String? Please let me know if you have any ideas. I do not want to change the DB design cause this would lead to lots migration and compatibility issues for clients already using the DB.

Just a note: the @Id has to be on the Id binary field as otherwise I wouldn't be able to make a self-join (I was unable to point twice to a column not being a primary key see: Is Hibernate ManyToMany self-join possible for non-key columns? getting mappingException ).

Cheers Adam

the referred column in your join table must be unique entry, here if you put @Id on login field then it works fine,but when you change it to different other than @Id column you cant be sure about the entries will be unique.what you can do is,

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

I think it should work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM