Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] bi-directional mapping causes two inserts

The issue is occurring because you have side-effects in your set methods and
are using property access.  When using property access you should ensure
that you do not have side-effects in your property methods, as they can
cause objects being build and managed to be corrupted.  This is the main
reason we recommend using field access, not property access (put annotations
on variables not get methods).

Either remove the side-effects from your set method, or use field access.  I
would strongly recommend field access in general.

The reason that the objects are being corrupted is that EclipseLink creates
back-up clones to track object changes.  Building these backup clones with
your side-effects causes the managed object to get a reference to the backup
clone and causes the double insert.  This only occurs with "deferred"
changes tracking, which is used when weaving is not used.  When weaving is
used "attribute" change tracking should be used, which does not have this
issue.  However you can still have many other issues with side-effects in
set methods, such as if the relationship was lazy, your set method would be
instantiating it.



Magnus Heino wrote:
> 
> But this works:
> 
>         this.building = ModelFixture.createAnonymousBuilding();
>         Comment comment = ModelFixture.createComment();
>         comment.setBuilding(this.building);
> 
>         this.building.addComment(comment);
> 
> 
> 
> And then:
> 
>     protected void setComments(Set<Comment> comments) {
>         this.comments = comments;
>     }
> 
>     public void addComment(Comment comment) {
>         comment.setBuilding(this);
>         this.comments.add(comment);
>     }
> 
> So eclipselink is calling my setter, and is getting confused if I set the
> building pointer there...
> 
> /Magnus
> 
> On Wed, May 28, 2008 at 10:22 AM, Magnus Heino <magnus@xxxxxxxxx> wrote:
> 
>>
>> More info...
>>
>> This:
>>
>>         this.building = ModelFixture.createAnonymousBuilding();
>>         Set<Comment> comments = new HashSet<Comment>();
>>         Comment comment = ModelFixture.createComment();
>>         comment.setBuilding(this.building);
>>         comments.add(comment);
>>
>>         this.building.setComments(comments);
>>         this.entityManager.persist(this.building);
>>
>> With setComments like this:
>>
>> public void setComments(Set<Comment> comments) {
>>         this.comments = comments;
>>     }
>>
>> works as expected!
>>
>> This however:
>>
>> this.building = ModelFixture.createAnonymousBuilding();
>>         Set<Comment> comments = new HashSet<Comment>();
>>         Comment comment = ModelFixture.createComment();
>>         comments.add(comment);
>>         this.building.setComments(comments);
>>         this.entityManager.persist(this.building);
>>
>> With setComments like this:
>>
>> public void setComments(Set<Comment> comments) {
>>         for (Comment comment : comments) {
>>             comment.setBuilding(this);
>>         }
>>         this.comments = comments;
>>     }
>>
>> Fails with double inserts.. what is different? I can't see anything
>> special
>> with the debugger.
>>
>> I have these properties set:
>>
>>                 <prop key="eclipselink.logging.level">ALL</prop>
>>                 <prop
>> key="eclipselink.logging.logger">se.lantmateriet.origo.persistence.eclipselink.support.SLF4JSessionLogger</prop>
>>                 <prop key="eclipselink.logging.timestamp">false</prop>
>>                 <prop key="eclipselink.logging.thread">false</prop>
>>                 <prop key="eclipselink.logging.session">true</prop>
>>                 <prop key="eclipselink.jdbc.native-sql">true</prop>
>>                 <prop key="eclipselink.jdbc.cache-statements">true</prop>
>>                 <prop key="eclipselink.weaving">false</prop>
>>                 <prop
>> key="eclipselink.ddl-generation">drop-and-create-tables</prop>
>>                 <prop
>> key="eclipselink.ddl-generation.output-mode">both</prop>
>>                 <prop
>> key="eclipselink.application-location">src/main/sql/</prop>
>>
>> /Magnus
>>
>>
>> On Wed, May 28, 2008 at 8:26 AM, Magnus Heino <magnus@xxxxxxxxx> wrote:
>>
>>> I added this to Building:
>>>
>>> public Building() {
>>>         try {
>>>             throw new RuntimeException("debug");
>>>         } catch (RuntimeException e) {
>>>             this.logger.info("Building created " + this.toString(), e);
>>>         }
>>>     }
>>>
>>> and this to Comment:
>>>
>>>     @ManyToOne
>>>     @JoinColumn(name = "BUILDING_ID", nullable = false)
>>>     public Building getBuilding() {
>>>         this.logger.debug("Comment " + this.toString() + "getBuilding
>>> returning " + this.building.toString());
>>>         return this.building;
>>>     }
>>>
>>>
>>> and uploaded the output to here: http://www.alvkarlebygk.com/eclipselink
>>>
>>> As you can see, Eclipselink is creating many buildings... for some
>>> reason
>>> I don't understand.
>>>
>>> Thanks!
>>>
>>> /Magnus
>>>
>>>
>>> On Tue, May 27, 2008 at 9:18 PM, Gordon Yorke <gordon.yorke@xxxxxxxxxx>
>>> wrote:
>>>
>>>>  It was also noted that the second Building instance that EclipseLink
>>>> is
>>>> inserting is different from the one in the object.  Are any of the
>>>> model's
>>>> getters returning a clone?
>>>> --Gordon
>>>>
>>>> Gordon Yorke wrote:
>>>>
>>>> "(insertable=false,updatable=false)" would be a bit of a hack.  I
>>>> expected this to work as mapped and created a simple test case that
>>>> confirms
>>>> this should be working.  What else is going on?  Is this reproducible
>>>> in a
>>>> small testcase?
>>>> --Gordon
>>>>
>>>> Tim Hollosy wrote:
>>>>
>>>> Magnus,
>>>> I think your @ManyToOne needs a joincolumn annotation like:
>>>>
>>>> 	@ManyToOne
>>>> 	@JoinColumn(name="comment_id") //or whatever your join column is
>>>>
>>>> The other thing to try is setting one side of the relationship to
>>>> readonly (insertable=false,updatable=false).
>>>>
>>>> Tim
>>>>
>>>> On Tue, May 27, 2008 at 10:47 AM, Magnus Heino <magnus@xxxxxxxxx>
>>>> <magnus@xxxxxxxxx> wrote:
>>>>
>>>>
>>>>  This is such a basic thing, but I cannot see what I'm doing wrong.
>>>>
>>>> I have a annotation like this:
>>>>
>>>> class Building
>>>> @OneToMany(cascade=CascadeType.ALL, mappedBy="building")
>>>> public Set<Comment> getComments() {}
>>>>
>>>> And then
>>>>
>>>> class Comment
>>>> @ManyToOne
>>>> public Building getBuilding() {}
>>>>
>>>> I start a transaction, create a building, add a comment, and call
>>>> entityManager.persist(building);
>>>>
>>>> Now the INSERT is called twice for the Building. Why? If I remove the
>>>> @ManyToOne and make it unidirectional using a join table, it works.
>>>>
>>>> I'm using M7.
>>>>
>>>> Thanks!
>>>>
>>>> /Magnus
>>>>
>>>>
>>>> 2008-05-27 16:34:37,792 [main] INFO  Began transaction (1): transaction
>>>> manager [org.springframework.orm.jpa.JpaTransactionManager@11d2066];
>>>> rollback [true] at
>>>> org.springframework.test.context.transaction.TransactionalTestExecutionListener.startNewTransaction(TransactionalTestExecutionListener.java:259)
>>>> 2008-05-27 16:34:37,964 [main] TRACE [EL Finest]:
>>>> UnitOfWork(24440876)--Execute query DoesExistQuery() at ?.?(?:?)
>>>> 2008-05-27 16:34:37,964 [main] TRACE [EL Finest]:
>>>> UnitOfWork(24440876)--PERSIST operation called on: [Building@51064e
>>>> objectId
>>>> = 25da2090-9b5e-4b8d-81c3-2197c0ca4e01, objectVersion = 1]. at ?.?(?:?)
>>>> 2008-05-27 16:34:37,964 [main] TRACE [EL Finest]:
>>>> ClientSession(30168161)--Execute query ValueReadQuery() at ?.?(?:?)
>>>> 2008-05-27 16:34:37,964 [main] DEBUG [EL Fine]:
>>>> ClientSession(30168161)--Connection(6881863)--SELECT id_seq.NEXTVAL
>>>> FROM
>>>> DUAL at ?.?(?:?)
>>>> 2008-05-27 16:34:37,979 [main] TRACE [EL Finest]:
>>>> ServerSession(25218858)--sequencing preallocation for id_seq: objects:
>>>> 50 ,
>>>> first: 1, last: 50 at ?.?(?:?)
>>>> 2008-05-27 16:34:37,979 [main] TRACE [EL Finest]:
>>>> UnitOfWork(24440876)--assign sequence to the object (1 ->
>>>> [Building@51064e
>>>> objectId = 25da2090-9b5e-4b8d-81c3-2197c0ca4e01, objectVersion = 1]) at
>>>> ?.?(?:?)
>>>> 2008-05-27 16:34:37,979 [main] TRACE [EL Finest]:
>>>> UnitOfWork(24440876)--Execute query DoesExistQuery() at ?.?(?:?)
>>>> 2008-05-27 16:34:37,979 [main] TRACE [EL Finest]:
>>>> UnitOfWork(24440876)--PERSIST operation called on:
>>>> se.lantmateriet.origo.domain.model.building.Comment@1b3278a. at
>>>> ?.?(?:?)
>>>> 2008-05-27 16:34:37,979 [main] TRACE [EL Finest]:
>>>> UnitOfWork(24440876)--assign sequence to the object (2 ->
>>>> se.lantmateriet.origo.domain.model.building.Comment@1b3278a) at
>>>> ?.?(?:?)
>>>> 2008-05-27 16:34:37,995 [main] TRACE [EL Finest]:
>>>> UnitOfWork(24440876)--Execute query InsertObjectQuery([Building@51064e
>>>> objectId = 25da2090-9b5e-4b8d-81c3-2197c0ca4e01, objectVersion = 1]) at
>>>> ?.?(?:?)
>>>> 2008-05-27 16:34:37,995 [main] DEBUG [EL Fine]:
>>>> ClientSession(30168161)--Connection(6881863)--INSERT INTO BUILDING (ID,
>>>> OBJECTID, OBJECTVERSION, VERSION, OBJECTSTATUS, CONSTRUCTIONYEAR,
>>>> HOUSENUMBER, EXTENSIONYEAR, REALPROPERTYKEY, VERSIONVALIDFROM,
>>>> VERSIONVALIDTO, NOADDRESSNEEDED, MAINBUILDING, PREFIX, IDENTITY) VALUES
>>>> (?,
>>>> ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
>>>>     bind => [1, 25da2090-9b5e-4b8d-81c3-2197c0ca4e01, 1, 1, 0, 2008,
>>>> null,
>>>> null, null, 2008-05-27 16:34:37.979, null, true, true, null, null] at
>>>> ?.?(?:?)
>>>> 2008-05-27 16:34:38,026 [main] TRACE [EL Finest]:
>>>> UnitOfWork(24440876)--Execute query
>>>> InsertObjectQuery(se.lantmateriet.origo.domain.model.building.Comment@1b3278a)
>>>> at ?.?(?:?)
>>>> 2008-05-27 16:34:38,026 [main] TRACE [EL Finest]:
>>>> UnitOfWork(24440876)--Execute query WriteObjectQuery([Building@24c672
>>>> objectId = 25da2090-9b5e-4b8d-81c3-2197c0ca4e01, objectVersion = 1]) at
>>>> ?.?(?:?)
>>>> 2008-05-27 16:34:38,026 [main] DEBUG [EL Fine]:
>>>> ClientSession(30168161)--Connection(6881863)--INSERT INTO BUILDING (ID,
>>>> OBJECTID, OBJECTVERSION, VERSION, OBJECTSTATUS, CONSTRUCTIONYEAR,
>>>> HOUSENUMBER, EXTENSIONYEAR, REALPROPERTYKEY, VERSIONVALIDFROM,
>>>> VERSIONVALIDTO, NOADDRESSNEEDED, MAINBUILDING, PREFIX, IDENTITY) VALUES
>>>> (?,
>>>> ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
>>>>     bind => [1, 25da2090-9b5e-4b8d-81c3-2197c0ca4e01, 1, 1, 0, 2008,
>>>> null,
>>>> null, null, 2008-05-27 16:34:37.979, null, true, true, null, null] at
>>>> ?.?(?:?)
>>>> 2008-05-27 16:34:38,042 [main] DEBUG [EL Fine]:
>>>> ClientSession(30168161)--SELECT 1 FROM DUAL at ?.?(?:?)
>>>> 2008-05-27 16:34:38,042 [main] WARN  [EL Warning]:
>>>> UnitOfWork(24440876)--Local Exception Stack:
>>>> Exception [EclipseLink-4002] (Eclipse Persistence Services - 1.0 (Build
>>>> SNAPSHOT - 20080508)):
>>>> org.eclipse.persistence.exceptions.DatabaseException
>>>> Internal Exception: java.sql.SQLException: ORA-00001: brott mot unik
>>>> begränsning (MAGHEI.SYS_C00135499)
>>>>
>>>> --
>>>>
>>>> /Magnus Heino
> 
> 


-----
---
http://wiki.eclipse.org/User:James.sutherland.oracle.com James Sutherland 
http://www.eclipse.org/eclipselink/
 EclipseLink ,  http://www.oracle.com/technology/products/ias/toplink/
TopLink 
Wiki:  http://wiki.eclipse.org/EclipseLink EclipseLink , 
http://wiki.oracle.com/page/TopLink TopLink 
Forums:  http://forums.oracle.com/forums/forum.jspa?forumID=48 TopLink , 
http://www.nabble.com/EclipseLink-f26430.html EclipseLink 
Book:  http://en.wikibooks.org/wiki/Java_Persistence Java Persistence 
-- 
View this message in context: http://www.nabble.com/bi-directional-mapping-causes-two-inserts-tp17492842p17513791.html
Sent from the EclipseLink - Users mailing list archive at Nabble.com.



Back to the top