Bugs item #837092, was opened at 2003-11-06 12:06
Message generated for change (Comment added) made by loubyansky
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=376685&aid=837092&group_id=22866

Category: JBossCMP
Group: v3.2
>Status: Closed
>Resolution: Fixed
Priority: 5
Submitted By: Adrian Price (adrianprice)
Assigned to: Alexey Loubyansky (loubyansky)
Summary: CMP Field Update Optimization is Broken

Initial Comment:
The mechanism for optimizing updates to CMP fields is
broken, because it makes the erroneous assumption that
the value being stored is immutable.

o.j.e.p.c.j.b.JDBCFieldBridge.setInstanceValue() or
thereabouts ignores the update if
oldValue().equals(newValue).  Whilst this works for
primitives (or rather their immutable java.lang.*
wrappers), it breaks completely for mutable
serializable objects in a BLOB, because if one
retrieves such an object from a CMP field, modifies it
then updates the CMP field with the same, modified
object, that change will never get flushed to the
database and the update will have been ignored.

class MyBean implements Serializable {
    int x, y;
    // getters & setters...

    // The correct implementation of equals() for this
class.
    public boolean equals(Object o) {
        if (!(o instanceof MyBean))
            return false;
        MyBean that = (MyBean)o;
        return this == that || this.x = that.x &&
this.y = that.y;
    }
/*
    // Horrid kludge necessary to force JBossCMP to
store me :(
    public boolean equals(Object o) {
        return false;
    }
*/
}

MyEntityLocal entity =
MyEntityLocalHome.findByPrimaryKey(pk);

// Retrieve bean from CMP field and modify it.
MyBean bean entity.getBean(); // CMP field getter
bean.x = 666;
bean.y = 777;

// Store modified bean. Setting field to null first
forces JBossCMP 
// to recognize the update in 3.0.4 and 3.2.1, but not
3.2.2.
entity.setBean(null); // (shouldn't be necessary anyway)
entity.setBean(bean);

// TXN commit...

// asserts fail, bean not persisted.  Uncommenting
Horrid Kludge forces JBossCMP v. 3.2.2 to recognize &
persist the update.
bean entity.getBean();
assert(bean.x == 666 && bean.y == 777);


----------------------------------------------------------------------

>Comment By: Alexey Loubyansky (loubyansky)
Date: 2003-11-18 01:50

Message:
Logged In: YES 
user_id=543482

Fixed in Branch_3_2 and HEAD.

By default, CMP fields of primitive types, their wrappers,
java.lang.String and audit and version fields of type
java.util.Date rely on equals() implementation when checked
for dirty state and have check-dirty-after-get=false. Other
fields, by default, rely on invalid-unless-null dirty
checking strategy, i.e. if the field was null and a new
value is null it is considered valid, otherwise - invalid;
and have check-dirty-after-get=true.

----------------------------------------------------------------------

Comment By: Adrian Price (adrianprice)
Date: 2003-11-07 21:45

Message:
Logged In: YES 
user_id=580837

I still don't believe equals() is a reliable way to determine 
whether the field value is dirty, because by definition (this == 
this) is always true.  The case I'm describing is where you 
pass the exact same object instance to the cmp setter as 
was initially retrieved from the cmp field getter.  In that case 
equals() will always return true, even if the object is actually 
dirty.  The only way round that would be to use some dirty 
trick to clone the original value in order to remember what it 
was.  If the object isn't clonable you'd have to deserialize it 
twice in order to keep a pristine copy, but that's an 
expensive operation.  I don't believe JBoss actually does 
clone the initial value because if it did, clonedValue.equals
(newValue) would return false if the object in question did 
not override Object.equals(), which simply checks identity 
(this == that).

I think the correct solution is to treat the field as dirty if 
oldFieldValue == newFieldValue.  You could add some smarts 
to check the object's class and ignore the immutable 
java.lang.* objects.

I've looked at the source for JDBCTypeFactory, and I'm 
convinced the flaw is in the implementation of the SIMPLE 
anonymous inner class that implements 
CMPFieldStateFactory.  I think the isStateValid() method 
should read:

public boolean isStateValid(Object state, Object fieldValue)
{
    // Must treat identical references as dirty,
    // because equals() won't tell you the object
    // has changed.
    return state == null ? fieldValue == null : state != 
fieldValue && state.equals(fieldValue);
}

----------------------------------------------------------------------

Comment By: Alexey Loubyansky (loubyansky)
Date: 2003-11-07 19:53

Message:
Logged In: YES 
user_id=543482

That is not completely correct. Check this link, for example
https://sourceforge.net/tracker/?func=detail&aid=816762&group_id=22866&atid=381174.

Current implementation relies on equals() implementation.
So, that is not the CMP engine who assumes your object is
not dirty. But you implementing equals() that way.

I can expose the mechanism that is used for Map, List, Set,
array to application developer and offer you to provide an
implementation of the following interface for MyBean to
solve the problem. 

public interface CMPFieldStateFactory
{
   /**
    * Calculates and returns an object that represents the
state of the field value.
    * The states produced by this method will be used to
check whether the field
    * is dirty at commit time.
    *
    * @param fieldValue  field's value.
    * @return an object representing the field's state.
    */
   Object getFieldState(Object fieldValue);

   /**
    * Checks whether the field's state <code>state</code>
    * is equal to the field value's state (possibly,
calculated with
    * the <code>getFieldState()</code> method).
    *
    * @param state  the state to compare with field value's
state.
    * @param fieldValue  field's value, the state of which
will be compared
    *                    with <code>state</code>.
    * @return  true if <code>state</code> equals to
<code>fieldValue</code>'s state.
    */
   boolean isStateValid(Object state, Object fieldValue);
}


----------------------------------------------------------------------

Comment By: Adrian Price (adrianprice)
Date: 2003-11-06 12:10

Message:
Logged In: YES 
user_id=580837

penultimate line should read:
bean = entity.getBean();

----------------------------------------------------------------------

You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=376685&aid=837092&group_id=22866


-------------------------------------------------------
This SF. Net email is sponsored by: GoToMyPC
GoToMyPC is the fast, easy and secure way to access your computer from
any Web browser or wireless device. Click here to Try it Free!
https://www.gotomypc.com/tr/OSDN/AW/Q4_2003/t/g22lp?Target=mm/g22lp.tmpl
_______________________________________________
JBoss-Development mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/jboss-development

Reply via email to