All, I posted the message below a few weeks ago, and I've just now taken another look.
I found a bug in my code that was causing the field change to 'null' to be ignored! (Imagine that :^) Sloppiness with my test rig's configuration made me think the problem existed ONLY with load-time enhancement. This just wasn't true. After fixing the bug, the application runs correctly with EITHER compile-time or load-time class enhancement, as expected. So, for the record, I'm now having no problem at all with OpenJPA. Thanks to the OpenJPA team for a quality product! --Rich -----Original Message----- From: Landers, Richard Sent: Wednesday, December 26, 2007 5:28 PM To: '[email protected]' Subject: RE: Does merge() handle deletes? Mike, Thank you for your timely response! Over the last couple of days I was finally able to spend the necessary time to study your example and investigate further. Here's what I found... Summary ======= * I am investigating a bug where a field of an entity, when set to 'null' goes unnoticed by the merge() operation. * When I set the field to any value but 'null' the merge operation updates the entity's record as expected. * When using load-time enhancement and deploying as part of a Web application in Websphere 6.1, I see the bug. * When using compile-time enhancement in the same situation, I do not. * When using load-time enhancement but running in a stand-alone JVM, I do not. * My environment is OpenJPA 1.1.1-SNAPSHOT and Spring 2.5 on Websphere 6.1. Details ======= Just so we were both on the same page, I upgraded to OpenJPA 1.1.1-SNAPSHOT (from 1.0.1), as in your example. -- There was no change in the behavior I'm seeing. I'm not serializing my entities, everything is within one JVM. You're showing explicit transaction control in your example. I'm using Spring's "@Transactional" declarative transactions. So, to make sure it wasn't my Spring setup, I created an example that uses my full Spring setup in a stand-alone Java program. Here's the main() routine: -------------- public static void main(String[] args) throws Exception { log.info("Boot up the Spring applicationContext..."); ServiceLocator services = new ServiceLocator(); log.info("Retrieve @Transactional services needed..."); OffenseCategoryService ocs = services.getOffenseCategoryService(); UserService us = services.getUserService(); AuditRevisionService ars = services.getAuditRevisionService(); log.info("Retrieve the '14-111n' OffenseCategory..."); OffenseCategory category = ocs.findByName("14-111n"); log.info("category.acdCode = " + category.getAcdCode()); log.info("Set the OffenseCategory's ACD Code field to null..."); category.setAcdCode(null); log.info("Merge the change back into the database..."); User user = us.login("landers.richard", "landers.richard"); AuditRevision ar = ars.createAuditRevision(user, "MergeDeleteTest"); ocs.merge(category, ar); log.info("Test complete."); } --------------- Sorry the example is full of jargon! Notes: 1. My ServiceLocator brings up the Spring application context. 2. An OffenseCategoryService is located, along with a UserService and AuditRevisionService. 3. A particular OffenseCategory (entity) is retrieved by name. This method is annotated @Transactional, which means by the time it returns it's value the transaction has ended, and the entity returned is therefore detached. 4. The OffenseCategory's "ACD Code" field is set to null. 5. The OffenseCategory is merged with the database. The stuff about the User and AuditRevision are necessary for our system of maintaining an audit trail. Under the hood there's an EntityListener that sets the auditRevision field on any entity OpenJPA is inserting or updating. ---------------- When I run this class, everything works as expected. The 'null' is noticed by OpenJPA and the OffenseCategory is updated in the database correctly. I conclude from this that I'm using OpenJPA and Spring's @Transactional correctly. ----------------- However, when I run code that's substantially the same as part of my web-app inside Websphere I still get the error I originally described. Reading the documentation related to "DetachState", which you noted may need to be set, I noticed that merging seems to rely on the DetachedStateField. And, further, that DetachedStateField seems to be added when OpenJPA "enhances" my classes. I discovered that I did not have a "LoadTimeWeaver" enabled in my Spring setup of OpenJPA. Buried in my logs there was a message indicating that load-time class enhancement was not available. I specified a LoadTimeWeaver in the Spring JPA section and added the corresponding "-javaagent: spring-agent.jar" JVM command-line argument to Websphere's JVM. Spring's log messages now indicate the LoadTimeWeaver is available. No change in behavior, though. Still the OffenseCategory's ACD Code field, when set to 'null', went unnoticed at merge() time. It's important to note that setting the ACD Code to anything but 'null' works every time -- merge() notices the change and updates the OffenseCategory record. ----------------- At this point in the investigation, I am pretty sure there's a bug. In an attempt to narrow it down further, I switched from load-time to compile-time class enhancement using the "org.apache.openjpa.ant.PCEnhancerTask". And, miraculously, the bug disappeared. Now I'm sure there's a bug. ------------------ I can try to come up with a smaller, more isolated, example that demonstrates the bug, but I'm unsure what to try next. Can you, or anyone on the list, think of any way to narrow it down further? --Rich -----Original Message----- From: Michael Dick [mailto:[EMAIL PROTECTED] Sent: Thursday, December 20, 2007 5:02 PM To: [email protected] Subject: Re: Does merge() handle deletes? Hi Rich, Are you serializing/deserializing the entity? If so you'll need to add the following property to persistence.xml : <property name="openjpa.DetachState" value="fgs(DetachedStateField=true)"/> I have a similar set up that is working for me on OpenJPA 1.1.0-SNAPSHOT : Customer c = em.find(Customer.class, id); em.clear(); // detach assertNotNull( c.getAddress()); c.setAddress(null); em.getTransaction().begin(); Customer cPrime = em.merge(c); em.getTransaction().commit(); assertNull(c.getAddress()); assertNull(cPrime.getAddress()); c = em.find(Customer.class, id); assertNull(c.getAddress ()); Relevant entity code : public class Customer extends TestEntity implements Serializable { /** * */ private static final long serialVersionUID = -5752300027182374895L; @GeneratedValue(strategy = GenerationType.AUTO) @Id Long id; @Version @Column(name = "VERSN") protected int version; @ManyToOne(targetEntity = Address.class , cascade = CascadeType.ALL) Address address; Which version of OpenJPA are you using? -Mike On Dec 19, 2007 2:34 PM, Landers, Richard < [EMAIL PROTECTED]> wrote: > Hello all, > > I'm having trouble using EntityManger.merge () operation... > > I have a entity A that holds a many-to-one reference to another, B. On > entity A, the relationship is annotated like this: > > @ManyToOne(fetch=FetchType.EAGER, cascade=CascadeType.MERGE) > > At a certain point in processing, I've got a detached instance of A > referencing an instance of B. > > I want to dissociate A from any instance of B. So I call: > > a.setB(null); > > while A is detached, and then call: > > merge(a) > > I thought the merge() operation would discover the change and update A > in the database, but it does not. > > Is my mental model wrong? > > Do I have to (or *can* I) mark A as "dirty" to get OpenJPA to notice it? > > Thanks in advance, > > --Rich > >
