#703: ReflectionToString.java

projectforge-business/src/main/java/org/projectforge/framework/persistence/utils/ReflectionToString.java Java utility — Hibernate-aware reflection-based toString() builder, projectforge-business/src/main/java/org/projectforge/framework/persistence/utils/ReflectionToString.java 118 lines · 82 code · 28 comments · 8 blank
Extends Apache Commons Lang3 ReflectionToStringBuilder to produce safe, human-readable toString() output for JPA entities. Handles Hibernate lazy proxies, uninitialized collections, BaseDO IDs, DisplayNameCapable labels, and TimeZone objects without triggering LazyInitializationException. The single static entry point ReflectionToString.asString(obj) is used throughout the codebase wherever debug/log output of domain objects is needed.

Architecture

Class Hierarchy

ReflectionToString extends org.apache.commons.lang3.builder.ReflectionToStringBuilder (Apache Commons Lang3). Internally, ReflectionToStringBuilder uses Java reflection to walk all declared fields of an object (and its superclasses), appending each field name/value pair into a ToStringBuilder. This class overrides three key methods to inject Hibernate awareness.

MethodOverride Purpose
ReflectionToString(Object) (constructor)Checks if the root object is initialized via Hibernate.isInitialized(). If not, passes a synthetic string like "Lazy" + className + "@" + identityHash instead of the actual proxy, preventing LazyInitializationException from the very start.
append(String, Object)The core of the Hibernate-safe logic. Before appending each field value, it checks Hibernate.isInitialized() and applies specialized formatting based on the value's type.
accept(Field)Filters out fields whose values are uninitialized Hibernate proxies. Rather than skipping silently, it calls append() directly (which will render the proxy info) and returns false so the superclass doesn't double-process the field.

append() Value Handling Decision Tree

When append(fieldName, object) is called, the following logic applies in order:

  1. Null: passes through to super (renders <null>).
  2. Not Hibernate-initialized:
  3. Initialized and DisplayNameCapable: delegates to private myToString() which renders "id:displayName" for BaseDO objects or just the display name for non-DO objects.
  4. Initialized and BaseDO: renders the entity ID alone.
  5. Collection: iterates elements, calls myToString() on each, formats as [el1, el2, ...]. Avoids the default collection toString() which would recursively call ReflectionToString on every element and potentially explode output.
  6. TimeZone: renders the timezone ID string (e.g. "Europe/Berlin") instead of the full TimeZone.toString().
  7. Otherwise: passes through to super.

private myToString(Object) Logic

Special-cases null"<null>", then DisplayNameCapable objects (with BaseDO prefixing the ID), BaseDO objects (ID only), and finally falls back to obj.toString() for all other types.

Javassist Workaround

When append() encounters an uninitialized BaseDO object, it calls HibernateUtils.getIdentifier() rather than using the proxy's own getId(). The comment in the source notes this is a "work around for Jassist bug" — Javassist proxies (used by Hibernate) can produce incorrect behavior when calling methods on uninitialized proxies, so the framework utility is preferred to extract the ID from the proxy metadata directly.

Integration Points

Design Decisions & Gotchas

Git History

868d6abb7 2025 -> 2026
63081666f Source file headers: 2024-> 2025.
b6092df09 Copyright 2023 -> 2024
ab45d51fa Copyright 2001-2022 -> 2001-2023.
5f7ef41b8 Copyright 2021 -> 2022
ceb63e8a1 Source code header: (C) 2001-2021.
a6a7aece4 Optimize Imports
b78b870bd ShortDisplayNameCapable -> DisplayNameCapable.
7c79f1922 Copyright of source header -> 2020.
73a9755df More code cleanup: - Collapsed catch blocks that did the exact same things
32f634b88 Optimize imports
000ca723d Remove pointless boolean expressions (business)
dd5ca38ac CopyRight of all java file-header updated or created.
f979e8a42 MGC-UPDATE: Update auf Version 3.0.0-SNAPSHOT
9ebb88522 Initial commit