The Programmer's Guide
  • About
  • Algorithm
    • Big O Notation
      • Tree
      • Problems
    • Basic Notes
    • Data Structure Implementation
      • Custom LinkedList
      • Custom Stack
      • Custom Queue
      • Custom Tree
        • Binary Tree Implementation
        • Binary Search Tree Implementation
        • Min Heap Implementation
        • Max Heap Implementation
        • Trie Implementation
      • Custom Graph
        • Adjacency List
        • Adjacency Matrix
        • Edge List
        • Bidirectional Search
    • Mathematical Algorithms
      • Problems - Set 1
      • Problems - Set 2
    • Bit Manipulation
      • Representation
      • Truth Tables
      • Number System
        • Java Program
      • Problems - Set 1
    • Searching
    • Sorting
    • Array Algorithms
    • String Algorithms
    • Tree
      • Tree Traversal Techniques
      • Tree Implementation
      • Applications of Trees
      • Problems - Set 1
    • Graph
      • Graph Traversal Techniques
      • Shortest Path Algorithms
      • Minimum Spanning Tree (MST) Algorithms
    • Dynamic Programming
      • Problems - Set 1
    • Recursion
    • Parallel Programming
    • Miscellaneous
      • Problems - Set 1
  • API
    • API Basics
      • What is an API?
      • Types of API
        • Comparison - TBU
      • Synchronous vs Asynchronous API
    • API Architecture
      • Synchronous & Asynchronous Communication
    • API Specification
  • Cloud Computing
    • Cloud Fundamentals
      • Cloud Terminology
      • Core Terminology
      • Cloud Models
      • Cloud Service Models
      • Benefits, Challenges and Risk of Cloud Computing
      • Cloud Ecosystem
  • Database
    • DBMS
      • Types of DBMS
        • Relational DBMS (RDBMS)
        • NoSQL DBMS
        • Object-Oriented DBMS (OODBMS)
        • Columnar DBMS
        • In-Memory DBMS
        • Distributed DBMS
        • Cloud-Based DBMS
        • Hierarchical DBMS
      • DBMS Architecture
      • DBMS Structure
    • SQL Databases
      • Terminology
      • RDBMS Concepts
        • Entity Relationship Diagram (ERD)
          • ERD Examples
        • Normalization
        • Denormalization
        • ACID & BASE Properties
          • ACID Properties
          • BASE Properties
        • Locking and Unlocking
      • SQL Fundamentals
        • SQL Commands
          • DDL (Data Definition Language)
          • DML (Data Manipulation Language)
          • DCL (Data Control Language)
          • TCL (Transaction Control Language)
          • DQL (Data Query Language)
        • SQL Operators
          • INTERSECT
          • EXCEPT
          • MINUS
          • IN and NOT IN
          • EXISTS and NOT EXISTS
        • SQL Clauses
          • Joins
          • OVER
          • WITH
          • CONNECT BY
          • MODEL
          • FETCH FIRST
          • KEEP
          • OFFSET with FETCH
        • SQL Functions
          • Oracle Specific
        • SQL Data Types
          • Numeric Types
          • Character Types
          • Date & Time Types
          • Large Object Types
        • Others
          • Indexing
      • Vendor Specific Concepts
        • Oracle Specific
          • Data Types
          • Character Set
          • Rownum, Rowid, Urowid
          • Order of Execution of the query
          • Keys
          • Tablespace
          • Partition
      • Best Practice
      • Resources & References
        • O’Reilly SQL Cookbook (2nd Edition)
          • 1. Retrieving Records
          • 2. Sorting Query Results
          • 3. Working with Multiple Tables
          • 4. Inserting, Updating, and Deleting
          • 5. Metadata Queries
          • 6. Working with Strings
          • 7. Working with Numbers
          • 8. Date Arithmetic
          • 9. Date Manipulation
          • 10. Working with Ranges
          • 11. Advanced Searching
          • 12. Reporting and Reshaping
          • 13. Hierarchical Queries
          • 14. Odds 'n' Ends
    • SQL vs NoSQL
    • Best Practices
  • Git
    • Commands
      • Setup and Configuration Commands
      • Getting and Creating Projects
      • Tracking Changes
      • Branching and Merging
      • Sharing and Updating Projects
      • Inspection and Comparison
      • Debugging
      • Patching
      • Stashing and Cleaning
      • Advanced Manipulations
    • Workflows
      • Branching Strategies
        • Git Flow
        • Trunk-Based Development
        • GitHub Flow
        • Comparison
      • Merge Strategies
        • Merge
        • Rebase
        • Squash
        • Fast-forward vs No-fast-forward
        • MR vs PR
      • Conflict Resolution
        • Handling Merge Conflicts
        • Merge Conflicts
        • Rebase Conflicts
        • Divergent Branches After git pull
        • Force Push
      • Patch & Recovery
        • Cherry-pick strategies
        • Revert vs Reset
        • Recover from a bad rebase
      • Rebasing Practices
        • Merge vs Rebase
        • Rebase develop branch on main branch
      • Repository Management
        • Working Directory
        • Mirror a repository
        • Convert a local folder to a Git repo
        • Backup and restore a Git repository
  • Java
    • Java Installation
    • Java Distributions
    • Java Platform Editions
      • Java SE
      • Java EE
      • Jakarta EE
      • Java ME
      • JavaFX
    • Java Overview
      • OOP Principles
        • Encapsulation
        • Inheritance
        • Polymorphism
        • Abstraction
          • Abstract Class & Method
          • Interface
            • Functional Interfaces
            • Marker Interfaces
          • Abstract Class vs Interface
      • OOP Basics
        • What is a Class?
          • Types of Classes
        • What is an Object?
          • Equals and HashCode
            • FAQ
          • Shallow Copy and Deep Copy
          • Ways to Create Object
          • Serialization & Deserialization
        • Methods & Fields
          • Method Overriding & Overloading
          • Method Signature & Header
          • Variables
        • Constructors
        • Access Modifiers
      • Parallelism & Concurrency
        • Ways to Identify Thread Concurrency or Parallelism
        • Thread Basics
          • Thread vs Process
          • Creating Threads
          • Thread Context Switching
          • Thread Lifecycle & States
          • Runnable & Callable
          • Types of Threads
          • Thread Priority
        • Thread Management & Synchronisation
          • Thread Resource Sharing
          • Thread Synchronization
            • Why is Synchronization Needed?
            • Synchronized Blocks & Methods
          • Thread Lock
            • Types of Locks
            • Intrinsic Lock (Monitor Lock)
            • Reentrant Lock
          • Semaphore
          • Thread Starvation
          • Thread Contention
          • Thread Deadlock
          • Best Practices for Avoiding Thread Issues
      • Keywords
        • this
        • super
        • Access Modifiers
      • Data Types
        • Default Values
        • Primitive Types
          • byte
          • short
          • int
          • long
          • float
          • double
          • char
          • boolean
        • Non-Primitive (Reference) Types
          • String
            • StringBuilder
            • StringBuffer
              • Problems
            • Multiline String
            • Comparison - String, StringBuilder & StringBuffer
          • Array
          • Collections
            • List
              • Array vs List
              • ArrayList
              • Vector
                • Stack
                  • Problems
              • LinkedList
            • Queue
              • PriorityQueue
              • Deque (Double-Ended Queue)
                • ArrayDeque
                • ConcurrentLinkedDeque - TBU
                • LinkedBlockingDeque - TBU
            • Map
              • HashMap
              • Hashtable
              • LinkedHashMap
              • ConcurrentHashMap
              • TreeMap
              • EnumMap
              • WeakHashMap
            • Set
              • HashSet
              • LinkedHashSet
              • TreeSet
              • EnumSet
              • ConcurrentSkipListSet
              • CopyOnWriteArraySet
        • Specialized Classes
          • BigInteger
          • BigDecimal
            • Examples
          • BitSet
          • Date and Time
            • Examples
          • Optional
          • Math
          • UUID
          • Scanner
          • Formatter
            • Examples
          • Properties
          • Regex (Pattern and Matcher)
            • Examples
          • Atomic Classes
          • Random
          • Format
            • NumberFormat
            • DateFormat
            • DecimalFormat
        • Others
          • Object
          • Enum
            • Pre-Defined Enum
            • Custom Enum
            • EnumSet and EnumMap
          • Record
          • Optional
          • System
          • Runtime
          • ProcessBuilder
          • Class
          • Void
          • Throwable
            • Error
            • Exception
              • Custom Exception Handling
              • Best Practice
            • Error vs Exception
            • StackTraceElement
    • Java Features by Version
      • How New Java Features are Released ?
      • Java Versions
        • Java 8
        • Java 9
        • Scoped Values
        • Unnamed Variables & Patterns
      • FAQ
    • Concepts
      • Set 1
        • Streams
          • flatmap
          • Collectors Utility Class
          • Problems
        • Functional Interfaces
          • Standard Built-In Interfaces
          • Custom Interfaces
        • Annotation
          • Custom Annotation
          • Meta Annotation
        • Generics
          • Covariance and Invariance
        • Asynchronous Computation
          • Future
          • CompletableFuture
          • Future v/s CompletableFuture
          • ExecutorService
            • Thread Pool
            • Types of Work Queues
            • Rejection Policies
            • ExecutorService Implementations
            • ExecutorService Usage
          • Locks, Atomic Variables, CountDownLatch, CyclicBarrier - TBU
          • Parallel Streams, Fork/Join Framework,Stream API with Parallelism - TBU
      • Set 2
        • Standards
          • ISO Standards
          • JSR
            • JSR 303, 349, 380 (Bean Validation)
        • Operator Precedence
      • Set 3
        • Date Time Formatter
        • Validation
      • Set 4
        • Input from User
        • Comparison & Ordering
          • Object Equality Check
          • Comparable and Comparator
            • Comparator Interface
          • Sorting of Objects
          • Insertion Ordering
    • Packages
      • Core Packages
        • java.lang
          • java.lang.System
          • java.lang.Thread
      • Jakarta Packages
        • jakarta.validation
        • javax.validation
      • Third-party Packages
    • Code Troubleshoot
      • Thread Dump
      • Heap Dump
    • Code Quality & Analysis
      • ArchUnit
      • Terminologies
        • Cyclic dependencies
    • Code Style
      • Naming Convention
      • Package Structure
      • Formatting
      • Comments and Documentation
      • Imports
      • Exception Handling
      • Class Structure
      • Method Guidelines
      • Page 1
      • Code Smells to Avoid
      • Lambdas and Streams Style
      • Tools
    • Tools
      • IntelliJ IDEA
        • Shortcuts for MAC
      • Apache JMeter
        • Examples
      • Thread Dump Capture
        • jstack
        • VisualVM - TBU
        • jcmd - TBU
        • JConsole - TBU
        • YourKit Java Profiler - TBU
        • Eclipse MAT - TBU
        • IntelliJ IDEA Profiler - TBU
        • AppDynamics - TBU
        • Dynatrace - TBU
        • Thread Dump Analyzers - TBU
      • Heap Dump Capture
        • jmap
        • VisualVM - TBU
        • jcmd - TBU
        • Eclipse MAT (Memory Analyzer Tool) - TBU
        • IntelliJ IDEA Profiler - TBU
        • YourKit Java Profiler - TBU
        • AppDynamics - TBU
        • Dynatrace - TBU
        • Kill -3 Command - TBU
        • jhat (Java Heap Analysis Tool) - TBU
        • JVM Options - TBU
      • Wireshark
        • Search Filters
    • Best Practices
      • Artifact and BOM Versioning
  • Maven
    • Installation
    • Local Repository & Configuration
    • Command-line Options
    • Build & Lifecycle
    • Dependency Management
      • Dependency
        • Transitive Dependency
        • Optional Dependency
      • Dependency Scope
        • Maven Lifecycle and Dependency Scope
      • Dependency Exclusions & Overrides
      • Bill of Materials (BOM)
      • Dependency Conflict Resolution
      • Dependency Tree & Analysis
      • Dependency Versioning Strategies
    • Plugins
      • Build Lifecycle Management
      • Dependency Management
      • Code Quality and Analysis
      • Documentation Generation
      • Code Generation
      • Packaging and Deployment
      • Reporting
      • Integration and Testing
      • Customization and Enhancement
        • build-helper-maven-plugin
        • properties-maven-plugin
        • ant-run plugin
        • exec-maven-plugin
        • gmavenplus-plugin
      • Performance Optimization
    • FAQs
      • Fixing Maven SSL Issues: Unable to Find Valid Certification Path
  • Spring
    • Spring Basics
      • What is Spring?
      • Why Use Spring
      • Spring Ecosystem
      • Versioning
      • Setting Up a Spring Project
    • Core Concepts
      • Spring Core
        • Dependency Injection (DI)
        • Stereotype Annotation
      • Spring Beans
        • Bean Lifecycle
        • Bean Scope
          • Singleton Bean
        • Lazy & Eager Initialization
          • Use Case of Lazy Initialization
        • BeanFactory
        • ApplicationContext
      • Spring Annotations
        • Spring Boot Specific
        • Controller Layer (Web & REST Controllers)
    • Spring Features
      • Auto Configuration
        • Spring Boot 2: spring.factories
        • Spring Boot 3: spring.factories
      • Spring Caching
        • In-Memory Caching
      • Spring AOP
        • Before Advice
        • After Returning Advice
        • After Throwing Advice
        • After (finally) Advice
        • Around Advice
      • Spring File Handling
      • Reactive Programming
        • Reactive System
        • Reactive Stream Specification
        • Project Reactor
          • Mono & Flux
      • Asynchronous Computation
        • @Async annotation
      • Spring Security
        • Authentication
          • Core Components
            • Security Filter Chain
              • HttpSecurity
              • Example
            • AuthenticationManager
            • AuthenticationProvider
            • UserDetailsService
              • UserDetails
              • PasswordEncoder
            • SecurityContext
            • SecurityContextHolder
            • GrantedAuthority
            • Security Configuration (Spring Security DSL)
          • Authentication Models
            • One-Way Authentication
            • Mutual Authentication
          • Authentication Mechanism
            • Basic Authentication
            • Form-Based Authentication
            • Token-Based Authentication (JWT)
            • OAuth2 Authentication
            • Multi-Factor Authentication (MFA)
            • SAML Authentication
            • X.509 Certificate Authentication
            • API Key Authentication
            • Remember-Me Authentication
            • Custom Authentication
          • Logout Handling
        • Authorization
        • Security Filters and Interceptors
        • CSRF
          • Real-World CSRF Attacks & Prevention
        • CORS
        • Session Management and Security
        • Best Practices
      • Spring Persistence
        • JDBC
          • JDBC Components
          • JDBC Template
          • Transaction Management
          • Best Practices in JDBC Usage
          • Datasource
            • Connection Pooling
              • HikariCP
            • Caching
        • JPA (Java Persistence API)
          • JPA Fundamentals
          • ORM Mapping Annotations
            • 1. Entity and Table Mappings
            • 2. Field/Column Mappings
            • 3. Relationship Mappings
            • 4. Inheritance Mappings
            • 5. Additional Configuration Annotations
          • Querying Data
            • JPQL
            • Criteria API
            • JPA Specification
              • Example - Employee Portal
            • Native SQL Queries
            • Named Queries
            • Query Return Types
            • Pagination & Sorting
              • Example - Employee Portal
            • Projection
          • Fetch Strategies in JPA
        • JPA Implementation
          • Hibernate
            • Properties
            • Example
        • Spring Data JPA
          • Repository Abstractions
          • Entity-to-Table Mapping
          • Derived Query Methods
        • Cross-Cutting Concerns
          • Transactions
          • Caching
          • Concurrency
        • Examples
          • Employee Portal
            • API
    • Distributed Systems & Communication
      • Distributed Scheduling
      • Inter-Service Communication
        • 1. RestTemplate
        • 2. WebClient
        • 3. OpenFeign
        • Retry Mechanism
          • @Retryable annotation
            • Example
    • Security & Data Protection
      • Encoding | Decoding
        • Types
          • Base Encoding
            • Base16 - TBD
              • Encoding and Decoding in Java - TBD
            • Base32
              • Encoding and Decoding in Java
            • Base64 -TBD
              • Encoding and Decoding in Java - TBD
          • Text Encoding - TBD
            • Extended ASCII
              • Encoding and Decoding in Java - TBD
                • ISO-8859-1
                • Windows-1252 - TBD
                • IBM Code Pages - TBD
            • ASCII
              • Encoding and Decoding in Java
        • Java Guidelines
          • Text Encoding Decoding Examples
          • Base Encoding Decoding Examples
          • Best Practices and Concepts
          • Libraries
      • Cryptography
        • Terminology
        • Java Cryptography Architecture (JCA)
        • Key Management
          • Key Generation
            • Tools and Libraries
              • OpenSSL
              • Java Keytool
                • Concept
                • Use Cases
            • Key & Certificate File Formats
          • Key Distribution
          • Key Storage
          • Key Rotation
          • Key Revocation
        • Encryption & Decryption
          • Symmetric Encryption
            • Algorithm
            • Modes of Operation
            • Examples
          • Asymmetric Encryption
            • Algorithm
            • Mode of Operation
            • Examples
    • Utilities & Libraries
      • Apache Libraries
        • Apache Camel
          • Camel Architecture
            • Camel Context
            • Camel Endpoints
            • Camel Components
            • Camel Exchange & MEP
          • Spring Dependency
          • Different Components
            • Camel SFTP
        • Apache Commons Lang
      • MapStruct Mapper
      • Utilities by Spring framework
        • FileCopyUtils
    • General Concepts
      • Spring Boot Artifact Packaging
      • Classpath and Resource Loading
      • Configuration - Mapping Properties to Java Class
      • Validations in Spring Framework
        • Jakarta Validation
          • Jakarta Bean Validation Annotations
    • Practical Guidelines
      • Spring Configuration
      • Spring Code Design
  • Software Testing
    • Software Testing Methodologies
      • Functional Testing
      • Non Functional Testing
    • Software Testing Life Cycle (STLC)
    • Integration Test
      • Dynamic Property Registration
    • Java Test Framework
      • JUnit
        • JUnit 4
          • Examples
        • JUnit 5
          • Examples
        • JUnit 4 vs JUnit 5
  • System Design
    • Foundations
      • Programming Paradigms
      • Object-Oriented Design
        • SOLID Principles
        • GRASP Principles
        • Composition
        • Aggregation
        • Association
      • Design Pattern
        • Creational Pattern
        • Structural Pattern
        • Behavioral Pattern
        • Examples
          • Data Collector
          • Payment Processor
        • Design Enhancements
          • Fluent API Design
            • Examples
    • Architectural Building Blocks
      • CAP Theorem
      • Load Balancer
        • Load Balancer Architecture
        • Load Balancing in Java Microservices
          • Client-Side Load Balancing Example
          • Server-Side Load Balancing Example
        • Load Balancer Monitoring Tool
      • Scaling
        • Vertical Scaling (Scaling Up)
        • Horizontal Scaling (Scaling Out)
        • Auto-Scaling
        • Database Scaling via Sharding
      • Caching
        • Pod-Level vs Distributed Caching
      • Networking Metrics
        • Types of Delay
        • Scenario
      • System Characteristics
      • Workload Types
      • Resilience & Failure Handling
    • Performance
      • Why Is My API Sometimes Slow ?
    • Security
      • Security by Design
      • Zero Trust Security Model
      • Zero Trust Architecture
      • Principles
        • CIA
        • Least Privilege Principle
        • Defense in Depth
      • Security Threats & Mitigations
        • OWASP
          • Top 10 Security Threats
          • Application Security Verification Standard
          • Software Assurance Maturity Model
          • Dependency Check
          • CSRFGuard
          • Cheat Sheets
          • Security Testing Guide
          • Threat Dragon
        • Threat Modeling
      • Compliance & Regulation
        • PCI DSS
    • Deployment Patterns
    • Diagrams
      • UML Diagrams
        • PlantUML
          • Class Diagram
          • Object Diagram
          • Sequence Diagram
          • Use Case Diagram
          • Activity Diagram
          • State Diagram
          • Architecture Diagram
          • Component Diagram
          • Timing Diagram
          • ER Diagram (Entity-Relationship)
          • Network Diagram
    • Common Terminologies
    • Problems
      • Reference Materials
      • Cache Design
  • Interview Guide
    • Non-Technical
      • Behavioural or Introductory Guide
      • Project Specific
    • Technical
      • Java Interview Companion
        • Java Key Concepts
          • Set 1
          • Set 2
        • Java Code Snippets
        • Java Practice Programs
          • Set 3 - Strings
          • Set 4 - Search
          • Set 5 - Streams and Collection
      • SQL Interview Companion
        • SQL Practice Problems
          • Set 1
      • Spring Interview Companion
        • Spring Key Concepts
          • Set 1 - General
          • Set 2 - Core Spring
        • Spring Code Snippets
          • JPA
      • Application Server
      • Maven
      • Containerized Application
      • Microservices
    • General
      • Applicant Tracking System (ATS)
      • Flowchart - How to Solve Coding Problem?
