After making some small changes in OpenJPA source code, I am able to call a
merge() method with an entity that's not detached. This allows our code that
works in JBoss also work in IBM Websphere.
Here is the change:
in the attach method of class VersionAttachStrategy
replace line 76:
boolean isNew = !broker.isDetached(pc);
with:
Object id = getDetachedObjectId(manager, toAttach);
boolean isNew = true;
if (id != null) isNew = false;
Do you see any issues with this change?
Thanks for the help!
jackson12 wrote:
>
> Kevin, Patrick,
>
> Any update on this?
>
> We want to quickly decide if we can switch to OpenJPA with IBM Websphere,
> this is currently our show stopper.
>
>
>
> jackson12 wrote:
>>
>> Kevin,
>>
>> What you described is exactly the problem.
>>
>> But unfortunately we don't have the luxury to use the approach you
>> proposed (find-->detach-->merge)
>> because our application is a struts based web application, and on the web
>> tier we have a different set of DTOs to hold the changes from end user
>> and currently we are only migrating our backend to use EJB3.
>>
>> so at someplaces, we have to copy the values from DTO to managed or
>> detached entities, but I don't know any "smarter copy" that can also
>> maintain the extra code in the managed or detached entity.
>>
>> This should be a very common scenarios. Please let me know if you or
>> Patrick have some other ideas.
>>
>> Thanks in advance!
>>
>>
>>
>> Kevin Sutter wrote:
>>>
>>> jackson12,
>>> I understand your problem. Since the Person object holds a nested
>>> Address
>>> object, when you use the copyProperties() method, the nested managed
>>> Address
>>> object gets overwritten with an unmanaged instance. Right?
>>>
>>> The copyProperties() method is kind of simple. It just copies attribute
>>> by
>>> attribute. Since it sees an Address attribute, it just copies the new
>>> Address attribute right on top of the managed one. The problem here is
>>> that
>>> the managed instance of Address has OpenJPA state associated with it.
>>> When
>>> the copyProperties() is invoked, we lose all of that state data (and
>>> code)
>>> due to the unmanaged Address object. Hibernate's method of keeping
>>> track of
>>> object state must be different from the way that OpenJPA does it.
>>>
>>> What it sounds like you need is a smarter copyProperties() method. One
>>> that
>>> will copy the simple property values, but then traverse into the nested
>>> managed objects to do it's own copyProperties(). But, then if these
>>> nested
>>> objects have additional nested managed objects, you run into the same
>>> complication.
>>>
>>> How about using the merge() operation to accomplish this task? Instead
>>> of
>>> just creating new unmanaged instances of your Person objects, do a find
>>> operation to get them managed by your persistence context. Then, detach
>>> the
>>> entities and do whatever updates you want to these detached instances.
>>> When
>>> you are ready to copy them back in to the persistence context, call the
>>> merge() method and let the changes merge back into the persistence
>>> context.
>>> This would seem to be along the lines of processing that you are looking
>>> for, but from a different angle.
>>>
>>> I'll copy Patrick on this reply just to see if he has some other ideas.
>>>
>>> Thanks,
>>> Kevin
>>>
>>>
>>> On Dec 11, 2007 8:02 AM, jackson12 <[EMAIL PROTECTED]> wrote:
>>>
>>>>
>>>> Kevin,
>>>>
>>>> Thanks again for the clarification.
>>>>
>>>> Here is the scenario for the nested properties.
>>>>
>>>> Suppose I have an entity Person that holds a nested entity Address.
>>>> Assume
>>>> I
>>>> use find() method to load
>>>> the Person entity with Address. Now in order to apply the changes, we
>>>> normally use
>>>>
>>>> PropertyUtils.copyProperties(managedPerson, inputPerson);
>>>>
>>>> or
>>>> BeanUtils.copyProperties(managedPerson, inputPerson);
>>>>
>>>> but these copyProperties method will make the nested Address entity
>>>> non-managed (because the Address reference will be replaced by , and
>>>> hence
>>>> OpenJPA will throw exception when I call the merge() method later. In
>>>> order
>>>> to bypass this, we have to copy field by field from inputPerson to
>>>> managedPerson including the nested entity. This makes the code very
>>>> error
>>>> prone and ugly.
>>>>
>>>> JBoss's implementation does not require the inputPerson to be managed
>>>> or
>>>> detached, I know this maybe beyond JPA1.0 spec, but it is really very
>>>> helpful.
>>>>
>>>> Do I misunderstand anything?
>>>>
>>>> thanks a lot
>>>>
>>>>
>>>>
>>>> Kevin Sutter wrote:
>>>> >
>>>> > jackson12,
>>>> > If I am understanding your scenario correctly, your userProfile input
>>>> > parameter is an unmanaged entity, but the key for this entity does
>>>> already
>>>> > exist in the database. So, when you attempt to do the merge()
>>>> processing
>>>> > (and subsequent transaction commit), you are getting a "duplicate
>>>> key"
>>>> > exception from Oracle.
>>>> >
>>>> > Calling merge() with a new unmanaged entity acts like a persist()
>>>> call
>>>> and
>>>> > adds it to the persistence context. The persist() method is intended
>>>> for
>>>> > new entities that do not currently exist in the database.
>>>> >
>>>> > So, on the surface, your scenario seems to be acting as I would
>>>> expect
>>>> > with
>>>> > any JPA implementation. But, you have indicated that JBoss'
>>>> Hibernate
>>>> is
>>>> > processing as you had hoped. Based on my understanding of the spec
>>>> and
>>>> > your
>>>> > scenario, I think you are getting lucky with Hibernate.
>>>> >
>>>> > I would suggest doing the find() operation first to properly load the
>>>> > entity
>>>> > into the persistence context and then do your updates. You can
>>>> always
>>>> > detach this entity from the persistence context, if necessary, for
>>>> other
>>>> > processing and then merge it back in later. Since the merge() would
>>>> be
>>>> > operating on a known, detached entity, an update operation will be
>>>> > performed
>>>> > instead of the insert.
>>>> >
>>>> > A few other observations... Remember that the merge() operation
>>>> returns
>>>> > the
>>>> > managed entity as a return value. The original entity that you
>>>> passed
>>>> > into
>>>> > the merge() method is not managed. So, if you want any state changes
>>>> to
>>>> > the
>>>> > entity to be properly maintained and persisted, you need to use the
>>>> > returned
>>>> > managed instance of the entity from the merge() operation.
>>>> >
>>>> > You also mentioned how the nested properties are not managed. This
>>>> may
>>>> be
>>>> > related to the above observation. Another possibility is that you
>>>> need
>>>> to
>>>> > declare whether you want the merge processing to be cascaded to the
>>>> > objects
>>>> > pointed to by the entity. You would specify this on the relationship
>>>> > annotation via the cascade=CascadeType.MERGE element. You haven't
>>>> > provided
>>>> > the complete entity definition, so I'm not exactly sure how you have
>>>> these
>>>> > entity types defined.
>>>> >
>>>> > Hope this information helps.
>>>> >
>>>> > Good luck,
>>>> > Kevin
>>>> >
>>>> > On Dec 7, 2007 11:50 AM, jackson12 <[EMAIL PROTECTED]> wrote:
>>>> >
>>>> >>
>>>> >> Thanks Kevin for your prompt response.
>>>> >> Here is a simple use case we have:
>>>> >> within an EJB , we have the following method:
>>>> >>
>>>> >> public UserProfile updateUserProfile(UserProfile userProfile) {
>>>> >> return getProfileDAO().update(userProfile);
>>>> >> }
>>>> >>
>>>> >> when this method is called, the input parameter has all of the
>>>> values
>>>> >> including the key, but we got a runtime exception because it's
>>>> trying
>>>> to
>>>> >> insert userProfile into database.
>>>> >>
>>>> >> org.apache.openjpa.persistence.PersistenceException: ORA-00001:
>>>> unique
>>>> >> constraint (VPDNGDITR17.PK_SECURITY_USER_PROFILE) violated
>>>> FailedObject:
>>>> >> prepstmnt 1645109774 INSERT INTO SECURITY_USER_PROFILE (OID,
>>>> >> MODIFIED_BY_USER, ........
>>>> >>
>>>> >> After we change the code to the following, it works fine:
>>>> >> public UserProfile updateUserProfile(UserProfile userProfile)
>>>> {
>>>> >> UserProfileDAO profileDAO = getProfileDao();
>>>> >> UserProfile tmpUserProfile =
>>>> >> profileDAO.getUserProfile(userProfile.getName());
>>>> >> PropertyUtils.copyProperties(tmpUserProfile,
>>>> userProfile);
>>>> >> return getProfileDao().update(tmpUserProfile);
>>>> >> }
>>>> >>
>>>> >> Here is the update method of ProfileDAO:
>>>> >>
>>>> >> public T update(T entity)
>>>> >> {
>>>> >> getEntityManager().merge(entity);
>>>> >> }
>>>> >>
>>>> >> When the input parameter has nested object, even the above approach
>>>> won't
>>>> >> work any more, because PropertyUtils.copyProperties will make the
>>>> nested
>>>> >> object not managed anymore.
>>>> >>
>>>> >> Thanks a lot
>>>> >>
>>>> >>
>>>> >> Kevin Sutter wrote:
>>>> >> >
>>>> >> > jackson12,
>>>> >> > Could you be more specific with your example? We have various
>>>> >> testcases
>>>> >> > that do this exact process of merging in non-managed entities
>>>> (without
>>>> >> > first
>>>> >> > retrieving the entity from the DB). Could you further explain
>>>> your
>>>> >> test
>>>> >> > scenario and what results you are getting?
>>>> >> >
>>>> >> > Thanks,
>>>> >> > Kevin
>>>> >> >
>>>> >> > On Dec 7, 2007 11:12 AM, jackson12 <[EMAIL PROTECTED]> wrote:
>>>> >> >
>>>> >> >>
>>>> >> >> Hi, We are trying to migrate our JBoss EJB3 application to IBM
>>>> >> websphere
>>>> >> >> with
>>>> >> >> OpenJPA. We noticed in OpenJPA, the merge method does not work
>>>> with
>>>> >> >> non-managed entity. You have to retrieve the entity from DB, make
>>>> some
>>>> >> >> changes and then call merge. But with JBoss's implementation, you
>>>> >> don't
>>>> >> >> have
>>>> >> >> to retrieve the entity from DB first. the merge method works fine
>>>> for
>>>> >> >> non-managed entity.
>>>> >> >>
>>>> >> >> Is there anyway for OpenJPA to work the same way as JBoss's JPA
>>>> >> >> implementation on this part? or is there a way in OpenJPA to make
>>>> a
>>>> >> >> non-managed entity managed?
>>>> >> >>
>>>> >> >> thanks in advance
>>>> >> >> --
>>>> >> >> View this message in context:
>>>> >> >>
>>>> >>
>>>> http://www.nabble.com/merge-only-works-with-managed-entity-tf4963245.html#a14216438
>>>> >> >> Sent from the OpenJPA Developers mailing list archive at
>>>> Nabble.com.
>>>> >> >>
>>>> >> >>
>>>> >> >
>>>> >> >
>>>> >>
>>>> >> --
>>>> >> View this message in context:
>>>> >>
>>>> http://www.nabble.com/merge-only-works-with-managed-entity-tf4963245.html#a14217307
>>>> >> Sent from the OpenJPA Developers mailing list archive at Nabble.com.
>>>> >>
>>>> >>
>>>> >
>>>> >
>>>>
>>>> --
>>>> View this message in context:
>>>> http://www.nabble.com/merge-only-works-with-managed-entity-tp14216438p14274498.html
>>>> Sent from the OpenJPA Developers mailing list archive at Nabble.com.
>>>>
>>>>
>>>
>>>
>>
>>
>
>
--
View this message in context:
http://www.nabble.com/merge-only-works-with-managed-entity-tp14216438p14339820.html
Sent from the OpenJPA Developers mailing list archive at Nabble.com.