Title: Transaction problem with referenced persistent objects

I have a system using long transactions. In this system, I have a class representing an Order that references a class representing an OrderStatus. In the database, this is achieved with a foreign key relationship; in the Castor mapping, thusly:

        <field name="orderStatus" type="com.gene.apis.fulfillment.OrderStatus" required="true">
            <sql name="ORD_ORDERSTATUSID"/>
        </field>

I've created methods to retrieve, add, and update objects that are very self-contained. They begin a transaction, do their work, close the transaction, and return an object or collection of objects if the method is for a query. This means that when my client code wants to deal with an Order an change its status, it goes through the following steps:

1. Retrieve Order - this gives me an instance of the Order after having closed the transaction that retrieved it. I have a cache set up for Orders, so it can be updated later

2. Retrieve the correct instance of OrderStatus from the database to describe the new state - same as above in terms of the transaction which retrieves the OrderStatus being quickly closed; however, in this case I do NOT have a cache defined and never expect to update these objects (they are defined as read-only)

3. Set the orderStatus field on my Order with the newly retrieved OrderStatus

4. Call an update method to update the Order. It is included here in its entirety:
    public void updateOrder(Order pOrder) {
        Database db = getDB();
        try {
            db.begin();
            pOrder.setDateUpdated(new Date());
            pOrder.setUpdatedBy("FulfillmentManager.updateOrder");
            pOrder.setOrderStatus((OrderStatus)db.load(OrderStatus.class, new Long(pOrder.getOrderStatus().getId())));
            db.update(pOrder);
            db.commit();
        }
        catch (PersistenceException e) {
            StringBuffer msg = new StringBuffer(CONFIG.getString("error.update.order"));
            msg.append(pOrder.getId());
            LOGGER.error(msg.toString(), e);
            throw new RuntimeException(msg.toString() + " " + e.getMessage());
        }
        finally {
            CastorUtils.closeDb(db);
        }
    }

I'm getting the following exception:
org.exolab.castor.jdo.PersistenceException: Object, [EMAIL PROTECTED], links to another object, [EMAIL PROTECTED] that is not loaded/updated/created in this transaction

        at org.exolab.castor.persist.ClassMolder.preStore(ClassMolder.java:1290)
        at org.exolab.castor.persist.LockEngine.preStore(LockEngine.java:723)
        at org.exolab.castor.persist.TransactionContext.prepare(TransactionContext.java:1462)
        at org.exolab.castor.jdo.engine.DatabaseImpl.commit(DatabaseImpl.java:528)
        at com.gene.castor.fulfillment.FulfillmentManager.updateOrder(FulfillmentManager.java:179)
        at com.gene.apis.fulfillment.batchtrans.BatchManager.updateOrders(BatchManager.java:217)
        at com.gene.apis.fulfillment.batchtrans.BatchManager.retrieveConfirmationFile(BatchManager.java:183)
        at jsp_servlet._fulfillment._admin.__DELETEME._jspService(__DELETEME.java:93)
        at weblogic.servlet.jsp.JspBase.service(JspBase.java:27)
        at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:263)
        at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:302)
        at weblogic.servlet.internal.ServletStubImpl.invokeServlet(ServletStubImpl.java:200)
        at weblogic.servlet.internal.WebAppServletContext.invokeServlet(WebAppServletContext.java:2390)
        at weblogic.servlet.internal.ServletRequestImpl.execute(ServletRequestImpl.java:1959)
        at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:137)
        at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:120)

Now first of all, I consider it a small design flaw that Castor does not permit me to change the value of a referenced field that is neither dependent nor even writable. In reality, consider that all I'm doing is changing the value of a single long field on my ORDERS table. At worst, I'd expect to get an integrity constraint message from the underlying database on the rare cases where the object no longer existed.

My feelings on the behavior aside, in the code snippet above I am accounting for the behavior as it exists by re-retrieving the OrderStatus object from the database and setting the orderStatus field again, all within the same transaction scope. I had a similar problem earlier and this work-around resolved the issue. However in this case, I seem to be having no luck.

Am I doing something wrong here, is there another way of accomplishing what I want, or should I start diving through the ClassMolder to find either a bug or a new special case that hasn't been tested for yet?

I'm going to request that any answers to this question be mailed to me as well as posted to the development list, but will nonetheless monitor the list as well.

Cheers!
Richard Porter
Software Engineer
Jel, Inc.

"Man is still the best computer...that can be mass produced with unskilled labor."
Werner Von Braun

Reply via email to