Powered by GitBook
On this page
  • About
  • Maven POM Dependency and Plugin
  • Core Features
  • Data type conversions
  • Implicit type conversions
  • Mapping nested object references
  • Controlling nested bean mappings
  • Invoking custom mapping method
  • Invoking other mappers
  • Passing context or state objects to custom methods
  • Mapping method resolution
  • Combining qualifiers @Named with defaults
  • Basic Mapping
  • Default values and constants
  • Expressions
  • Default Expressions
  • Subclass Mapping
  • Determining the result type
  • Controlling mapping result for 'null' arguments
  • Source presence checking and Conditional Mapping
  • Exceptions
  • Using builders
  • Using Constructors
  • Mapping Map to Bean
  • Retrieving a mapper in another class to use its methods
  • Injection strategy
  • Composition Mapping
  • Mapping collections
  • Mapping maps
  • Implementation types used for collection mappings
  • Mapping Streams
  • Mapping Values
  • Mapping enum to enum types
  • Mapping enum-to-String or String-to-enum
  • Custom name transformation
  • Object factories
  • Reusing mapping configurations
  • Mapping configuration inheritance
  • Inverse mappings
  • Shared configurations
  • Customizing mappings
  • Mapping customization with before-mapping and after-mapping methods

Was this helpful?

  1. Spring
  2. Utilities & Libraries

