On 03/15/2010 09:18 PM, Paul Libbrecht wrote:
Does it mean that any transaction might actually live in the DB for a
long long time, beyond restarts of xwiki or the DB, and that a global
rollback?
=== The context ===
Object properties can have different logical types: StringClass,
BooleanClass, NumberClass, DBListClass, etc. This is the type that is
defined in the Class, and which specifies how values are presented to
the user (text input, radio boxes, textarea...).
They can also have different storage types: StringProperty,
LargeStringProperty, StringListProperty, IntegerProperty, LongProperty,
DateProperty, etc. This is the type that is stored in the database, and
which determines the column type and length.
There is no 1-1 relation between logical and storage types; depending on
the settings, one logical type can have up to four different storage
types, and different logical types can have the same storage type. For
example, the NumberClass, depending on the number type, can be stored as
one of: IntegerProperty, LongProperty, FloatProperty, DoubleProperty.
Another example, StaticListClass, DBListClass and DBTreeListClass, which
have configurable multipleSelect and relationalStorage, can be stored as:
- MS=1 and RS=1 => DBStringListProperty
- MS=1 and RS=0 => StringListProperty
- MS=0 => StringProperty
=== The problem ===
The problem is that the storage type for a property can be changed
dynamically, by changing its settings. Thus, a Number property that used
to be stored as a Float can become a Double. This means that the table
that stores this type of properties will change. But since this is a
weak type information, and the change happens outside Hibernate,
existing properties won't get moved from one table to the other.
Now, Hibernate is a little tricky, it normally doesn't expect this kind
of change, so it doesn't automatically detect that an entity moved
between tables. Yet, since in our mapping all properties are declared as
subclasses of a generic BaseProperty, a FloatProperty and its
corresponding DoubleProperty have the same identifier.
So, the process that causes problems is:
- Create a class with a Number property of type float
- Create an object for that class
- Change the property type to double
- Update the existing object
(the same thing for other type of changes)
Internally, Hibernate does this:
- Load Entity XYZ as a FloatProperty
- Store Entity XYZ as a DoubleProperty
Now, the two XYZ should be the same entity as far as Hibernate sees,
since they are both BaseProperty and have the same ID. But, since the
real type differs (FloatProperty vs DoubleProperty), they are two
distinct entities. Also, the initial type of the property is stored in
the BaseProperty table. Hibernate can't remove the FloatProperty, since
the new XYZ doesn't reference in any way the old XYZ. It also can't save
the new XYZ, since the stored type differs from the current type.
Now, two things happen:
- The FloatProperty is ghosted; you can't remove it from the database in
any normal way, just a low level SQL query can remove it.
- The DoubleProperty can't be saved because the FloatProperty is
blocking it.
The second issue prevents the property, along with its object, and along
with its document, from being saved anymore. The first issue means that
this problem will NEVER be fixed.
Now, to answer your question, there are two storages active in a normal
XWiki engine: the Hibernate storage, and the Cache storage. For
performance reasons, the cache returns the real object that is stored in
it. Depending on how the document is updated, the code might get the
document from the cache (storage), update it, and push it back to the
storage. Although the save fails, the object remains in the changed
state, and a reference to it was already present in the cache. Next time
the object is retrieved from the cache, the unsaved, but changed
document will be returned. This happens as long as the document is still
present in the cache.
=== The solution? ===
Theoretically, this bug was fixed (see
http://jira.xwiki.org/jira/browse/XWIKI-299 ) by manually moving
existing affected properties to the new correct type when a problematic
class change occurs. Why does it continue to happen, beats me. I'll have
to investigate again.
paul
Le 06-mars-10 à 00:21, Sergiu Dumitriu a écrit :
>>> org.hibernate.StaleObjectStateException: Row was updated or deleted
>>> by another transaction (or unsaved-value mapping was incorrect):
>>> [com.xpn.xwiki.objects.StringProperty#<?xml version="1.0"
>>> encoding="UTF-8"?>
>>> <eduLevelFine></eduLevelFine>
>>> ]
>>> at
>>> org
>>> .hibernate
>>> .persister
>>> .entity.AbstractEntityPersister.check(AbstractEntityPersister.java:
>>> 1765)
>>
>> I am puzzled and have no idea where to go except into SQL, which is
>> not really a safe thing.
>
> But I'm afraid it's the only real solution...
--
Sergiu Dumitriu
http://purl.org/net/sergiu/