Question

Is it possible in plain JPA or JPA+Hibernate extensions to declare a composite key, where an element of the composite key is a sequence?

This is my composite class:

@Embeddable
public class IntegrationEJBPk implements Serializable {

    //...


    @ManyToOne(cascade = {}, fetch = FetchType.EAGER)
    @JoinColumn(name = "APPLICATION")
    public ApplicationEJB getApplication() {
        return application;
    }


    @Column(name = "ENTITY", unique = false, nullable = false, insertable = true, updatable = true)
    public String getEntity() {
        return entity;
    }

    @GeneratedValue(strategy = GenerationType.AUTO, generator = "INTEGRATION_ID_GEN")
    @SequenceGenerator(name = "INTEGRATION_ID_GEN", sequenceName = "OMP_INTEGRATION_CANONICAL_SEQ")
    @Column(name = "CANONICAL_ID", unique = false, nullable = false, insertable = true, updatable = true)
    public String getCanonicalId() {
        return canonicalId;
    }

    @Column(name = "NATIVE_ID", unique = false, nullable = false, insertable = true, updatable = true)
    public String getNativeId() {
        return nativeId;
    }

    @Column(name = "NATIVE_KEY", unique = false, nullable = false, insertable = true, updatable = true)
    public String getNativeKey() {
        return nativeKey;
    }

    //...
}

I already supply the values for application, entity, nativeId and nativeKey. I want to construct an entity like the one below:

IntegrationEJB i1 = new IntegrationEJB();
i1.setIntegrationId(new IntegrationEJBPk());
i1.getIntegrationId().setApplication(app1);
i1.getIntegrationId().setEntity("Entity");
i1.getIntegrationId().setNativeId("Nid");
i1.getIntegrationId().setNativeKey("NK");

And when I call em.persist(i1), I want that the canonicalId is generated and the integration is inserted.

Is this possible? If so, what's the simple way? (I prefer not to use application-provided keys or native sql).

Was it helpful?

Solution

I believe that this is not possible with plain JPA.

OTHER TIPS

Using @GeneratedValue for composite PKs is not specified withi JPA 2 spec.

From the JPA spec:

11.1.17 GeneratedValue Annotation

The GeneratedValue annotation provides for the specification of generation strategies for the values of primary keys. The GeneratedValue annotation may be applied to a primary key property or field of an entity or mapped superclass in conjunction with the Id annotation.[97] The use of the GeneratedValue annotation is only required to be supported for simple primary keys. Use of the GeneratedValue annotation is not supported for derived primary keys.

Note that under a different scenario, you can have a parent entity with a simple PK (@Id) that uses @GeneratedValue, and then have a child entity that has a composite PK that includes the generated Id from the parent, plus another column.

  • Give the child a @ManyToOne relationship to the parent entity, so that it inherits the generated ID in a FK column.
  • Then give the child a composite PK, via @IdClass specified against the entity, plus two @Id columns within the entity.
@Entity
public class ParentEntity {
       @Id
       @GenerateValue(IDENTITY) // If using DB auto-increment or similar
       int id;

       // ...
}
@Entity
@IdClass(ChildId.class)
public class ChildEntity {
   // The child table will have a composite PK:
   // (parent_ID, child_name)
       @Id 
       @ManyToOne
       int parentEntity;
       @Id
       String childName;

       String favoriteColor;  // plus other columns

       // ...

}
// child Id attributes have the same name as the child entity
// however the types change to match the underlying PK attributes 
// (change ParentEntity to int)
 public class ChildId implements Serializable {
        int parentEntity;
        String childName;

        public ChildId() { //... }

        // Add extra constructor & remove setter methods so Id objects are immutable
        public ChildId(int parentEntity, String childName) { //... }


        public int getParentEntity() { //... }
        // make Id objects immutable:
        //  public void setParentEntity(int parentEntity) { //... }
        public String getChildName() { //... }
        //  public void setChildName(String childName) { //... }
}

Try like this:

@TableGenerator(name = "canonicalKeys", allocationSize = 1, initialValue = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "canonicalKeys")
@Column(name = "CANONICAL_ID", unique = false, nullable = false, insertable = true, updatable = true)
public String getCanonicalId() {
    return canonicalId;
}

In this way instead of using a sequence you can use a table.

I notice that it appears your building a composite primary key like this example. While I was poking at a similar problem in my own database I wondered if maybe you could call the sql directly like:

select nextval ('hibernate_sequence')

I suppose that would be cheating though ;-)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top