ReflectionToString.javaReflectionToStringBuilder 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.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.
| Method | Override 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. |
When append(fieldName, object) is called, the following logic applies in order:
<null>).BaseDO — extracts the identifier via HibernateUtils.getIdentifier(); renders the numeric or string ID (or "<id>" if null)."LazyCollection" string literal.DisplayNameCapable: delegates to private myToString() which renders "id:displayName" for BaseDO objects or just the display name for non-DO objects.BaseDO: renders the entity ID alone.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.TimeZone: renders the timezone ID string (e.g. "Europe/Berlin") instead of the full TimeZone.toString().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.
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.toString() implementation in BaseDO and its subclasses (every domain object in ProjectForge).log.debug("entity: {}", entity) will trigger this formatter via the entity's toString().HibernateUtils.getIdentifier() for safe ID extraction from proxies.ReflectionToStringBuilder for the reflection loop skeleton.accept() filter prevents it from descending into uninitialized lazy associations which would trigger database queries.accept() method calls getValue(field) which forces field access. If the field holds an uninitialized PersistentCollection, Hibernate.isInitialized() returns false and the field is handled inline — no database load occurs.append() uses myToString() (which renders IDs/display names) rather than toString() on collection elements. This prevents infinite recursion when entities have bidirectional @OneToMany/@ManyToOne relationships.TimeZone.toString() produces long output with offset details. This override extracts only the ID, keeping output concise.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