MapStruct Mapper

PreviousApache Commons LangNextUtilities by Spring framework

Last updated 8 months ago

Was this helpful?

About

MapStruct is a code generator that simplifies the implementation of mappings between Java bean types based on a convention-over-configuration approach. It is a tool designed to help developers map data from one Java object to another. It is a popular choice for mapping objects, especially in large-scale enterprise applications, due to its performance and ease of use.

Refer to documentation for more details:

What is "Convention-Over-Configuration"?

"Convention-over-configuration" is a software design principle used to reduce the number of decisions developers need to make, without sacrificing flexibility. In simpler terms, it means that the framework will assume reasonable default behavior unless the developer specifies otherwise. This approach minimizes the need for extensive configuration by relying on common conventions.

How Does MapStruct Use Convention-Over-Configuration?

MapStruct uses this principle to simplify object mappings by following these conventions

  • Property Name Matching:

    • By default, MapStruct maps properties in source and target objects that have the same name and compatible types.

    • For example, if we have a UserDTO class with a name field and a UserEntity class with a name field, MapStruct will automatically map UserDTO.name to UserEntity.name.

  • Type Matching:

    • MapStruct can automatically convert between types that have a clear conversion path (e.g., String to int, Date to String).

    • This reduces the need for explicit type conversion configuration.

  • Default Method Generation:

    • MapStruct generates implementation code for the mapping methods based on the interface definitions provided by the developer.

    • For instance, if we define a method UserDTO toUserDTO(UserEntity entity); in a mapper interface, MapStruct will generate the implementation for this method, mapping each field based on conventions.

