tags:

views:

129

answers:

1

We are new to JPA and trying to setup a very simple one to many relationship where a pojo called Message can have a list of integer group id's defined by a join table called GROUP_ASSOC. Here is the DDL:

CREATE TABLE "APP"."MESSAGE" (
        "MESSAGE_ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1)
    );

ALTER TABLE "APP"."MESSAGE" ADD CONSTRAINT "MESSAGE_PK" PRIMARY KEY ("MESSAGE_ID");

CREATE TABLE "APP"."GROUP_ASSOC" (
        "GROUP_ID" INTEGER NOT NULL,
        "MESSAGE_ID" INTEGER NOT NULL
    );

ALTER TABLE "APP"."GROUP_ASSOC" ADD CONSTRAINT "GROUP_ASSOC_PK" PRIMARY KEY ("MESSAGE_ID", "GROUP_ID");

ALTER TABLE "APP"."GROUP_ASSOC" ADD CONSTRAINT "GROUP_ASSOC_FK" FOREIGN KEY ("MESSAGE_ID")
    REFERENCES "APP"."MESSAGE" ("MESSAGE_ID");

Here is the pojo:

@Entity
@Table(name = "MESSAGE")
public class Message {
    @Id
    @Column(name = "MESSAGE_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int messageId;

    @OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
    private List groupIds;

    public int getMessageId() {
        return messageId;
    }
    public void setMessageId(int messageId) {
        this.messageId = messageId;
    }
    public List getGroupIds() {
        return groupIds;
    }
    public void setGroupIds(List groupIds) {
        this.groupIds = groupIds;
    }
}

I know this is wrong as there is no @Column mapping to GROUP_ASSOC.GROUP_ID for the groupIds property, but hopefully this illustrates what we are trying to do. When we run the following test code we get <openjpa-1.2.3-SNAPSHOT-r422266:907835 fatal user error> org.apache.openjpa.util.MetaDataException: The type of field "pojo.Message.groupIds" isn't supported by declared persistence strategy "OneToMany". Please choose a different strategy.

Message msg = new Message();
List groups = new ArrayList();
groups.add(101);
groups.add(102);
EntityManager em = Persistence.createEntityManagerFactory("TestDBWeb").createEntityManager();
em.getTransaction().begin();
em.persist(msg);
em.getTransaction().commit();

Help!

+1  A: 

When you are working with JPA, you should think Object and relations between Objects and you should map your Object model, not ids, to your relational model (it is possible to map a List of basic values with @ElementCollection in JPA 2.0 though but what I said just before still applies).

Here, (assuming this really is a one-to-many relation between Message and GroupAssoc and not a many-to-many relation between Message and Group entities) you should have something like this:

@Entity
@Table(name = "MESSAGE")
public class Message implements Serializable {
    @Id
    @Column(name = "MESSAGE_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)    
    private Long messageId;

    @OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)    
    private List<GroupAssoc> groupAssocs = new ArrayList<GroupAssoc>();

    public Long getMessageId() {
        return messageId;
    }
    public void setMessageId(Long messageId) {
        this.messageId = messageId;
    }

    public List<GroupAssoc> getGroupAssocs() {
        return groupAssocs;
    }
    public void setGroupAssocs(List<GroupAssoc> groupAssocs) {
        this.groupAssocs = groupAssocs;
    }

    // equals() and hashCode()
}

And another entity for GroupAssoc.

PS: Your DDL really looks like a (M:N) relation between MESSAGE and GROUP (or I don't understand the PK constraint of GROUP_ASSOC) but you didn't show any FK constraint on GROUP_ID so I'm not 100% sure. But if that's the case, then you should use an @ManyToMany instead of @OneToMany.

Pascal Thivent
Thank you very much for your help. I should have mentioned that our application needs to display groupIds (in the UI) and that list of groupId's is what we ultimately need to query for by message. I think it is an M:N relationship because two massages may share the same groupIds.
Robert
My DDL is my best guess as to how to efficiently model this list of integers so there may be a problem with it (I'm a UI developer not a DBA). It seems cumbersome to create a separate `GroupAssoc` pojo especially if we have to create a third identity class (since it has a composite primary key) just to query for a list of groupIds for a given message.
Robert
@Robert You wouldn't have to create a third entity, JPA abstract the implementation at the database level. So you would get a List of `GroupAssoc` and iterate on the list to get each id.
Pascal Thivent
So the `GroupAssoc` pojo doesn't need to have `@Id` or `@IdClass`?
Robert
@Robert Not easy to discuss in a 600 char box :) `GroupAssoc` does need to have a property annotated with `@Id`, I didn't say it doesn't. What I said is that you don't need a third entity for the "join table". JPA can map a `Message-*--*-Group` object model to the representation `Message-0--n-MessageGroup-m--0-Group` in database without the need for an entity for `MessageGroup`. I hope it's more clear.
Pascal Thivent
I really appreciate your help.. I agree that this is not an easy discussion via comments, so I created a follow-up post where I've tried your suggestions as far as I understand: http://stackoverflow.com/questions/2501383/persisting-a-list-of-integers-with-jpa
Robert