Details
-
Type:
New Feature
-
Status:
Closed
-
Resolution: Fixed
-
Affects Version/s: None
-
Fix Version/s: 1.4
-
Component/s: None
-
Labels:None
Description
A converter/mapper for supporting hibernate collections.
-
- HibernateCollectionConverter.java
- 14/Jun/05 7:11 AM
- 4 kB
- Costin Leau
-
- HibernateCollectionConverterNEW.java
- 09/Oct/09 1:29 AM
- 4 kB
- Erich S.
-
- HibernateCollectionsMapper.java
- 14/Jun/05 7:11 AM
- 3 kB
- Costin Leau
-
- HibernateCollectionsMapperNEW.java
- 09/Oct/09 1:29 AM
- 4 kB
- Erich S.
-
- HibernateMapper.java
- 24/Jan/11 5:51 AM
- 1 kB
- Tim De Meyer
-
- HibernateMapperNEW.java
- 09/Oct/09 1:29 AM
- 2 kB
- Erich S.
-
- HibernateProxyConverterNEW.java
- 09/Oct/09 1:29 AM
- 1 kB
- Erich S.
-
- HibernateProxyConverterNEW2.java
- 29/Aug/10 7:42 PM
- 1 kB
- Jaime Metcher
-
- HibernateProxyXPathMarshaller.java
- 07/Sep/10 4:39 PM
- 1.0 kB
- Jaime Metcher
-
- xstream_hib.tar.gz
- 08/Apr/11 1:15 AM
- 24 kB
- Jaime Metcher
-
- XStreamMarshallingStrategy.java
- 07/Sep/10 4:39 PM
- 1.0 kB
- Jaime Metcher
Issue Links
Activity
I found this more comprehensive solution only after I'd written my own. I'd love for this to be included in the main xstream distro, it's probably going to be a very common use case.
Question while I'm at it - anyone done any work on handling proxy classes decorated by cglib by hibernate? The simple solution would be to simply disable them, but lazy loading of fields is occasionally a big big win, so I'm reluctant to go down that road.
I've seen your comment only now - I didn't had any watches placed on. I'm glad you like the solution.
I have done some work on decorated classes some time ago and I can donate some code if there is such a need. If you are in need of a quick fix make a mapper that removes the signature of CGLIB.
After struggling with the HB api I stopped working on the tests. PersistentCollections & Co. are highly connected to HB API and to me it seems almost impossible (you have to create the whole internal HB hierarchy with dummy objects that in the end do not work).
I've added a mapper for CGLIB enhanced classes: http://jira.codehaus.org/browse/XSTR-238
Also see http://forum.hibernate.org/viewtopic.php?t=939007&highlight=xstream for apparent error in the code (have checked it myself).
I just reopen the issue, since the HIbernate problem is not solved, but asked at a constant rate. So the issue will not get lost, even if the donated code might not solve any issue.
The following line of code might be incorrect in the HibernateCollectionConverter
// the set is returned as a map by Hibernate (unclear why exactly)
if (source instanceof PersistentSet)
((HashMap)collection).entrySet() will return a set of Map.Entry and not the value which I think is what we want.
Maybe it should be something like:
if (source instanceof PersistentSet)
{
Set newSet = new HashSet();
Set mapEntrySet = ((HashMap) collection).entrySet();
for (Object o : mapEntrySet)
}
Another approach here is to integrate functionality provided by beanlib to make this problem go away.
http://beanlib.sourceforge.net/
Hibernate3BeanReplicator().deepCopy() handles two issues with hibernate:
- It de-proxies all the (lazy) objects
- It replaces hibernate specific collection types with the Java built in versions.
Of course this deep copy is not very efficient if you do it in addition to XStream's traversal of the object. Ideally this would be handled internally as XStream is traversing.
Other than converting the Hibernate collection type as Java standard collection types, is there any other benefit of this converter (And why should one need to do that) ? If I'm not wrong, It does not facilitate serializing hibernate lazy loaded classes.
Hello all,
on the work of Costin and others we improved some things to get Hibernate 3 objects (incl. Lazy-Loading) sererialised - and back again. With the following solution we get succeeded:
public String exportToXML(Object obj) {
if (obj == null)
return null;
XStream xstream = new XStream();
final ClassMapper cm = xstream.getClassMapper();
xstream = new XStream() {
protected MapperWrapper wrapMapper(MapperWrapper next)
protected Mapper buildMapper()
{ return new HibernateCollectionsMapperNEW(cm); }};
xstream.registerConverter(new HibernateCollectionConverterNEW(xstream
.getConverterLookup()));
xstream.registerConverter(new HibernateProxyConverterNEW(xstream
.getMapper(), new PureJavaReflectionProvider()),
XStream.PRIORITY_VERY_HIGH);
return xstream.toXML(obj);
}
public Object importXML(String xmlString)
{ if (xmlString == null || xmlString.length() <= 0) return null; /* * No Hibernate specific things to do: simple POJOS are to be importet */ XStream xstream = new XStream(); return xstream.fromXML(xmlString); }Files with Suffix ...NEW are for my suggested solution
Files with Suffix ...NEW are for my suggested solution
This last solution works well directly with hibernate objects but if you use something like List<HibernateObject> (or HibernateObject[] ...) after the first object in the group it fails to dereference deep relations. Anyway you could parse the objects one by one.
It also uses deprecated Classes but it is easy to fix using:
Mapper instead of ClassMapper
AbstractXmlFriendlyMapper instead of XmlFriendlyMapper
escapeFieldName(replaceClasses(javaName)) instead of super.mapNameToXML(replaceClasses(javaName))
Just a minor tweak to HibernateProxyConverterNEW to make it look up a converter for the proxied class, rather than just call super.marshal(). See HibernateProxyConverterNEW2 ![]()
Without this you just get basic reflection converter behaviour, meaning any registered converters are ignored. It's a little worse than that, actually, as registered converters may or may not be invoked depending on whether you happen to have a proxied or non-proxied instance at the time.
Actually, the HibernateProxyConverterNEW2 approach still doesn't cut it. It fails to cope with the scenario where the one object appears in the object graph in both proxied and unproxied form. It doesn't recognise them as the same object, so you get two instances in the XML.
See the attached XStreamMarshallingStrategy and HibernateProxyXPathMarshaller for a possible solution. Then you just need:
xstream.setMarshallingStrategy(new XStreamMarshallingStrategy(XStreamMarshallingStrategy.RELATIVE));
Note that this strategy is only required for toXML, not fromXML.
Does anyone have an example of using this and which files are needed. I am unclear from reading the comments what I actually need to implement in order to try this.
@David,
Start with the attachements added by Erich S. and look at his comment from 9/Oct/09 to see how to use them. Then, if you care about the issues I mentioned you can have a look at the changes I made. All you need to do is use HibernateProxyConverterNEW2 instead of HibernateProxyConverterNEW, and add the line from my previous comment straight after you instanstiate xstream.
This code is working like a charm for me.
My only problem is that text data (string columns defined as LOB in hibernate annotations... CLOB in oracle) are not appearing. Any ideas/pointers on how to implement a mapping on this type of column?
Guys,
any idea on how to make class aliasing work for lazy loaded objects?
We want to alias my.package.MyEntity to my.other.package.MyOtherEntity.
We do this by calling xStream.aliasType() or xStream.alias().
However, it doesn't do the trick for lazy loaded objects (appears as MyEntity_$$_javassist_42 in eclipse debugger)
XStream initialization is based on code from Erich S. earlier in this thread.
Hi,
I found out that the culprit is actually Erich's HibernateMapper.
As you can see in the implementation, the Mapper chain gets broken by directly returning a value, instead of handing it off to the next Mapper in the chain.
Therefore XStreams ClassAliasingMapper, which comes later in the chain, doesn't get a chance to do any aliasing.
I'll attach the fix in a few minutes.
By the way, Costin, Erich and Jaime, I'll take this opportunity to thank you guys for the contributions you've made.
This has really been a life saver!
t.
Guys, normally I stop conversation like this in JIRA, because it is a bug tracker and not a help forum. However, I admin that the Hibernate problem is a delicate and a long standing issue and is a somewhat special case.
What is the basic problem of not adding this support until now:
1/ XStream is currently delivered as a single library and Hibernate adds a lot of new dependencies
2/ No Hibernate knowledge on our side
To address the first topic, we'd add Hibernate support as separate artifact that delivers all stuff necessary for the integration. However, the second problem is more serious. If it is officially delivered with XStream, we have to maintain it. While you all have put quite some effort into this integration, nobody delivered unit tests so far. To enable maintenace from our side this is an absolute requirement. The unit tests have to cover the typical Hibernate scenarios (incl. collections and lazy resp. non-lazy loading). If a pom is provided on top the chance is very high to get this into XStream 1.4.
Hi all,
Thanks all for this great solution !
I was playing around with this fix and I was having the classic hibernate exceptions telling there is no session attached any more.
The culprit was in HibernateCollectionConverterNEW.java when at the beginning of the marshal method, col.forceInitialization() is called.
I added a simple fix which will write null if the collection is null and won't call col.forceInitialization() is the session is null or not connected:
if (source instanceof AbstractPersistentCollection) {
AbstractPersistentCollection col = (AbstractPersistentCollection) source;
if ((col.getSession() != null) && col.getSession().isConnected())
collection = col.getStoredSnapshot();
} else if (source instanceof PersistentCollection)
if (collection == null)
{ NULL_CONVERTER.marshal(collection, writer, context); return; }...
xstream_hib.tar.gz (new version 29/Mar) is a tarball containing a maven/eclipse project bringing together some of the code people have been contributing to this issue, along with some minimal unit tests. The eclipse project is not a standalone alternative to maven - it relies on all the dependencies being in the maven repo.
This is intended to be a starting point so that future tweaks have a ready-made place to add unit tests. I have not come within light years of testing all of XStream's functionality in a Hibernate context, or within megaparsecs of testing all of Hibernate in an XStream context.
In particular:
I haven't addressed Jake's concerns about unflushed changes not being serialized.
I haven't done anything with LOB's (sorry Kevin).
I have included a test for Tim's aliasing concerns, but I removed a lot of Tim's changes because I didn't understand what they did and they weren't needed for that specific test.
I wasn't able to reproduce my own issues with the same object appearing in both proxied and unproxied form.
I haven't included Nicolas' code for serializing detached objects.
More background on the dev mailing list.
Hi Jaime,
can you provide a version where the copyright in the sources is hold by the "XStream committers"? Simply add your name with an author tag to the classes instead.
Thanks!
New version of xstream_hib.tar.gz with amended copyright notices
Support is now in trunk in separate module. Thanks to all contributions and remarks.
converter