Maven POM Dependency and Plugin

Include the required dependencies in pom.xml file.

<!-- This dependency includes the core MapStruct library which provides 
the API and main functionality for object mapping. -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.5.Final</version>
</dependency>
<!-- This dependency includes the MapStruct annotation processor which 
generates the implementation of the mapper interfaces at compile time. -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.5.Final</version>
</dependency>

It comprises the following artifacts:

  • org.mapstruct:mapstruct: contains the required annotations such as @Mapping

  • org.mapstruct:mapstruct-processor: contains the annotation processor which generates mapper implementations

<!-- The maven-compiler-plugin is a Maven plugin used to compile Java source files. 
In the context of using MapStruct, it is configured to ensure that the MapStruct annotation 
processor is correctly set up during the compilation phase. -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>${maven-compiler-plugin.version}</version>
    <configuration>
        <annotationProcessorPaths>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>${mapstruct.version}</version>
            </path>
        </annotationProcessorPaths>
        <useIncrementalCompilation>false</useIncrementalCompilation>
        <showWarnings>true</showWarnings>
        <compilerArgs>
            <arg>-Amapstruct.suppressGeneratorTimestamp=true</arg>
            <arg>-Amapstruct.suppressGeneratorVersionInfoComment=true</arg>
            <arg>-Amapstruct.verbose=true</arg>
        </compilerArgs>
    </configuration>
</plugin>

MapStruct processor options -

Option
Purpose
Default

mapstruct. suppressGeneratorTimestamp

If set to true, the creation of a time stamp in the @Generated annotation in the generated mapper classes is suppressed.

false

mapstruct.verbose

If set to true, MapStruct in which MapStruct logs its major decisions. Note, at the moment of writing in Maven, also showWarnings needs to be added due to a problem in the maven-compiler-plugin configuration.

false

mapstruct. suppressGeneratorVersionInfoComment

If set to true, the creation of the commentattribute in the @Generated annotation in the generated mapper classes is suppressed. The comment contains information about the version of MapStruct and about the compiler used for the annotation processing.

false

mapstruct.defaultComponentModel

The name of the component model based on which mappers should be generated.

Supported values are:

  • default: the mapper uses no component model, instances are typically retrieved via Mappers#getMapper(Class)

  • cdi: the generated mapper is an application-scoped (from javax.enterprise.context or jakarta.enterprise.context, depending on which one is available with javax.inject having priority) CDI bean and can be retrieved via @Inject

  • spring: the generated mapper is a singleton-scoped Spring bean and can be retrieved via @Autowired or lombok annotation like @RequiredArgsConstructor

  • jsr330: the generated mapper is annotated with {@code @Named} and can be retrieved via @Inject (from javax.inject or jakarta.inject, depending which one is available with javax.inject having priority), e.g. using Spring

  • jakarta: the generated mapper is annotated with {@code @Named} and can be retrieved via @Inject (from jakarta.inject), e.g. using Spring

  • jakarta-cdi: the generated mapper is an application-scoped (from jakarta.enterprise.context) CDI bean and can be retrieved via @Inject

If a component model is given for a specific mapper via @Mapper#componentModel(), the value from the annotation takes precedence.

default

mapstruct.defaultInjectionStrategy

The type of the injection in mapper via parameter uses. This is only used on annotated based component models such as CDI, Spring and JSR 330.

Supported values are:

  • field: dependencies will be injected in fields

  • constructor: will be generated constructor. Dependencies will be injected via constructor.

When CDI componentModel a default constructor will also be generated. If a injection strategy is given for a specific mapper via @Mapper#injectionStrategy(), the value from the annotation takes precedence over the option.

field

mapstruct.unmappedTargetPolicy

The default reporting policy to be applied in case an attribute of the target object of a mapping method is not populated with a source value.

Supported values are:

  • ERROR: any unmapped target property will cause the mapping code generation to fail

  • WARN: any unmapped target property will cause a warning at build time

  • IGNORE: unmapped target properties are ignored

If a policy is given for a specific mapper via @Mapper#unmappedTargetPolicy(), the value from the annotation takes precedence. If a policy is given for a specific bean mapping via @BeanMapping#unmappedTargetPolicy(), it takes precedence over both @Mapper#unmappedTargetPolicy() and the option.

WARN

mapstruct.unmappedSourcePolicy

The default reporting policy to be applied in case an attribute of the source object of a mapping method is not populated with a target value.

Supported values are:

  • ERROR: any unmapped source property will cause the mapping code generation to fail

  • WARN: any unmapped source property will cause a warning at build time

  • IGNORE: unmapped source properties are ignored

If a policy is given for a specific mapper via @Mapper#unmappedSourcePolicy(), the value from the annotation takes precedence. If a policy is given for a specific bean mapping via @BeanMapping#ignoreUnmappedSourceProperties(), it takes precedence over both @Mapper#unmappedSourcePolicy() and the option.

WARN

mapstruct. disableBuilders

If set to true, then MapStruct will not use builder patterns when doing the mapping. This is equivalent to doing @Mapper( builder = @Builder( disableBuilder = true ) ) for all of our mappers.

false

Core Features

MapStruct provides a set of core features that allow to map properties between different objects seamlessly. These include:

  • Basic Type Mapping: MapStruct automatically maps properties with the same name and compatible types.

  • Handling Null Values: By default, MapStruct maps null values, but we can customize this behavior.

  • Customizing Mappings with @Mapping: This annotation allows to define how individual fields are mapped.

Data type conversions

It is possible to have mapped attribute with the same type or different in the source and target objects. We need to understand how MapStruct deals with such data type conversions.

Implicit type conversions

Mapstruct applies the following conversion automatically.

  • Between all Java primitive data types and their corresponding wrapper types, e.g. between int and Integer, boolean and Boolean etc. When converting a wrapper type into the corresponding primitive type a null check will be performed.

  • Between all Java primitive number types and the wrapper types, e.g. between int and long or byte and Integer

The Mapper and MapperConfig annotations have a method typeConversionPolicy to control warnings / errors. Due to backward compatibility reasons the default value is ReportingPolicy.IGNORE.

  • Between all Java primitive types (including their wrappers) and String, e.g. between int and String or Booleanand String. A format string as understood by java.text.DecimalFormat can be specified.

@Mapper
public interface CarMapper {

