views:

1005

answers:

1

Hi guys, when I trying to update the object RFQ , Hibernate tends to update its child collection BQMaster and trying to set and update the identifier of BQMaster which normally it should not be allowed.

The postgre log file:
*2009-05-22 11:16:53 ERROR: duplicate key violates unique constraint "bq_masters_pkey"*
*2009-05-22 11:16:53 STATEMENT: update bq_masters set rfq_id=$1, id=$2 where id=$3*

error in console:
*Caused by: java.sql.BatchUpdateException: Batch entry 0 update bq_masters set rf q_id=2, id=0 where id=49449 was aborted. Call getNextException to see the cause .*
at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handle Error(AbstractJdbc2Statement.java:2392)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutor Impl.java:1257)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.ja va:334)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc 2Statement.java:2451)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.jav a:58)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java: 195)
... 32 more

It seems very weird as it tried to update the id of bq_masters. It should keep the id and update only the other fields.

Criteria crit = session.createCriteria(BQMaster.class);
crit.add(Restrictions.eq("project",project);
crit.setFirstResult(5);
crit.setMaxResult(2);
List bqMasters = crit.list();  

RFQ rfq = (RFQ)session.get(RFQ.class, RFQId);
rfq.setBqMasters(bqMasters);
session.update(rfq);

Hibernate mapping:

<class name="RFQ" table="rfq">
  <id name="id" column="id">
     <generator class="native">
        <param name="sequence">rfq_id_seq</param>
     </generator>
  </id>

  <property name="reference" column="reference" />
  <property name="status" column="status" />
  <property name="created" column="created" />
  <property name="modified" column="modified" />

  <many-to-one name="project" class="Project" column="project_id" cascade="save-update" />

  <list name="subcons" table="rfq_subcons">
     <key column="rfq_id" />
     <list-index column="id"/>
     <many-to-many class="Subcon" column="subcon_id"/> 
  </list>

  <list name="bqMasters">
     <key column="rfq_id" />
     <list-index column="id"/>
     <one-to-many class="BQMaster"/>
  </list>
</class>  

<class name="BQMaster" table="bq_masters">
  <id name="id" column="id">
     <generator class="native">
        <param name="sequence">bq_masters_id_seq</param>
     </generator>
  </id>

  <property name="reference" column="reference" />
  <property name="description" column="description" />
  <property name="lod" column="lod" />
  <property name="uom" column="uom" />
  <property name="quantity" column="quantity" />
  <property name="parentId" column="parent_id" />
  <property name="groupId" column="group_id" />
  <property name="leaf" column="leaf" />
  <property name="active" column="active" />
  <property name="subcontract" column="subcontract" />
  <property name="created" column="created" />
  <property name="modified" column="modified" />

  <many-to-one name="project" class="Project" column="project_id" cascade="save-update" />

  <many-to-one name="page" class="Page" column="page_id" cascade="save-update" />

  <many-to-one name="rfq" class="RFQ" column="rfq_id" cascade="save-update" />

</class>

database structures for reference :

CREATE TABLE rfq
(   
    id bigserial NOT NULL, 
    project_id bigint, 
    reference text, 
    status text, 
    created timestamp without time zone, 
    modified timestamp without time zone, 
    CONSTRAINT rfq_pkey PRIMARY KEY (id), 
    CONSTRAINT rfq_project_id_fkey FOREIGN KEY (project_id) 
    REFERENCES projects (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
);  

CREATE TABLE bq_masters
(
    id bigserial NOT NULL, 
    reference varchar(100), 
    project_id bigint, 
    page_id bigint, 
    rfq_id bigint,
    description text, 
    lod integer, 
    uom varchar(20), 
    quantity numeric, 
    parent_id bigint, 
    group_id bigint,
    leaf boolean, 
    active boolean, 
    subcontract boolean, 
    created timestamp without time zone, 
    modified timestamp without time zone, 
    CONSTRAINT bq_masters_pkey PRIMARY KEY (id), 
    CONSTRAINT bq_masters_project_id_fkey FOREIGN KEY (project_id) 
    REFERENCES projects (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE, 
    CONSTRAINT bq_masters_page_id_fkey FOREIGN KEY (page_id)
    REFERENCES pages (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE,
    CONSTRAINT bq_masters_rfq_id_fkey FOREIGN KEY (rfq_id)
    REFERENCES rfq (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
);

I have tried adding inverse="true" in RFQ one-to-many mapping but to no avail, as it doesn't update the BQMasters of corresponding RFQ object.

Does anybody has any clue on solving this problem? Thanks in advance.

A: 

Hibernate does exactly what yo are doing in memory. You are putting the bqmasters into the bqMasters table, and you mapped this relation to the rfq_id column in bqmasters, so it updates the foreign key.

You must use inverse for this. The same column rfq_id is used for two properties, only one could be used by Hibernate.

You have actually a consistence problem in memory. If you add a bqmaster to the list, you also need to update the rfq property of it, because it is holding the same parent-child relation and also mapped to the same database column.

By the way, you don't have to call update on objects that are already in the session. Hibernate will notify that the object has changed and will update it.

Stefan Steinegger