Sub table with Composite key is not linkable from parent

I have a master table with a prodKey string as ID. I have a sub table which has 2 key fields, prodKey and lineNo. I created a Composite Key of these 2 fields (embedded entity) and then created the sub table with this Composite Key. However, I cannot set a Composition field on the Master table to this sub-table records. It does not show as an option. How I do I link these 2 tables ?

Do I need to use UUIDs in every table which I need to link up ?

CK

Hi,
If I have correctly understood, you want to link the entities thought the prodKey property which is a part of Composite PK. It is possible but coding is required (is not supported out of the box). How to create such associations was described several times on this forum. For instance in this topic.

I have tried the example. After I have made the changes, I can see the sub table as an option to create a Composition in the master table. But when I do this and run the project, the system failed to start and complained about an Eclipse Persistence Exception.

Hi,
I have prepared a little sample project to show how the entities might be linked through fields that are parts of Composite PK.
Note that this way is a bit hacky and is not supported by Studio.

In my sample:

  1. OrderLine entity has two links: to Order and Product. They are presented by datatypes and assembled to Composite PK:
@MetaClass(name = "sample_KeyEntity")
@Embeddable
public class KeyEntity extends EmbeddableEntity {
    private static final long serialVersionUID = -3160428472596560086L;

    @Column(name = "ORDER_LINK")
    private UUID orderLink;

    @Column(name = "PRODUCT_CODE", length = 10)
    private String productCode;
  1. And @Transient @MetaProperty present the associations in OrderLine.
@Table(name = "SAMPLE_ORDER_LINE")
@Entity(name = "sample_OrderLine")
@NamePattern("%s %s|order,product")
public class OrderLine extends BaseGenericIdEntity<KeyEntity> {
    private static final long serialVersionUID = -3872032066065715379L;

    @EmbeddedId
    private KeyEntity id;

    @MetaProperty
    @Transient
    private Order order;

    @Transient
    @MetaProperty
    private Product product;

    @Column(name = "QUANTITY")
    private Integer quantity;


    public Integer getQuantity() {
        return quantity;
    }

    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
    }

    public Product getProduct() {
        if (this.product == null && id.getProductCode() != null) {
            DataManager dm = AppBeans.get(DataManager.class);
            product = dm.load(LoadContext.create(Product.class).setId(id.getProductCode()));
        }
        return product;
    }

    public void setProduct(Product product) {
        this.product = product;
        if (product != null) {id.setProductCode(product.getId());}
    }

    public Order getOrder() {
        if (this.order == null && id.getOrderLink() != null) {
            DataManager dm = AppBeans.get(DataManager.class);
            order = dm.load(LoadContext.create(Order.class).setId(id.getOrderLink()));
        }
        return order;
    }

    public void setOrder(Order order) {
        this.order = order;
        if (order != null) {id.setOrderLink(order.getId());}
    }
...
  1. In Order: one_to_many Collection with the @JoinColumn annotation instead of “mappedBy”.
@Composition
    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
    @JoinColumn(name = "ORDER_LINK", referencedColumnName = "ID")
    private List<OrderLine> lines;
  1. As there was no “mappedBy” defined, we should manually fill the link to Order, when creating a new OrderLine.
    This could be done in the controller of the OrderEdit screen
@UiController("sample_Order.edit")
@UiDescriptor("order-edit.xml")
@EditedEntityContainer("orderDc")
@LoadDataBeforeShow
public class OrderEdit extends StandardEditor<Order> {

    @Inject
    private Metadata metadata;

   
    @Install(to = "linesTable.create", subject = "newEntitySupplier")
    private OrderLine linesTableCreateNewEntitySupplier() {
        OrderLine ordrline = metadata.create(OrderLine.class);
        ordrline.setOrder(this.getEditedEntity());
        return ordrline;
    }
...

Regards.

One of my colleagues found a way to map a field in the composite key in the OrderLine entity using:

@MapsId("orderLink")
@JoinColumn(name="ORDER_LINK")
@ManyToOne(fetch = FetchType.LAZY)
Order order;

And it then works as per normal, with the Order entity being able to define a Composition to the OrderLine entity, without needing additional code.

Unfortunately, this MapsId JPA tag does not work for Cuba if the parent has > 1 field (composite key) as PK. I tried to create an Association (single attribute) or use Embedded in the PK class of the child, but Cuba does not allow it.

MapsId does work for me.

The composite key definition:

@MetaClass(name = "sample_OrderLineKey")
@Embeddable
public class OrderLineKey extends EmbeddableEntity {
  // private static final long serialVersionUID = ...;
  
  @NotNull
  @Column(name = "ORDER_ID", nullable = false)
  private Integer orderId;

  @NotNull
  @Column(name = "LINE_ID", nullable = false)
  private Integer lineId;

  // getters, setters
  // make sure to implement equals and hashCode!
}

Code for the OrderLine Entity:

@Table("DEMO_ORDERLINE")
@Entity(name = "demo_OrderLine")
public class OrderLine extends BaseGenericIdEntity<OrderLineKey> {
  // private static final long serialVersionUID = ...
  @EmbeddedId
  @AttritubeOverrides({
      @AttributeOverride(name = "orderId", column = @Column(name = "ORDER_ID")),
      @AttributeOverride(name = "lineId", column = @Column(name = "LINE_ID"))
  })
  private OrderLineKey id;

  @ManyToOne(fetch = FetchType.LAZY)
  @MapsId("orderId")
  @JoinColumn(name = "ORDER_ID", nullable = false)
  private Order order;

  // getter, setter
}

MapsId only works if the PARENT (i.e. Order) only has 1 key field (e.g. Order ID).
If the parent has 2 key fields or more (composite key), I am not sure how use it then.

What I need is a 3 level relationship: Parent -> Child -> Grandchild (has 3 key fields, 2 coming from Child).
e.g if each of the Orderline has a list of Remarks associated with it, how do can I define it ?

The procedure is the same. Add an embeddable entity which represents the primary key.

i.E. for the OrderLineRemarkKey we could define it as follows:

@MetaClass(name = "demo_OrderLineRemarkKey")
@Embeddable
public class OrderLineRemarkKey extends EmbeddableEntity {
    //private static final long serialVersionUID ...

    @Embedded
    private OrderLineKey orderLineKey;

    @Column(name = "REMARK_ID", nullable = false)
    private Integer remarkId;
  
    // getter, setter, equality / hashcode ..
}

and we can add the reference to the OrderLine inside the OrderLineRemark like this:

@Table(name = "DEMO_ORDER_LINE_REMARK")
@Entity(name = "demo_OrderLineRemark")
public class OrderLineRemark extends BaseGenericIdEntity<OrderLineRemarkKey> {
    // private static final long serialVersionUID = ...;

    @EmbeddedId
    private OrderLineRemarkKey id;

    @Column(name = "REMARK", nullable = false)
    private String remark;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("orderLineKey")
    @JoinColumns({
            @JoinColumn(name = "ORDER_ID", referencedColumnName = "ORDER_ID"),
            @JoinColumn(name = "LINE_ID", referencedColumnName = "LINE_ID")
    })
    private OrderLine orderLine;

    // getter, setter, etc.

Note: I haven’t used this kind of composition with cuba, so I don’t have any experience on what kind of problems you might encounter using this kind of model. Doesn’t seem to be well supported at the moment. It should work with JPA/EclipseLink, so I’d expect the ORM commands to be working

Looks like the SQL Generator doesn’t produce the correct DDL.