    @Mapping(source = "price", numberFormat = "$#.00")
    CarDto carToCarDto(Car car);

    @IterableMapping(numberFormat = "$#.00")
    List<String> prices(List<Integer> prices);
}
  • Between enum types and String.

  • Between big number types (java.math.BigInteger, java.math.BigDecimal) and Java primitive types (including their wrappers) as well as String. A format string java.text.DecimalFormat can be specified.

@Mapper
public interface EventMapper {

    @Mapping(source = "fee", numberFormat = "#.##E0")
    EventDto eventToEventDto(Event event);

}
  • Between JAXBElement<T> and T, List<JAXBElement<T>> and List<T>

  • Between java.util.Calendar/java.util.Date and JAXB’s XMLGregorianCalendar

  • Between java.util.Date/XMLGregorianCalendar and String. A format string as understood by java.text.SimpleDateFormat can be specified via the dateFormat option

@Mapper
public interface EventMapper {

    @Mapping(source = "paymenDate", dateFormat = "dd.MM.yyyy")
    EventDto eventToEventDto(Event event);

    @IterableMapping(dateFormat = "dd.MM.yyyy")
    List<String> stringListToDateList(List<Date> dates);
}
  • Between Jodas org.joda.time.DateTime, org.joda.time.LocalDateTime, org.joda.time.LocalDate, org.joda.time.LocalTime and String. A format string as understood by java.text.SimpleDateFormat can be specified via the dateFormat option (see above).

  • Between Jodas org.joda.time.DateTime and javax.xml.datatype.XMLGregorianCalendar, java.util.Calendar.

  • Between Jodas org.joda.time.LocalDateTime, org.joda.time.LocalDate and javax.xml.datatype.XMLGregorianCalendar, java.util.Date.

  • Between java.time.LocalDate, java.time.LocalDateTime and javax.xml.datatype.XMLGregorianCalendar.

  • Between java.time.ZonedDateTime, java.time.LocalDateTime, java.time.LocalDate, java.time.LocalTimefrom Java 8 Date-Time package and String. A format string as understood by java.text.SimpleDateFormat can be specified via the dateFormat option (see above).

  • Between java.time.Instant, java.time.Duration, java.time.Period from Java 8 Date-Time package and String using the parse method in each class to map from String and using toString to map into String.

  • Between java.time.ZonedDateTime from Java 8 Date-Time package and java.util.Date where, when mapping a ZonedDateTime from a given Date, the system default timezone is used.

  • Between java.time.LocalDateTime from Java 8 Date-Time package and java.util.Date where timezone UTC is used as the timezone.

  • Between java.time.LocalDate from Java 8 Date-Time package and java.util.Date / java.sql.Date where timezone UTC is used as the timezone.

  • Between java.time.Instant from Java 8 Date-Time package and java.util.Date.

  • Between java.time.ZonedDateTime from Java 8 Date-Time package and java.util.Calendar.

  • Between java.sql.Date and java.util.Date

  • Between java.sql.Time and java.util.Date

  • Between java.sql.Timestamp and java.util.Date

  • When converting from a String, omitting Mapping#dateFormat, it leads to usage of the default pattern and date format symbols for the default locale. An exception to this rule is XmlGregorianCalendar which results in parsing the String according to XML Schema.

  • Between java.util.Currency and String.

    • When converting from a String, the value needs to be a valid ISO-4217 alphabetic code otherwise an IllegalArgumentException is thrown.

  • Between java.util.UUID and String.

    • When converting from a String, the value needs to be a valid UUID otherwise an IllegalArgumentException is thrown.

  • Between String and StringBuilder

  • Between java.net.URL and String.

    • When converting from a String, the value needs to be a valid URL otherwise a MalformedURLException is thrown.

Mapping nested object references

Suppose, Event has reference to Address object.

@Mapper
public interface EventMapper {
    EventDto eventToEventDto(Event event);

    AddressDto addressToAddressDto(Address address);
}

Controlling nested bean mappings

MapStruct will generate a method based on the name of the source and target property. In many occasions these names do not match. The ‘.’ notation in an @Mapping source or target type can be used to control how properties should be mapped when names do not match.

@Mapper
public interface FishTankMapper {
    @Mapping(target = "fish.kind", source = "fish.type")
    @Mapping(target = "fish.name", ignore = true)
    @Mapping(target = "ornament", source = "interior.ornament")
    @Mapping(target = "material.materialType", source = "material")
    @Mapping(target = "quality.report.organisation.name", source = "quality.report.organisationName")
    FishTankDto map( FishTank source );
}

Invoking custom mapping method

Sometimes, some fields require custom logic. For example, MapStruct will take the entire parameter source and generate code to call the custom method mapVolumein order to map the FishTank object to the target property volume.

public class FishTank {
    Fish fish;
    String material;
    Quality quality;
    int length;
    int width;
    int height;
}

public class FishTankWithVolumeDto {
    FishDto fish;
    MaterialDto material;
    QualityDto quality;
    VolumeDto volume;
}

public class VolumeDto {
    int volume;
    String description;
}

@Mapper
public abstract class FishTankMapperWithVolume {

    @Mapping(target = "fish.kind", source = "source.fish.type")
    @Mapping(target = "material.materialType", source = "source.material")
    @Mapping(target = "quality.document", source = "source.quality.report")
    @Mapping(target = "volume", source = "source")
    abstract FishTankWithVolumeDto map(FishTank source);

    VolumeDto mapVolume(FishTank source) {
        int volume = source.length * source.width * source.height;
        String desc = volume < 100 ? "Small" : "Large";
        return new VolumeDto(volume, desc);
    }
}

Invoking other mappers

MapStruct can also invoke mapping methods defined in other classes, be it mappers generated by MapStruct or hand-written mapping methods. For eg, when generating code for the implementation of the carToCarDto() method, MapStruct will look for a method which maps a Date object into a String, find it on the DateMapper class and generate an invocation of asString() for mapping the manufacturingDate attribute.

// Manually implemented class
public class DateMapper {
    public String asString(Date date) {
        return date != null ? new SimpleDateFormat( "yyyy-MM-dd" )
            .format( date ) : null;
    }

    public Date asDate(String date) {
        try {
            return date != null ? new SimpleDateFormat( "yyyy-MM-dd" )
                .parse( date ) : null;
        }
        catch ( ParseException e ) {
            throw new RuntimeException( e );
        }
    }
}

// Using the DateMapper in a mapper
@Mapper(uses=DateMapper.class)
public interface CarMapper {
    CarDto carToCarDto(Car car);
}

Passing context or state objects to custom methods

Additional context or state information can be passed through generated mapping methods to custom methods with @Context parameters. Such parameters are passed to other mapping methods, @ObjectFactory methods or @BeforeMapping / @AfterMapping methods when applicable and can thus be used in custom code.

public abstract CarDto toCar(Car car, @Context Locale translationLocale);

