Hi Ernico,

The cause of the problem is not the composite primary key but the  
mapping of the primary key column and join column in the child table:

(1) in the parent class TblScmpdt:
the primary key:  scmpdtId (the column: SCMPDT_ID)

(2) in the child class
the composite primary key: pdtbnfId (column: PDTBNF_ID)
                           scmpdtId (column: SCMPDT_ID)
the join column: SCMPDT_ID 

Please note that in this mapping, you have a primary key and the join value all 
mapped to the column SCMPDT_ID in the child table.

In your test case:

TblScmpdt tblScmpdt = new TblScmpdt();  (1)
TblPdtbnf tblPdtbnf = new TblPdtbnf();  (2)   
TblScmpdt.addTblpdtbnf(tblPdtbnf);      (3)

Since you did not have the primary key set in the parent table (TblScmpdt ), 
openjpa will regard this entity as a new one, and perform persist() on it even 
though you call merge().

The problem is in the child entity. In statement (3), you establish the 1-many 
relationship between tblScmpdt and tblPdtbnf. The join value is therefore the 
primary key of tblScmpdt, which is automatically generated by openjpa via table 
generator. However, since you did not set the primary keys (pdtbnfId and 
scmpdtId, both are Integer class) for tblPdtbnf, they are null values (the 
default values). When both the null value of scmpdtId primary key and the 
non-null join value are set to column SCMPDT_ID, you got the following error 
message:

Caused by: <openjpa-1.0.0-r420667:568756 fatal user error>
org.apache.openjpa.persistence.InvalidStateException: Attempt to set column
"TBL_PDTBNF.SCMPDT_ID" to two different values: (null)"null", (class
java.lang.Integer)"700" 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.


This scenario can not be distinguished from the following one which is 
obviously a user-error (note Statement (2.1)):

TblScmpdt tblScmpdt = new TblScmpdt();  (1)
TblPdtbnf tblPdtbnf = new TblPdtbnf();  (2)   
tblPdtbnf.setScmpdtId(null);            (2.1)
TblScmpdt.addTblpdtbnf(tblPdtbnf);      (3)


This problem can be fixed by not using the primary key column as the join 
column. Specifically, setting the join-column name to something other than  
SCMPDT_ID should solve this problem:

@ManyToOne(fetch = FetchType.LAZY,cascade=CascadeType.MERGE)
@JoinColumn(name = "XX_ID",referencedColumnName="SCMPDT_ID")
private TblScmpdt tblScmpdt;

If you really want to use the primary key column as the join column, then don't 
let openjpa to automatically generate the primary key, so that you can 
explicitly set the primary keys of the child class the same as the join value. 
If you really want openjpa to automatically generate primary key, then you have 
to use the "lousy" way you described in your earlier email. Please let me know 
what you think. Thanks!


-f

--- On Sat, 6/14/08, Enrico Goosen <[EMAIL PROTECTED]> wrote:

> From: Enrico Goosen <[EMAIL PROTECTED]>
> Subject: Re: @OneToMany/@ManyToOne, Bidirectional, Composite Key
> To: users@openjpa.apache.org
> Date: Saturday, June 14, 2008, 5:46 AM
> Hi Fay,
> 
> The primary key for TblScmpdt is database generated.
> Here's the mapping:
> 
> @TableGenerator(name="baseGenerator",schema="EBSTATUS",table="TBL_KEYGEN",pkColumnName="PRIMARY_KEY_COLUMN"
>       
> ,valueColumnName="LAST_USED_ID",pkColumnValue="TBL_SCMPDT_ID",allocationSize=100)
> @Id
> @GeneratedValue(strategy=GenerationType.TABLE,generator="baseGenerator")
> @Column(name = "SCMPDT_ID",nullable=false)
> private Integer scmpdtId; 
> 
> As I mentioned in my previous post, the code works (no
> exceptions), but its
> flawed because of the bug in JPA when performing a cascade
> persist on a
> OneToMany entity where the child (many) entity has a
> composite key.
> 
> I should simply be able to do the following:
> 
> TblScmpdt tblScmpdt = new TblScmpdt(); //new
> (non-persistent) parent entity
> //set fields on tblScmpdt ...
> TblPdtbnf tblPdtbnf = new TblPdtbnf(); //new
> (non-persistent) child entity
> //set fields on tblPdtbnf ...
> tblScmpdt.addTblpdtbnf(tblPdtbnf); //see method below,
> which sets the parent
> referrence on child
> 
> //TblScmpdt method:
> public void addTblPdtbnf(TblPdtbnf tblPdtbnf) {
>       tblPdtbnf.setTblScmpdt(this); //need to set both sides of
> bidirectional
> relationship
>       getTblPdtbnfs().add(tblPdtbnf);
> }
> //Now I should be able to persist parent and cascade
> persist child
> automatically according to JPA docs
> tblScmpdt = em.merge(tblScmpdt); 
> 
> ...but unfortunately, the above doesn't work.
> I have to merge/persist the parent first > then get the
> ID of the newly
> persisted parent and set it on the new child > then add
> the child to the
> parent > then merge the parent again. Tedious!
> 
> Do you understand the problem?
> 
> Regarding TblPdtbnfcde, this is a OneToOne reference on
> TblPdtbnf.
> TblPdtbnf's composite primary key is made up of
> TblPdtbnfcde.pdtbnfId and
> TblScmpdt.scmpdtId
> 
> By the way, cascade persist works fine on my other
> OneToMany classes where
> there isn't a composite key.
> So this is definitely a bug.
> 
> 
> 
> Enrico,
> 
>     What is the primary key field in the TblScmpdt entity?
> Did you set
> primary key for tblScmpdt before calling merge? I made an
> integer primary
> key field in TblScmpdt, and set the primary key to 1 in
> tblScmpdt before
> calling merge, and your test code works fine for me (I also
> omit
> TblPdtbnfcde in your test code for lack of detailed
> information). If you
> already set the primary key and still get the error, please
> specify more
> detail of your TblScmpdt class.     
>  
> 
> -f
> 
> -- 
> View this message in context:
> http://www.nabble.com/%40OneToMany-%40ManyToOne%2C-Bidirectional%2C-Composite-Key-BUG-tp17801245p17839130.html
> Sent from the OpenJPA Users mailing list archive at
> Nabble.com.


      

Reply via email to