The Java EE 5 Tutorial

Compound Primary Keys

A compound primary key is made up of multiple fields and follows the requirements described in Primary Key Classes. To use a compound primary key, you must create a wrapper class.

In order, two entities use compound primary keys: Part and LineItem.

Part uses the PartKey wrapper class. Part’s primary key is a combination of the part number and the revision number. PartKey encapsulates this primary key.

LineItem uses the LineItemKey class. LineItem’s primary key is a combination of the order number and the item number. LineItemKey encapsulates this primary key. This is the LineItemKey compound primary key wrapper class:

package order.entity;

public final class LineItemKey implements
             java.io.Serializable {

    private Integer orderId;
    private int itemId;

    public int hashCode() {
        return ((this.getOrderId()==null
                        ?0:this.getOrderId().hashCode())
                 ^ ((int) this.getItemId()));
    }

    public boolean equals(Object otherOb) {
        if (this == otherOb) {
            return true;
        }
        if (!(otherOb instanceof LineItemKey)) {
            return false;
        }
        LineItemKey other = (LineItemKey) otherOb;
        return ((this.getOrderId()==null
                        ?other.orderId==null:this.getOrderId().equals
                (other.orderId)) && (this.getItemId ==
                    other.itemId));
    }

    public String toString() {
        return "" + orderId + "-" + itemId;
    }
}

The @IdClass annotation is used to specify the primary key class in the entity class. In LineItem, @IdClass is used as follows:

@IdClass(order.entity.LineItemKey.class)
@Entity
...
public class LineItem {
...
}

The two fields in LineItem are tagged with the @Id annotation to mark those fields as part of the compound primary key:

@Id
public int getItemId() {
    return itemId;
}
...
@Id
@Column(name="ORDERID", nullable=false,
    insertable=false, updatable=false)
public Integer getOrderId() {
    return orderId;
}

For orderId, you also use the @Column annotation to specify the column name in the table, and that this column should not be inserted or updated, as it is an overlapping foreign key pointing at the EJB_ORDER_ORDER table’s ORDERID column (see One-to-Many Relationship Mapped to Overlapping Primary and Foreign Keys). That is, orderId will be set by the Order entity.

In LineItem’s constructor, the line item number (LineItem.itemId) is set using the Order.getNextId method.

public LineItem(Order order, int quantity, VendorPart
        vendorPart) {
    this.order = order;
    this.itemId = order.getNextId();
    this.orderId = order.getOrderId();
    this.quantity = quantity;
    this.vendorPart = vendorPart;
}

Order.getNextId counts the number of current line items, adds one, and returns that number.

public int getNextId() {
    return this.lineItems.size() + 1;
}

Part doesn’t require the @Column annotation on the two fields that comprise Part’s compound primary key. This is because Part’s compound primary key is not an overlapping primary key/foreign key.

@IdClass(order.entity.PartKey.class)
@Entity
...
public class Part {
...
    @Id
    public String getPartNumber() {
        return partNumber;
    }
...
    @Id
    public int getRevision() {
        return revision;
    }
...
}