protected OwnerManualDto translateOwnerManual(OwnerManual ownerManual, @Context Locale locale) {
    // manually implemented logic to translate the OwnerManual with the given Locale
}
//GENERATED CODE
public CarDto toCar(Car car, Locale translationLocale) {
    if ( car == null ) {
        return null;
    }

    CarDto carDto = new CarDto();

    carDto.setOwnerManual( translateOwnerManual( car.getOwnerManual(), translationLocale );
    // more generated mapping code

    return carDto;
}

Mapping method resolution

When mapping a property from one type to another, MapStruct looks for the most specific method which maps the source type into the target type. The method may either be declared on the same mapper interface or on another mapper which is registered via @Mapper#uses()

Combining qualifiers @Named with defaults

Default value will be used if the returned value from the @Named method is null.

@Mapper
public interface MovieMapper {
     @Mapping( target = "category", qualifiedByName = "CategoryToString", defaultValue = "DEFAULT" )
     GermanRelease toGerman( OriginalRelease movies );

     @Named("CategoryToString")
     default String defaultValueForQualifier(Category cat) {
         // some mapping logic
     }
}

Basic Mapping

The @Mapper annotation causes the MapStruct code generator to create an implementation of the UserMapper interface during build-time. MapStruct will generate a method based on the name of the source and target property.

  • When a property has the same name as its target entity counterpart, it will be mapped implicitly.

  • When a property has a different name in the target entity, its name can be specified via the @Mapping annotation.

  • The DTO or the objects being mapped should have getter setter to access fields.

  • @BeanMapping with the ignoreByDefault attribute in MapStruct allows us to specify that all properties should be ignored by default, and we can then explicitly specify which properties to include in the mapping. This can be useful for partial updates or when you want to map only a subset of the properties.

  • In some cases, it can be required to manually implement a specific mapping from one type to another which can’t be generated by MapStruct. Use the Default method for such case.

  • In some cases, we may need mappings which don’t create a new instance of the target type but instead update an existing instance of that type. We can achieve this by adding a parameter for the target object and marking this parameter with @MappingTarget. There may be only one parameter marked as mapping target.

  • MapStruct also supports mappings of public fields that have no getters/setters. MapStruct will use the fields as read/write accessor if it cannot find suitable getter/setter methods for the property. A field is considered as a read accessor if it is public or public final. If a field is static it is not considered as a read accessor. A field is considered as a write accessor only if it is public. If a field is final and/or static it is not considered as a write accessor.

package mapper;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(componentModel = "spring")
public interface UserMapper {

    // UserId will be mapped implicitly since field name from both the object is same
    @Mapping(target = "userName", source = "name")
    @Mapping(target = "userEmail", source = "email")
    @Mapping(target = "addressDTO.pincode", source = "pincode")
    UserDTO mapToUserDTO(User user);
    
    // All properties need to map explicitly
    @BeanMapping(ignoreByDefault = true)
    AddressDTO mapToAddressDTO(Address address);
    
    // Implement custom method if it has complex mapping logic which cannot be handled by mapstruct
    default PersonDto personToPersonDto(Person person) {
        //hand-written mapping logic
    }
    
    // Mapping methods with several source parameters
    @Mapping(target = "description", source = "person.description")
    @Mapping(target = "houseNumber", source = "address.houseNo")
    DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
    
    // Mapping nested bean properties to current target. "." as target
    // Generated code will map every property from CustomerDto.record and Customer.account
    // to Customer directly, without manually naming. 
    @Mapping( target = "name", source = "record.name" )
    @Mapping( target = ".", source = "record" )
    @Mapping( target = ".", source = "account" )
    Customer customerDtoToCustomer(CustomerDto customerDto);
    
    // Updating existing bean instances
    // In some cases, we need to update an existing instance of a type rather a new instance
    void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
    
    // We can return the target type as well
    Car updateAndReturnCarFromDto(CarDto carDto, @MappingTarget Car car);
    
    // Here, say CustomerDto has only public fields but no getter setter. Whereas Customer has private fields with getter setter
    // Mapstruct allows mapping even if no getter setter provided it satisfies accessor conditons
    @Mapping(target = "name", source = "customerName")
    Customer toCustomer(CustomerDto customerDto);
    
    // Annotation @InheritInverseConfiguration indicates that a method shall inherit the inverse configuration of the corresponding reverse method.
    @InheritInverseConfiguration
    CustomerDto fromCustomer(Customer customer);
}

Default values and constants

Default values and constants are specified as String values. If the source is null, default value will be used.

@Mapper(uses = StringListMapper.class)
public interface SourceTargetMapper {
    SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );

    @Mapping(target = "stringProperty", source = "stringProp", defaultValue = "undefined")
    @Mapping(target = "longProperty", source = "longProp", defaultValue = "-1")
    @Mapping(target = "stringConstant", constant = "Constant Value")
    @Mapping(target = "integerConstant", constant = "14")
    @Mapping(target = "longWrapperConstant", constant = "3001")
    @Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014")
    @Mapping(target = "stringListConstants", constant = "jack-jill-tom")
    Target sourceToTarget(Source s);
}

Expressions

As per documentation, MapStruct will not validate the java expression at generation-time, but errors will show up in the generated classes during compilation. Fully qualified package name is specified because MapStruct does not take care of the import of the TimeAndFormat class (unless it’s used otherwise explicitly in the SourceTargetMapper). This can be resolved by defining imports on the @Mapper annotation.

@Mapper
public interface SourceTargetMapper {

    SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );

    @Mapping(target = "timeAndFormat",
         expression = "java( new org.sample.TimeAndFormat( s.getTime(), s.getFormat() ) )")
    Target sourceToTarget(Source s);
}


imports org.sample.TimeAndFormat;

@Mapper( imports = TimeAndFormat.class )
public interface SourceTargetMapper {

    SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );

    @Mapping(target = "timeAndFormat",
         expression = "java( new TimeAndFormat( s.getTime(), s.getFormat() ) )")
    Target sourceToTarget(Source s);
}

Default Expressions

Default expressions are a combination of default values and expressions. They will only be used when the source attribute is null.

imports java.util.UUID;

@Mapper( imports = UUID.class )
public interface SourceTargetMapper {

    SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );

    @Mapping(target="id", source="sourceId", defaultExpression = "java( UUID.randomUUID().toString() )")
    Target sourceToTarget(Source s);
}

Subclass Mapping

It is used when both input and result types have an inheritance relation. Suppose an Apple and a Banana, which are both specializations of Fruit.

@Mapper
public interface FruitMapper {
    @SubclassMapping( source = AppleDto.class, target = Apple.class )
    @SubclassMapping( source = BananaDto.class, target = Banana.class )
    Fruit map( FruitDto source );
}

If we would just use a normal mapping both the AppleDto and the BananaDto would be made into a Fruit object, instead of an Apple and a Banana object.

Determining the result type

When result types have an inheritance relation, selecting either mapping method (@Mapping) or a factory method (@BeanMapping) can become ambiguous. Suppose an Apple and a Banana, which are both specializations of Fruit.

public class FruitFactory {
    public Apple createApple() {
        return new Apple( "Apple" );
    }

    public Banana createBanana() {
        return new Banana( "Banana" );
    }
}

@Mapper( uses = FruitFactory.class )
public interface FruitMapper {
    @BeanMapping( resultType = Apple.class )
    Fruit map( FruitDto source );
}

Controlling mapping result for 'null' arguments

As per documentation, when the source argument of the mapping method equals null then by default null will be returned.

However, by specifying nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT on @BeanMapping, @IterableMapping, @MapMapping, or globally on @Mapper or @MapperConfig, the mapping result can be altered to return empty default values. This means for:

  • Bean mappings: an 'empty' target bean will be returned, with the exception of constants and expressions, they will be populated when present.

  • Iterables / Arrays: an empty iterable will be returned.

  • Maps: an empty map will be returned.

Source presence checking and Conditional Mapping

Some frameworks generate bean properties that have a source presence checker. Often this is in the form of a method hasXYZ, XYZ being a property on the source bean in a bean mapping method. MapStruct will call this hasXYZ instead of performing a null check when it finds such hasXYZ method.

A custom condition method is a method that is annotated with org.mapstruct.Condition and returns boolean.

