Wednesday, January 13, 2010

OpenJpa bi-direction mappings

Let’s say we have Order and OrderPostEntry which relation is one to many.

In Order, it has a set of OrderPostEntry.

private Set<OrderPostEntry> orderPostEntries  = new HashSet<OrderPostEntry>();


In OrderPostEntry, it has the reference to Order.



        private Order order;

@ManyToOne(optional = false, targetEntity = OrderImpl.class)
@JoinColumn(name = "ORDER_UID")
@ForeignKey
public Order getOrder() {
return this.order;
}

public void setOrder(final Order order) {
this.order = order;
}


When the OrderPostEntry was added into Order, we want to the order will be set into each OrderPostEntry.



        @OneToMany(mappedBy = "order", targetEntity = OrderPostEntry.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Set getOrderPostEntries() {
return orderPostEntries;
}

public void setOrderPostEntries(final Set orderPostEntries) {
for (OrderPostEntry orderPostEntry : orderPostEntries) {
orderPostEntry.setOrder(this);
}
this.orderPostEntries= orderPostEntries;
}


if you do so, you will get the exception "org.apache.renamed.openjpa.persistence.InvalidStateException: Attempt to set column "TORDERPOSTENTRY.ORDER_UID" to two different values: (null)"null", (class java.lang.Long)"117,801" This can occur when you fail to set both sides of a two-sided relation between objects, or when you map different fields to the same column, but you do not keep the values of these fields in synch."


The setXXX() method will be called by openjpa when it try to read data from database and populate them into java object. The code to set order automatically should not be called by the openjpa.

To make it work, another set method should be created and that will be used by openjpa internally.



        @OneToMany(mappedBy = "order", targetEntity = AbstractOrderPostEntryImpl.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
protected Set getOrderPostEntriesInternal() {
return this.orderPostEntries;
}

protected void setOrderPostEntriesInternal(final Set orderPostEntries) {
this.orderPostEntries = orderPostEntries;
}

@Transient
public Set getOrderPostEntries() {
return getOrderPostEntriesInternal();
}

public void setOrderPostEntries(final Set orderPostEntries) {
for (OrderPostEntry orderPostEntry : orderPostEntries) {
orderPostEntry.setOrder(this);
}
setOrderPostEntriesInternal(orderPostEntries);
}

The setOrderPostEntriesInternal method will be called by openjpa. The method setOrderPostEntries will be called when our code adds orderPostEntries into Order.

The code below on OrderPostEntry.java is not good. It may cause dead lock on database.



private long orderUid;

@Basic
@Column(name = "ORDER_UID")
public long getOrderUid() {
return this.orderUid;
}

public void setOrderUid(final long orderUid) {
this.orderUid = orderUid;
}

No comments:

Post a Comment