#706: HibernateMapper.java

projectforge-business/src/main/java/org/projectforge/framework/persistence/xstream/HibernateMapper.java XStream mapper — comprehensive Hibernate collection type mapper and proxy class resolver, projectforge-business/src/main/java/org/projectforge/framework/persistence/xstream/HibernateMapper.java 95 lines · 46 code · 35 comments · 14 blank
Extends XStream's MapperWrapper (Decorator pattern) to serve as the central mapper for all Hibernate-aware XStream serialization. It performs two critical functions: (1) substituting Hibernate's internal collection implementation classes with their standard JDK equivalents when determining default implementations during marshalling/unmarshalling, and (2) detecting HibernateProxy instances by checking implemented interfaces and resolving their real superclass names for XML element naming. This is the most comprehensive of the Hibernate XStream components — it handles PersistentBag (which the older HibernateCollectionsMapper omits) and also detects HibernateProxy wrappers that HibernateCollectionsMapper ignores.

Architecture

Class Hierarchy

HibernateMapper extends com.thoughtworks.xstream.mapper.MapperWrapper, the base class for XStream mapper decorators. Two constructors accept either MapperWrapper or plain Mapper delegates and call init() to populate the collection substitution map.

Collection Mapping Table

The init() method populates a Map<Class<?>, Class<?>> with six Hibernate→JDK mappings:

Hibernate Persistent CollectionJDK Replacement
PersistentBagArrayList
PersistentListArrayList
PersistentMapHashMap
PersistentSetHashSet
PersistentSortedMapTreeMap
PersistentSortedSetTreeSet
Unlike HibernateCollectionsMapper, this mapper includes PersistentBag — the default Hibernate collection for un-indexed List fields declared without @OrderColumn. This is the most common collection type in ProjectForge entities and its absence from the older mapper is a notable gap.

Overridden Methods

defaultImplementationOf(Class clazz)

Called by XStream when it needs to instantiate a concrete class for an interface or abstract type (e.g., when deserializing a List field, XStream asks what implementation to create). If the requested class is in the collectionMap, the JDK replacement is returned. This ensures deserialized XML produces plain ArrayList/HashSet objects rather than Hibernate-specific implementations that would be broken without a Hibernate session.

serializedClass(Class clazz)

Called by XStream to determine the class name to write into the XML. This method performs a two-step check:

  1. HibernateProxy detection: Iterates over clazz.getInterfaces() looking for HibernateProxy.class. If found, returns clazz.getSuperclass().getName() — the real entity class name. This ensures <org.projectforge.business.user.UserDO> appears in XML rather than <org.projectforge.business.user.UserDO_$$_jvstXXX> (the Javassist-generated proxy name).
  2. Collection substitution: If the class is in the collectionMap, returns the JDK replacement's fully-qualified name.
  3. Fallback: Delegates to the wrapped mapper via super.serializedClass(clazz).

Integration Points

Design Decisions & Gotchas

Git History

868d6abb7 2025 -> 2026
63081666f Source file headers: 2024-> 2025.
c1d14ecdb Migration stuff in progress...
b6092df09 Copyright 2023 -> 2024
ab45d51fa Copyright 2001-2022 -> 2001-2023.
5f7ef41b8 Copyright 2021 -> 2022
ceb63e8a1 Source code header: (C) 2001-2021.
7c79f1922 Copyright of source header -> 2020.
73a9755df More code cleanup
32f634b88 Optimize imports
dd5ca38ac CopyRight of all java file-header updated or created.
9ebb88522 Initial commit