Hibernate - Child Collection Id changed in update
when I try to update the RFQ object, Hibernate tends to update its BQMaster child collection and tries to set and update the BQMaster id which shouldn't normally be resolved.
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 write 0 update bq_masters set rf q_id = 2, id = 0, where id = 49449 was aborted. Call getNextException to see why.
at org.postgresql.jdbc2.AbstractJdbc2Statement $ BatchResultHandler.handle Error (AbstractJdbc2Statement.java:2392)
at org.postgresql.core.v3.QueryExecutorImpl.processResults: org.postgresql.core.v3.QueryExecutorImpl.processResults (QueryExecutor
. .execute (QueryExecutorImpl.ja wa: 334)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch (AbstractJdbc 2Statement.java:2451)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch.BatchingBatcher.doExecuteBatch.
at org.hibernate.jdbc.AbstractBatcher.executeBatch (AbstractBatcher.java: 195)
... 32 more
Seems very strange as it was trying to update the bq_masters id. It should contain an ID and only update 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);
Sleep mode display:
<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 tried adding inverse = "true" to the one-to-many conversion of the RFQ, but to no avail as it does not update the BQMasters of the corresponding RFQ object.
Does anyone have a clue to solve this problem? Thanks in advance.
Hibernate does exactly what you do in memory. You put bqmasters in the bqMasters table and you mapped that relationship to a column rfq_id
in bqmasters, so it updates the foreign key.
For this, you must use inversion. The same column is rfq_id
used for two properties, only one can be used by Hibernate.
You actually have a consistency problem in memory. If you add bqmaster to the list, you also need to update its rfq property as it maintains the same parent-child relationship and also maps to the same database column.
By the way, you don't need to call update for objects that are already in session. Hibernate will notify the object change and update it.
a source to share