e.g. if you only want to map a String property when it is not `null, and it is not empty then you can do something like:

@Mapper
public interface CarMapper {
    CarDto carToCarDto(Car car);

    @Condition
    default boolean isNotEmpty(String value) {
        return value != null && !value.isEmpty();
    }
}

When using this in combination with an update mapping method it will replace the null-check there, for example:

@Mapper
public interface CarMapper {
    CarDto carToCarDto(Car car, @MappingTarget CarDto carDto);

    @Condition
    default boolean isNotEmpty(String value) {
        return value != null && !value.isEmpty();
    }
}

Exceptions

Calling applications may require handling of exceptions when calling a mapping method. These exceptions could be thrown by hand-written logic and by the generated built-in mapping methods or type-conversions of MapStruct.

@Mapper(uses = HandWritten.class)
public interface CarMapper {
    CarDto carToCarDto(Car car) throws GearException;
}

public class HandWritten {
    private static final String[] GEAR = {"ONE", "TWO", "THREE", "OVERDRIVE", "REVERSE"};

    public String toGear(Integer gear) throws GearException, FatalException {
        if ( gear == null ) {
            throw new FatalException("null is not a valid gear");
        }

        if ( gear < 0 && gear > GEAR.length ) {
            throw new GearException("invalid gear");
        }
        return GEAR[gear];
    }
}

Using builders

MapStruct also supports mapping of immutable types via builders. When performing a mapping MapStruct checks if there is a builder for the type being mapped. This is done via the BuilderProvider SPI. If a Builder exists for a certain type, then that builder will be used for the mappings.

// Builder Pattern for Person class
public class Person {
    private final String name;

    protected Person(Person.Builder builder) {
        this.name = builder.name;
    }
    public static Person.Builder builder() {
        return new Person.Builder();
    }

    public static class Builder {

        private String name;

        public Builder name(String name) {
            this.name = name;
            return this;
        }
        public Person create() {
            return new Person( this );
        }
    }
}

// Mapstruct mapper
@Mapper(componentModel = "spring")
public interface PersonMapper {
    Person map(PersonDto dto);
}

Using Constructors

MapStruct supports using constructors for mapping target types. When doing a mapping MapStruct checks if there is a builder for the type being mapped. If there is no builder, then MapStruct looks for a single accessible constructor.

  • If a constructor is annotated with an annotation named @Default it will be used.

  • If a single public constructor exists then it will be used to construct the object, and the other non public constructors will be ignored.

  • If a parameterless constructor exists then it will be used to construct the object, and the other constructors will be ignored.

  • If there are multiple eligible constructors then there will be a compilation error due to ambiguous constructors.

public class Vehicle {
    protected Vehicle() { }
    // MapStruct will use this constructor, because it is a single public constructor
    public Vehicle(String color) { }
}

public class Car {
    // MapStruct will use this constructor, because it is a parameterless empty constructor
    public Car() { }
    public Car(String make, String color) { }
}

public class Truck {
    public Truck() { }
    // MapStruct will use this constructor, because it is annotated with @Default
    @Default
    public Truck(String make, String color) { }
}

public class Van {
    // There will be a compilation error when using this class because MapStruct cannot pick a constructor
    public Van(String make) { }
    public Van(String make, String color) { }
}

// Mapper
@Mapper(componentModel = "spring")
public interface PersonMapper {
    Person map(PersonDto dto);
}

Mapping Map to Bean

We want to map Map<String, ???> into a specific bean. When a raw map or a map that does not have a String as a key is used, then a warning will be generated.

public class Customer {
    private Long id;
    private String name;

    //getters and setter omitted for brevity
}

@Mapper
public interface CustomerMapper {
    // Here, source act as key. Mapstruct will try to use map.containsKey( "customerName" ) to check and then ex
    // map.get( "id" ) to get the value and assign to target
    @Mapping(target = "name", source = "customerName")
    Customer toCustomer(Map<String, String> map);
}

Retrieving a mapper in another class to use its methods

Without using DI framework

CarMapper mapper = Mappers.getMapper( CarMapper.class );

But with this, we need to repeatedly instantiating new instances if we need to use it in several classes. To fix this, a mapper interface should define a member called INSTANCE which holds a single instance of the mapper type.

// Declaring an instance of a mapper (interface)
@Mapper(componentModel = ComponentModel.SPRING)
public interface CarMapper {

    CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );

    CarDto carToCarDto(Car car);
}

// Declaring an instance of a mapper (abstract class)
@Mapper
public abstract class CarMapper {

    public static final CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );

    CarDto carToCarDto(Car car);
}

// Usage in other classes
CarDto dto = CarMapper.INSTANCE.carToCarDto( car );

Using DI framework

When using Spring Framework, it is recommended to obtain mapper objects via dependency injection and not via the Mappersclass as described above.

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface EventMapper {
...
}

// Usage
@RequiredArgsConstructor
@Component
public class SomeEventProcessor {
...
private final EventMapper eventMapper;
...
..
}

Injection strategy

When using dependency injection, we can choose between field and constructor injection. This can be done by providing injection strategy via @Mapper or @MapperConfig annotation. Constructor injection is recommended to simplify testing. As per documentation, for abstract classes or decorators setter injection should be used.

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface CarMapper {
    CarDto carToCarDto(Car car);
}

Composition Mapping

MapStruct supports the use of meta annotations like @Retention. This allows @Mapping to be used on other (user defined) annotations for re-use purposes.

For example below. The @ToTransactionHeader assumes both target beans TransactionEntity and PaymentEntity have properties: "id", "creationDate" and "name"

@Retention(RetentionPolicy.CLASS)
@Mapping(target = "id", ignore = true)
@Mapping(target = "creationDate", expression = "java(new java.util.Date())")
@Mapping(target = "name", source = "groupName")
public @interface ToTransactionHeader { }

@Mapper
public interface TransactionMapper {

    TransactionMapper INSTANCE = Mappers.getMapper( TransactionMapper.class );

    @ToTransactionHeader
    @Mapping( target = "type", source = "type")
    TransactionEntity map(TransactionDto source);

    @ToTransactionHeader
    @Mapping( target = "accountId", source = "accountId")
    PaymentEntity map(PaymentDto source);
}

Mapping collections

As per documentation, the mapping of collection types (List, Set etc.) is done in the same way as mapping bean types, i.e. by defining mapping methods with the required source and target types in a mapper interface

@Mapper
public interface CarMapper {
    Set<String> integerSetToStringSet(Set<Integer> integers);

    List<CarDto> carsToCarDtos(List<Car> cars);

    CarDto carToCarDto(Car car);
}

MapStruct has a CollectionMappingStrategy, with the possible values: ACCESSOR_ONLY, SETTER_PREFERRED, ADDER_PREFERRED and TARGET_IMMUTABLE. The option DEFAULT is synonymous to ACCESSOR_ONLY.

An adder method is typically used in case of generated (JPA) entities, to add a single element (entity) to an underlying collection. Invoking the adder establishes a parent-child relation between parent - the bean (entity) on which the adder is invoked - and its child(ren), the elements (entities) in the collection

Mapping maps

public interface SourceTargetMapper {
    @MapMapping(valueDateFormat = "dd.MM.yyyy")
    Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
}

Implementation types used for collection mappings

As per documentation, when an iterable or map mapping method declares an interface type as return type, one of its implementation types will be instantiated in the generated code.

Interface type
Implementation type

Iterable

ArrayList

Collection

ArrayList

List

ArrayList

Set

LinkedHashSet

SortedSet

TreeSet

NavigableSet

TreeSet

Map

LinkedHashMap

SortedMap

TreeMap

NavigableMap

TreeMap

ConcurrentMap

ConcurrentHashMap

ConcurrentNavigableMap

ConcurrentSkipListMap

Mapping Streams

As per documentation, mapping of java.util.Stream is done in a similar way as the mapping of collection types, i.e. by defining mapping methods with the required source and target types in a mapper interface.

@Mapper
public interface CarMapper {
    Set<String> integerStreamToStringSet(Stream<Integer> integers);

    List<CarDto> carsToCarDtos(Stream<Car> cars);

    CarDto carToCarDto(Car car);
}

Mapping Values

Mapping enum to enum types

@Mapper
public interface OrderMapper {
    OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class );

    @ValueMappings({
        @ValueMapping(target = "SPECIAL", source = "EXTRA"),
        @ValueMapping(target = "DEFAULT", source = "STANDARD"),
        @ValueMapping(target = "DEFAULT", source = "NORMAL")
    })
    ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
}

As per documentation, MapStruct also support for mapping any remaining (unspecified) mappings to a default. This can be used only once in a set of value mappings and only applies to the source. It comes in two flavors: <ANY_REMAINING> and <ANY_UNMAPPED>. They cannot be used at the same time.

In case of source <ANY_REMAINING> MapStruct will continue to map a source enum constant to a target enum constant with the same name. The remainder of the source enum constants will be mapped to the target specified in the @ValueMapping with <ANY_REMAINING> source.

MapStruct will not attempt such name based mapping for <ANY_UNMAPPED> and directly apply the target specified in the @ValueMapping with <ANY_UNMAPPED> source to the remainder.

MapStruct is able to handle null sources and null targets by means of the <NULL> keyword.

In addition, the constant value <THROW_EXCEPTION> can be used for throwing an exception for particular value mappings.

@Mapper
public interface SpecialOrderMapper {

    SpecialOrderMapper INSTANCE = Mappers.getMapper( SpecialOrderMapper.class );

    // MapStruct would have refrained from mapping the RETAIL and B2B when <ANY_UNMAPPED> was used instead of <ANY_REMAINING>
    @ValueMappings({
        @ValueMapping( source = MappingConstants.NULL, target = "DEFAULT" ),
        @ValueMapping( source = "STANDARD", target = MappingConstants.NULL ),
        @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "SPECIAL" )
    })
    ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
}
@Mapper
public interface SpecialOrderMapper {

    SpecialOrderMapper INSTANCE = Mappers.getMapper( SpecialOrderMapper.class );

    @ValueMappings({
        @ValueMapping( source = "STANDARD", target = "DEFAULT" ),
        @ValueMapping( source = "C2C", target = MappingConstants.THROW_EXCEPTION )
    })
    ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
}

Mapping enum-to-String or String-to-enum

MapStruct supports enum to a String mapping on the similar lines.

Custom name transformation

As per documentation, when no @ValueMapping(s) are defined then each constant from the source enum is mapped to a constant with the same name in the target enum type. However, there are cases where the source enum needs to be transformed before doing the mapping. E.g. a suffix needs to be applied to map from the source into the target enum.

MapStruct provides the following enum name transformation strategies:

  • suffix - Applies a suffix on the source enum

  • stripSuffix - Strips a suffix from the source enum

  • prefix - Applies a prefix on the source enum

  • stripPrefix - Strips a prefix from the source enum

  • case - Applies case transformation to the source enum. Supported case transformations are:

    • upper - Performs upper case transformation to the source enum

    • lower - Performs lower case transformation to the source enum

    • capital - Performs capitalisation of the first character of every word in the source enum and everything else to lowercase. A word is split by "_"

@Mapper
public interface CheeseMapper {
    CheeseMapper INSTANCE = Mappers.getMapper( CheeseMapper.class );

    @EnumMapping(nameTransformationStrategy = "suffix", configuration = "_TYPE")
    CheeseTypeSuffixed map(CheeseType cheese);

    @InheritInverseConfiguration
    CheeseType map(CheeseTypeSuffix cheese);
}

Object factories

By default, the generated code for mapping one bean type into another or updating a bean will call the default constructor to instantiate the target type. Alternatively, Mapstruct supports custom object factories which will be invoked to obtain instances of the target type.

public class DtoFactory {
     public CarDto createCarDto() {
         return // ... custom factory logic
     }
}

public class EntityFactory {
     public <T extends BaseEntity> T createEntity(@TargetType Class<T> entityClass) {
         return // ... custom factory logic
     }
}

@Mapper(uses= { DtoFactory.class, EntityFactory.class } )
public interface CarMapper {
    CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );

    CarDto carToCarDto(Car car);

    Car carDtoToCar(CarDto carDto);
}

@Mapper(uses = { DtoFactory.class, EntityFactory.class, CarMapper.class } )
public interface OwnerMapper {

    OwnerMapper INSTANCE = Mappers.getMapper( OwnerMapper.class );

    void updateOwnerDto(Owner owner, @MappingTarget OwnerDto ownerDto);

    void updateOwner(OwnerDto ownerDto, @MappingTarget Owner owner);
}

Reusing mapping configurations

Mapping configuration inheritance

Method-level configuration annotations such as @Mapping, @BeanMapping, @IterableMapping, etc., can be inheritedfrom one mapping method to a similar method using the annotation @InheritConfiguration

@Mapper
public interface CarMapper {
    @Mapping(target = "numberOfSeats", source = "seatCount")
    Car carDtoToCar(CarDto car);

    @InheritConfiguration
    void carDtoIntoCar(CarDto carDto, @MappingTarget Car car);
}

Inverse mappings

In case of bi-directional mappings, e.g. from entity to DTO and from DTO to entity, the mapping rules for the forward method and the reverse method are often similar and can simply be inversed by switching source and target

@Mapper
public interface CarMapper {

    @Mapping(target = "seatCount", source = "numberOfSeats")
    CarDto carToDto(Car car);

    @InheritInverseConfiguration
    @Mapping(target = "numberOfSeats", ignore = true)
    Car carDtoToCar(CarDto carDto);
}

Shared configurations

MapStruct offers the possibility to define a shared configuration by pointing to a central interface annotated with @MapperConfig. For a mapper to use the shared configuration, the configuration interface needs to be defined in the @Mapper#config property. Attributes specified in @Mapper take precedence over the attributes specified via the referenced configuration class

@MapperConfig(
    uses = CustomMapperViaMapperConfig.class,
    unmappedTargetPolicy = ReportingPolicy.ERROR
)
public interface CentralConfig {
}

@Mapper(config = CentralConfig.class, uses = { CustomMapperViaMapper.class } )
// Effective configuration:
// @Mapper(
//     uses = { CustomMapperViaMapper.class, CustomMapperViaMapperConfig.class },
//     unmappedTargetPolicy = ReportingPolicy.ERROR
// )
public interface SourceTargetMapper {
  ...
}

Customizing mappings

If we need to apply custom logic before or after certain mapping methods, MapStruct provides two ways for doing so: decorators which allow for a type-safe customization of specific mapping methods and the before-mapping and after-mapping lifecycle methods which allow for a generic customization of mapping methods with given source or target types.

Mapping customization with before-mapping and after-mapping methods

@Mapper
public abstract class VehicleMapper {
    @BeforeMapping
    protected void flushEntity(AbstractVehicle vehicle) {
        // I would call my entity manager's flush() method here to make sure my entity
        // is populated with the right @Version before I let it map into the DTO
    }

    @AfterMapping
    protected void fillTank(AbstractVehicle vehicle, @MappingTarget AbstractVehicleDto result) {
        result.fuelUp( new Fuel( vehicle.getTankCapacity(), vehicle.getFuelType() ) );
    }

    public abstract CarDto toCarDto(Car car);
}

Conditional Mapping is a type of . The difference is that it allows users to write custom condition methods that will be invoked to check if a property needs to be mapped or not.

https://mapstruct.org/documentation/1.5/reference/html/
Source presence checking