3

I have the following existing DB schema, which I'd like to recreate with Java and plain JPA annotations (using hibernate as provider, so hibernate specific annotations would work as a last resort):

CREATE TABLE users (
    user_id NUMBER NOT NULL                     -- pk
);

CREATE TABLE userdata_keys (
    userdata_key_id NUMBER NOT NULL,            -- pk
    key VARCHAR2(128) NOT NULL                  
);

CREATE TABLE users_userdata (
    user_id NUMBER NOT NULL,                    -- fk users.user_id
    userdata_key_id NUMBER NOT NULL,            -- fk userdata_keys.userdata_key_id 
    value VARCHAR2(256)                         
);

I've thus created the following classes and annotations:

class User {
    @Id
    Long id;
    @OneToMany
    Set<Userdata> userdata;
}

class UserdataKey {
    @Id
    Long id;
    String key;
}

class Userdata {
    String value;
    @EmbeddedId
    UserdataId userdataId;
}

@Embeddable
class UserdataId {
    User user;
    UserdataKey userdataKey;
}

I left out columnName attributes and other attributes of the entities here.
It does however not quite work as intended. If I do not specify a mappedBy attribute for User.userdata, hibernate will automatically create a table USERS_USERS_USERDATA, but as far as I've seen does not use it. It does however use the table which I specified for the Userdata class.

Since I'm rather new to Java and hibernate as well, all I do to test this currently is looking at the DB schema hibernate creates when persisting a few sample entries.

As a result, I'm entirely puzzled as to whether I'm doing this the right way at all. I read the hibernate documentation and quite a bunch of Google results, but none of them seemed to deal with what I want to do (composite key with "subclasses" with their own primary key).

Darrell Teague
  • 4,132
  • 1
  • 26
  • 38
user967058
  • 475
  • 6
  • 12
  • having a similar problem: http://stackoverflow.com/questions/31600142/how-to-define-onetomany-in-parent-entity-when-child-has-composite-pk – amphibient Jul 24 '15 at 17:03

1 Answers1

3

The mappedBy attribute is mandatory at one of the sides of every bidirectional association. When the association is a one-to-many, the mappedBy attribute is placed ot the one- side (i.e. on the User's userdata field in your case).

That's because when an association is bidirectional, one side of the association is always the inverse of the other, so there's no need to tell twice to Hibernate how the association is mapped (i.e. which join column or join table to use).

If you're ready to recreate the schema, I would do it right (and easier), and use a surrogate auto-generated key in users_userdata rather than a composite one. This will be much easier to handle, in all the layers of your application.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • I think I'll take your suggestion and add a surrogate primary key. I guess the automatically generated table is due the "other side" of the association? Shouldn't hibernate at least warn about this? What would I use for the `mappedBy` attribute? Using `userdataId.user` seems to work, but I'd expected `userdataId` to be correct (which yields an error about primary keys with different number of colums). – user967058 Sep 27 '11 at 13:29
  • Yes, the generated table is due to the other side of the association having no mappedBy attribute. No, it shouldn't warn: You might have two distinct unidirectional associations, one in one direction and the other in the other direction (for example: a person having many children, and a child having one teacher: two different relationships, so two different mappings). – JB Nizet Sep 27 '11 at 13:34
  • It appears to be working with the surrogate primary key now. Thanks a lot! – user967058 Sep 27 '11 at 14:09