Dozer Documentation

repository·master·Indexed 24 days ago

https://github.com/dozermapper/dozer

Dozer is a Java Bean to Java Bean mapping framework used to automate the process of copying data between different object layers, such as DTOs, Domain objects, and Entity objects. It supports recursive mapping, automatic type conversion, and flexible configuration via XML, Java API (BeanMappingBuilder), or @Mapping annotations. Key features include support for the Assembler pattern, collection and array mapping, and bi-directional mapping. Note: The project is currently inactive and discouraged for new greenfield projects.

Tokens
32.8K
Snippets
101
Records
137
Agent score
82%

What's inside Dozer

  1. What is Dozer?

    master

    Dozer is a Java Bean to Java Bean mapper designed to recursively copy data from one object to another. It is primarily used at architectural boundaries (entry/exit points) to prevent internal domain objects from leaking into external presentation layers or API calls.

    Key Capabilities:

    • Mapping Types: Supports simple property mapping, complex type mapping, bi-directional mapping, implicit-explicit mapping, and recursive mapping (including collection elements).
    • Automatic Conversion: Automatically converts between types for most common scenarios.
    • Mapping Logic:
      • Automatic: Uses reflection to map fields with identical names.
      • Explicit: Custom mappings can be defined using XML.
      • Bi-directional: Defining a relationship between two classes allows for mapping in both directions.

    Dependencies: Dozer is written in Java and relies on the Jakarta Commons Bean Utils package for utility methods.

  2. How Expression Language works in Dozer

    master

    Dozer's Expression Language (EL) support allows for dynamic configuration of mapping files using the jakarta.el standard.

    Key characteristics:

    • Resolution Timing: Expressions are resolved during mapping loading time (start-up), not during the mapping execution itself. This ensures high performance during actual data transformation.
    • Implementation Agnostic: While tested with glassfish, Dozer supports any EL implementation that adheres to the jakarta.el standard API.
    • Variable Scoping: You can define reusable constants in a <variables> block within the <configuration> section of your mapping file, allowing you to reference them throughout the mapping using ${variable_name}.
  3. Understanding the purpose of a mapping framework

    master

    In layered architectures (e.g., separating external service data, domain objects, DTOs, and internal service objects), a mapping framework like Dozer is used to encapsulate changes to specific data objects. Instead of manually coding value object assemblers or converters to copy data between objects, Dozer provides a generic, flexible, and configurable way to map data between different object hierarchies.

    Key use cases include:

    • Separation of Architectural Layers: Mapping between presentation, domain, and persistence layers where each layer has its own set of Java Beans.
    • Integration with External Code: Acting as a bridge to guard your codebase from frequently changing external object hierarchies. Because Dozer uses reflection, many changes (like a type changing from Number to String) are resolved automatically without breaking your API.
    • Serialization Requirements: Converting Rich Domain Models into Presentation Models that satisfy constraints imposed by frameworks like Google Web Toolkit (GWT).
    • Framework Integration: Converting between domain models and XML objects using JAXB-compatible factory classes.
  4. Understand Implicit vs Explicit mapping modes

    master

    Dozer operates using two modes, referred to as the "wildcard" switch:

    • Implicit mode (Default): Dozer attempts to resolve mappings automatically. It assumes that if two objects have bean properties with the same names, those properties should be mapped. If names do not match, you must provide additional configuration via XML, annotations, or the API.
    • Explicit mode: Dozer assumes no mappings should be performed unless they are specifically defined. This provides full control over the mapping process but requires more configuration code.
  5. Implement the Assembler Pattern with Dozer

    master

    Dozer can be used to implement the Assembler pattern, which involves taking multiple fine-grained objects and combining them into a single coarse-grained object used for data transfer.

    To achieve this, you must define individual mappings for each fine-grained object to your target coarse-grained object in your mapping configuration. You then call the mapper.map() method multiple times, passing the same target instance to accumulate data from different sources.

    ClassD d = new ClassD();
    mapper.map(sourceA, d);
    mapper.map(sourceB, d);
    mapper.map(sourceC, d);
  6. How recursive mapping works in Dozer

    master

    Dozer supports full class-level mapping recursion. When mapping complex types defined as field-level mappings, Dozer will look for a corresponding class-level mapping between those two specific classes in your mapping file.

    If no explicit class-level mapping is found for the complex types, Dozer will fall back to only mapping fields that share the same name between those two complex types.

  7. Enable Copying By Object Reference

    master

    Dozer allows you to copy objects by reference instead of performing a full conversion/transformation. This reduces object allocations and improves performance, but it should only be used when the source Java Beans are intended to be garbage collected immediately after transformation.

    Warning: Ensure that both the source and destination object types are the same to avoid ClassCastException errors. The default value for this setting is false.

    <field copy-by-reference="true">
        <a>copyByReference</a>
        <b>copyByReferencePrime</b>
    </field>
  8. How Dozer handles proxy objects

    master

    Dozer can perform mappings on proxy objects, which are commonly used by persistence frameworks (like Hibernate) to support features such as lazy-loading. When working with these frameworks, the application often interacts with 'fake' objects that encapsulate the real data.

    Dozer provides a generic way to handle common proxy libraries like Cglib and Javassist by default. However, for optimal performance and reliability, it is recommended to tune the proxy handling behavior to match your specific framework or scenario.

  9. Configure Cumulative vs. Non-Cumulative List Mapping

    master

    When mapping to a destination class that is already initialized, Dozer determines whether to 'add' or 'update' objects in a List, Set, or Array using the relationship-type attribute. This behavior is controlled by the contains() method of the destination collection.

    • cumulative (Default): Objects are always added to the existing collection.
    • non-cumulative: Dozer checks if an object exists; if it does, it updates it; if not, it adds it.

    Important: For non-cumulative mapping to work correctly, you must implement custom equals() and hashCode() methods in your destination classes. Otherwise, Dozer will rely on JDK-generated IDs and treat every instance as unique, preventing updates.

    relationship-type can be specified at three levels:

    1. Global configuration
    2. Class mapping
    3. Field mapping
    <!-- Global configuration -->
    <mappings>
        <configuration>
            <relationship-type>non-cumulative</relationship-type>
        </configuration>
    </mappings>
    
    <!-- Class mapping level -->
    <mappings>
        <mapping relationship-type="non-cumulative">
            <class-a>com.github.dozermapper.core.vo.TestObject</class-a>
            <class-b>com.github.dozermapper.core.vo.TestObjectPrime</class-b>
            <field>
                <a>someList</a>
                <b>someList</b>
            </field>
        </mapping>
    </mappings>
    
    <!-- Field mapping level -->
    <!-- objects will always be added to an existing List -->
    <field relationship-type="cumulative">
        <a>hintList</a>
        <b>hintList</b>
        <a-hint>com.github.dozermapper.core.vo.TheFirstSubClass</a-hint>
        <b-hint>com.github.dozermapper.core.vo.TheFirstSubClassPrime</b-hint>
    </field>
    
    <!-- objects will updated if already exist in List, added if they are not present -->
    <field relationship-type="non-cumulative">
        <a>unequalNamedList</a>
        <b>theMappedUnequallyNamedList</b>
    </field>
  10. Understand Dozer XML configuration scopes

    master

    Dozer allows you to configure mapping behavior using XML within five distinct scopes. Settings applied in a broader scope are inherited by narrower scopes unless explicitly overridden:

    1. Global Scope: Defined within a <configuration> block. Sets default settings for all mappings in the file and defines custom-converters. Multiple mapping files can each have their own configuration block.
    2. Per Class Mapping: Defined on the <mapping> element using attributes (e.g., wildcard="false"). Affects all mapping operations between the two specified classes.
    3. At Individual Class Level: Defined within <class-a> or <class-b> tags. Allows applying settings to only one of the two classes (e.g., is-accessible="true").
    4. Per Field Mapping: Defined on the <field> element using attributes (e.g., remove-orphans="false"). Affects specific field pairs.
    5. At Individual Field Level: Defined within <a> or <b> tags inside a <field> element (e.g., get-method="getTheAttribute").
    <!-- Example of Global Scope configuration -->
    <configuration>
        <date-format>MM/dd/yyyy HH:mm</date-format>
        <stop-on-errors>true</stop-on-errors>
        <wildcard>true</wildcard>
        <custom-converters>
            <converter type="com.github.dozermapper.core.converters.TestCustomConverter">
                <class-a>com.github.dozermapper.core.vo.TestCustomConverterObject</class-a>
                <class-b>another.type.to.Associate</class-b>
            </converter>
        </custom-converters>
    </configuration>
  11. Optimize mapping XML using inheritance

    master

    When working with class hierarchies, you do not need to repeat field mappings for attributes that belong to a common base class in every subclass mapping. Instead, define a single <mapping> block for the base classes. Dozer will automatically apply these base mappings to any subclasses it encounters during the mapping process.

    Benefits:

    • Reduces XML redundancy.
    • Simplifies maintenance when base class attributes change.
    • Leverages Dozer's ability to analyze inheritance depth to find parent mappings.
    <!-- Refactored mapping for a hierarchy -->
    <mapping>
        <class-a>com.github.dozermapper.core.vo.SuperClass</class-a>
        <class-b>com.github.dozermapper.core.vo.SuperClassPrime</class-b>
        <field>
            <a>superAttribute</a>
            <b>superAttr</b>
        </field>
    </mapping>
    <mapping>
        <class-a>com.github.dozermapper.core.vo.SubClass</class-a>
        <class-b>com.github.dozermapper.core.vo.SubClassPrime</class-b>
        <field>
            <a>attribute</a>
            <b>attributePrime</b>
        </field>
    </mapping>