Hello.
I'm using Hibernate to map a legacy database with some complicated associations. I need to map a bidirectional @oneToMany association where the @onetoMany is the owner side. So I used the @JoinColumns annotation.
My problem is to use Cascade on this association. When I try to save a new instance it works (using save or merge). But when I remove an item of the collection and need to update the instance I have some problems. I'll post here a small version of the classes (the problem happens with this reduced version too):
Code:
@Table(name="AVORDER")
@Entity
public class Order implements Serializable {
@EmbeddedId
private OrderPK pk;
@Column(name = "DT_SHIPMENT")
private Date shipmentDate;
@OneToMany(cascade=CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@JoinColumns( {
@JoinColumn(name = "NR_ID", referencedColumnName = "NR_ID"),
@JoinColumn(name = "NR_YEAR", referencedColumnName = "NR_YEAR") })
private List<OrderItem> items;
//getters and setters...
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (this == obj)
return true;
if ( obj.getClass() != getClass() )
return false;
Order other = (Order) obj;
return new EqualsBuilder().append(other.getPk(), this.getPk())
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(13, 21).append(this.getPk()).toHashCode();
}
}
Code:
@Embeddable
public class OrderPK implements Serializable {
private static final long serialVersionUID = 1L;
@Column(name = "NR_ID")
private Integer id;
@Column(name = "NR_YEAR")
private Integer year;
//getters and setters...
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37).append(year).append(id).toHashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (this == obj)
return true;
if (getClass() != obj.getClass())
return false;
OrderPK other = (OrderPK) obj;
return new EqualsBuilder().append(year, other.year).append(id,
other.id).isEquals();
}
}
Code:
@Entity
public class OrderItem implements Serializable {
private static final long serialVersionUID = 1L;
@EmbeddedId
private OrderItemPK pk;
@Column(name = "VL_QTDVOL")
private Double quantity;
//getters and setters...
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (this == obj)
return true;
if (!(obj instanceof OrderItem))
return false;
OrderItem other = (OrderItem) obj;
return new EqualsBuilder().append(this.getPk(), other.getPk())
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(39, 41).append(this.getPk()).toHashCode();
}
}
Code:
@Embeddable
public class OrderItemPK implements Serializable {
private static final long serialVersionUID = 1L;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns( {
@JoinColumn(name = "NR_ID", referencedColumnName = "NR_ID", insertable = false, updatable = false, nullable = false),
@JoinColumn(name = "NR_YEAR", referencedColumnName = "NR_YEAR", insertable = false, updatable = false, nullable = false) })
private Order order;
@Column(name = "NR_ITEID")
private Integer id;
//getters and setters...
@Override
public int hashCode() {
return new HashCodeBuilder(15, 35).append(order).append(id)
.toHashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (obj == this)
return true;
if (obj.getClass() != this.getClass())
return false;
OrderItemPK other = (OrderItemPK) obj;
return new EqualsBuilder().append(order, other.order).append(id,
other.id).isEquals();
}
}
If I try to save, like this:
Code:
public void saving() {
OrderPK orderPK = new OrderPK();
orderPK.setYear(2011);
orderPK.setId(1);
Order order = new Order();
order.setPk(orderPK);
order.setShipmentDate(new Date());
OrderItemPK itemPk = new OrderItemPK();
itemPk.setId(1);
itemPk.setOrder(order);
OrderItem item1 = new OrderItem();
item1.setQuantity(1d);
item1.setPk(itemPk);
OrderItemPK itemPk2 = new OrderItemPK();
itemPk2.setId(2);
itemPk2.setOrder(order);
OrderItem item2 = new OrderItem();
item2.setQuantity(2d);
item2.setPk(itemPk2);
List<OrderItem> itens = new ArrayList<OrderItem>();
itens.add(item1);
itens.add(item2);
order.setItems(itens);
Transaction tx = session.beginTransaction();
session.save(order);
tx.commit();
}
It works. But the next time, when I try to merge the existent instance with another one (with a removed OrderItem), it does not work:
Code:
public void merging() {
OrderPK orderPK = new OrderPK();
orderPK.setYear(2011);
orderPK.setId(1);
Order order = new Order();
order.setPk(orderPK);
order.setShipmentDate(new Date());
OrderItemPK itemPk = new OrderItemPK();
itemPk.setId(1);
itemPk.setOrder(order);
OrderItem item1 = new OrderItem();
item1.setQuantity(1d);
item1.setPk(itemPk);
List<OrderItem> itens = new ArrayList<OrderItem>();
itens.add(item1);
order.setItems(itens);
Transaction tx = session.beginTransaction();
session.merge(order);
tx.commit();
}
The SQL output:
Code:
Hibernate: update AVORDER set DT_SHIPMENT=? where NR_ID=? and NR_YEAR=?
18:59:36,265 TRACE BasicBinder:81 - binding parameter [1] as [TIMESTAMP] - Fri Mar 18 18:59:36 AFT 2011
18:59:36,265 TRACE BasicBinder:81 - binding parameter [2] as [INTEGER] - 1
18:59:36,265 TRACE BasicBinder:81 - binding parameter [3] as [INTEGER] - 2011
...
Hibernate: update OrderItem set NR_ID=null, NR_YEAR=null where NR_ID=? and NR_YEAR=? and NR_ITEID=?
18:59:36,281 TRACE BasicBinder:81 - binding parameter [1] as [INTEGER] - 1
18:59:36,281 TRACE BasicBinder:81 - binding parameter [2] as [INTEGER] - 2011
18:59:36,281 TRACE BasicBinder:81 - binding parameter [3] as [INTEGER] - 2
18:59:36,281 TRACE BasicBinder:81 - binding parameter [4] as [INTEGER] - 1
...
org.postgresql.util.PSQLException: The column index is out of range: 4, number of columns: 3.
First, why Hibernate tries to update OrderItem if I set orphanRemoval to true? Shouldn't it just delete the OrderItem? And why it tries to bind another parameter?
I probably have done something wrong, but I can't figure it out. If I change the referencedColumnNames in the JoinColumns to new ones everything works fine, but hibernate creates new fields (of course) to represent the new fields. I can't do this.
I'm using Hibernate 3.6.0 Final and tried this both with Postgresql 9 and Oracle (not sure about the version).
Thanks in advance, I'm really stuck with this!