0

I am trying to persist a new Order object from my arquillian test class. The order entity contains a list of OrderDetail entity which has a composed primary key of 4 fields.

But while Hibernate is trying to insert the new OrderDetail object it also insert the NOT NEW Item object (Item is one of the main PK fields in the OrderDetail entity and annotated with @ID ) , and this results in a unique constrain exception from the Item table in the DB.

Note that I removed the CascadeType from the Item @OneToMany annotation and tried also the CascadeType.Refresh but I got the same result and nothing changed. It works as it force CascadeType.Insert in all cases.

It worked as expected when I used @EmbeddedId annotation instead of @IDClass But I want to know why it did not work with @IDClass ? is it a bug in Hibernate ? It should work perfectly as per the JPA 2.1 specification.

Order Entity

@Entity
@Table(name = "ORDERS")
public class Orders extends BaseEntity {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "ORDERNO")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long orderNo ;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "order", fetch = FetchType.LAZY)
    private List<OrderDetail> orderDetailsList ;

    // setters and getters
}

OrderDetail Entity

@Entity
@Table(name = "ORDERDETAILS")
@IdClass(OrderDetailsPK.class)
public class OrderDetail extends BaseEntity {

private static final long serialVersionUID = 1L;

@Id
@JoinColumn(name = "ORDERNO", referencedColumnName = "ORDERNO")
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private Orders order;

@Id
@JoinColumn( name = "ITEMNO", referencedColumnName = "ITEMNO", insertable = false, updatable = false)
@ManyToOne(cascade = CascadeType.REFRESH,optional = false, fetch = FetchType.LAZY)
//@Cascade({CascadeType.DETACH}) // I tried this but did not work, it keep try to insert the item.
private Item item;

@Id
@Column(name = "LINETYPE")
private String lineType;

@Id
@Column(name = "PROMITEMNO")
private String promItemNo;

Item Entity

@Entity
@Table(name = "ITEM")
public class Item extends BaseEntity {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "ITEMNO")
    private String itemno;
    // setters and getters

   @Override
   public int hashCode() {
    int hash = 0;
    hash += (itemno != null ? itemno.hashCode() : 0);
    return hash;
   }

   @Override
   public boolean equals(Object object) {
    if (!(object instanceof Item)) {
        return false;
    }
    Item other = (Item) object;
    System.out.println("Item Equal Method = " + !((this.itemno == null &&        other.itemno != null) || (this.itemno != null && !this.itemno.equals(other.itemno))));
    return !((this.itemno == null && other.itemno != null) || (this.itemno != null && !this.itemno.equals(other.itemno)));
}

  @Override
  public String toString() {
    return "com.unilever.sas.model.entities.masterdata.Item[ itemno=" + itemno + " ]";
}

}

OrderDetailsPK

public class OrderDetailsPK  implements Serializable 
{

    private static final long serialVersionUID = 1L;

    private Long order;
    private String item;
    private String lineType;
    private String promItemNo;

    public OrderDetailsPK() {
    }

    public OrderDetailsPK(Long order, String item, String lineType, String promItemNo) {
        this.order = order;
        this.item = item;
        this.lineType = lineType;
        this.promItemNo = promItemNo;
    }

    public Long getOrder() {
        return order;
    }

    public String getItem() {
        return item;
    }

    public String getLineType() {
        return lineType;
    }


    public String getPromItemNo() {
        return promItemNo;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 73 * hash + Objects.hashCode(this.order);
        hash = 73 * hash + Objects.hashCode(this.item);
        hash = 73 * hash + Objects.hashCode(this.lineType);
        hash = 73 * hash + Objects.hashCode(this.promItemNo);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final OrderDetailsPK other = (OrderDetailsPK) obj;
        if (!Objects.equals(this.item, other.item)) {
            return false;
        }
        if (!Objects.equals(this.lineType, other.lineType)) {
            return false;
        }
        if (!Objects.equals(this.promItemNo, other.promItemNo)) {
            return false;
        }
        if (!Objects.equals(this.order, other.order)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "OrderDetailsPK{" + "order=" + order + ", item=" + item + ", lineType=" + lineType + ", promItemNo=" + promItemNo + '}';
    }

Arquillian Test

@RunWith(Arquillian.class)
public class OrdersFacadeTest
{


@Inject
OrdersFacadeLocal ordersFacade ;

@Inject    
ItemFacadeLocal itemFacade ;

@Test
public void testCreateOrder() throws Exception 
{
    Item item = itemFacade.find("20247912");
    Orders order = new Orders();

    OrderDetail orderDetail = new OrderDetail();
    orderDetail.setOrder(order);
    orderDetail.setItem(item);
    orderDetail.setLineType("NLI");
    orderDetail.setPromItemNo("99999");

    List<OrderDetail> orderDeialsList = new ArrayList<>();
    orderDeialsList.add(orderDetail);

    order.setOrderDetailsList(orderDeialsList);      

    ordersFacade.create(order);

    Orders createdOrder = ordersFacade.find(order.getOrderNo());
    assertEquals(order, createdOrder);
}
Samy Omar
  • 800
  • 14
  • 29
  • 1
    suppose this [post](https://stackoverflow.com/questions/3585034/how-to-map-a-composite-key-with-hibernate/) may be helpful to get a understanding on how it works. – Rajith Pemabandu May 23 '17 at 00:01
  • If you can replicate it with a [test case](http://in.relation.to/2016/01/14/hibernate-jpa-test-case-template/), then you should open a Jira issue. – Vlad Mihalcea May 23 '17 at 05:11

1 Answers1

0

It could be a bug, but there are also some strange mapping choices here as well.

Why does the @JoinColumn is not allowed to set the FK on insert or update? Who should set the FK in this case?

@JoinColumn( 
    name = "ITEMNO", 
    referencedColumnName = "ITEMNO", 
    insertable = false, updatable = false
)

If you can replicate it with a test case, then you should open a Jira issue.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • This is because this FK is part of the composite PK and annotated with @ID – Samy Omar May 23 '17 at 09:14
  • Also I found something else very strange, when I moved the test function logic to the OrderFacade EJB and just call this function from the my arquillian test class it works normally !!! – Samy Omar May 23 '17 at 09:17