{"id":2069,"date":"2026-01-19T13:53:21","date_gmt":"2026-01-19T00:53:21","guid":{"rendered":"https:\/\/www.ronella.xyz\/?p=2069"},"modified":"2026-01-19T13:53:21","modified_gmt":"2026-01-19T00:53:21","slug":"mapstruct-mappingtarget-and-targettype-with-pure-java","status":"publish","type":"post","link":"https:\/\/www.ronella.xyz\/?p=2069","title":{"rendered":"MapStruct: MappingTarget and TargetType with Pure Java"},"content":{"rendered":"<p>MapStruct's <code>@MappingTarget<\/code> and <code>@TargetType<\/code> annotations solve distinct mapping challenges. <code>@MappingTarget<\/code> enables in-place updates of existing objects, while <code>@TargetType<\/code> provides runtime type information for dynamic object creation.<\/p>\n<h2>Core Concepts<\/h2>\n<p><strong>@MappingTarget<\/strong> marks a parameter as an existing target for modification rather than creating a new instance. Use it for DTO-to-entity updates where the target object already exists in memory (e.g., from a database).<\/p>\n<p><strong>@TargetType<\/strong> injects the concrete target <code>Class&lt;T&gt;<\/code> as a parameter, essential for generic factories and lifecycle methods where compile-time type information is erased.<\/p>\n<h2>Complete Working Example<\/h2>\n<pre><code class=\"language-java\">import org.mapstruct.*;\nimport java.lang.reflect.Constructor;\n\n\/\/ 1. Plain Java DTOs and Entities\nclass CarDto {\n    private String make;\n    private String model;\n    private int year;\n\n    public CarDto() {}\n\n    public CarDto(String make, String model, int year) {\n        this.make = make;\n        this.model = model;\n        this.year = year;\n    }\n\n    \/\/ Getters and setters\n    public String getMake() { return make; }\n    public void setMake(String make) { this.make = make; }\n\n    public String getModel() { return model; }\n    public void setModel(String model) { this.model = model; }\n\n    public int getYear() { return year; }\n    public void setYear(int year) { this.year = year; }\n}\n\nclass CarEntity {\n    private String make;\n    private String model;\n    private int year;\n    private String vin;  \/\/ Entity-specific field\n\n    public CarEntity() {}\n\n    \/\/ Getters and setters\n    public String getMake() { return make; }\n    public void setMake(String make) { this.make = make; }\n\n    public String getModel() { return model; }\n    public void setModel(String model) { this.model = model; }\n\n    public int getYear() { return year; }\n    public void setYear(int year) { this.year = year; }\n\n    public String getVin() { return vin; }\n    public void setVin(String vin) { this.vin = vin; }\n}\n\n\/\/ 2. @TargetType Custom Factory\nclass CarEntityFactory {\n    public &lt;T extends CarEntity&gt; T createEntity(@TargetType Class&lt;T&gt; entityClass) {\n        try {\n            Constructor&lt;T&gt; constructor = entityClass.getDeclaredConstructor();\n            T entity = constructor.newInstance();\n            entity.setVin(&quot;VIN-&quot; + entityClass.getSimpleName());\n            return entity;\n        } catch (Exception e) {\n            throw new RuntimeException(&quot;Cannot create entity: &quot; + entityClass.getName(), e);\n        }\n    }\n}\n\n\/\/ 3. Mapper Demonstrating Both Annotations\n@Mapper(uses = CarEntityFactory.class)\ninterface CarMapper {\n\n    \/\/ Creates NEW object using @TargetType factory\n    CarEntity toEntity(CarDto dto);\n\n    \/\/ Updates EXISTING object - @MappingTarget required\n    void updateEntity(@MappingTarget CarEntity entity, CarDto dto);\n\n    \/\/ Updates and returns - also uses @MappingTarget\n    CarEntity updateAndReturn(@MappingTarget CarEntity entity, CarDto dto);\n}\n\n\/\/ 4. Advanced Mapper with Lifecycle Methods\n@Mapper(uses = CarEntityFactory.class)\nabstract class AdvancedCarMapper {\n\n    public abstract CarEntity toEntity(CarDto dto);\n\n    @BeforeMapping\n    void beforeUpdate(CarDto source,\n                              @MappingTarget CarEntity target,\n                              @TargetType Class&lt;?&gt; targetType) {\n        System.out.println(&quot;\ud83d\udd04 Before: Updating &quot; + targetType.getSimpleName());\n        System.out.println(&quot;   Target VIN before: &quot; + target.getVin());\n    }\n\n    @AfterMapping\n    void afterUpdate(CarDto source,\n                             @MappingTarget CarEntity target,\n                             @TargetType Class&lt;?&gt; targetType) {\n        System.out.println(&quot;\u2705 After: Updated &quot; + targetType.getSimpleName());\n        System.out.println(&quot;   Target VIN after: &quot; + target.getVin());\n    }\n}<\/code><\/pre>\n<h2>Usage Demonstration<\/h2>\n<pre><code class=\"language-java\">import org.mapstruct.factory.Mappers;\n\npublic class MapStructDemo {\n    public static void main(String[] args) {\n        CarMapper mapper = Mappers.getMapper(CarMapper.class);\n        AdvancedCarMapper advancedMapper = Mappers.getMapper(AdvancedCarMapper.class);\n\n        System.out.println(&quot;=== 1. NEW Object Creation (uses @TargetType factory) ===&quot;);\n        CarDto newDto = new CarDto(&quot;Toyota&quot;, &quot;Camry&quot;, 2023);\n        CarEntity newEntity = mapper.toEntity(newDto);\n        System.out.println(&quot;New entity: &quot; + newEntity.getMake() + &quot; VIN: &quot; + newEntity.getVin());\n        \/\/ Output: VIN-CarEntity\n\n        System.out.println(&quot;\\n=== 2. Update Existing (uses @MappingTarget) ===&quot;);\n        CarEntity existingEntity = new CarEntity();\n        existingEntity.setVin(&quot;EXISTING-123&quot;);\n        CarDto updateDto = new CarDto(&quot;Honda&quot;, &quot;Civic&quot;, 2022);\n\n        mapper.updateEntity(existingEntity, updateDto);\n        System.out.println(&quot;Updated: &quot; + existingEntity.getModel() + &quot; VIN preserved: &quot; + existingEntity.getVin());\n        \/\/ VIN preserved! Only mapped fields updated\n\n        System.out.println(&quot;\\n=== 3. Lifecycle Hooks (both annotations) ===&quot;);\n        advancedMapper.toEntity(new CarDto(&quot;Ford&quot;, &quot;Mustang&quot;, 2024));\n        \/\/ Prints before\/after messages with type info\n    }\n}<\/code><\/pre>\n<h2>Expected Output<\/h2>\n<pre><code class=\"language-shell\">=== 1. NEW Object Creation (uses @TargetType factory) ===\nNew entity: Toyota VIN: VIN-CarEntity\n\n=== 2. Update Existing (uses @MappingTarget) ===\nUpdated: Civic VIN preserved: EXISTING-123\n\n=== 3. Lifecycle Hooks (both annotations) ===\n\ud83d\udd04 Before: Updating CarEntity\n   Target VIN before: null\n\u2705 After: Updated CarEntity\n   Target VIN after: VIN-CarEntity<\/code><\/pre>\n<h2>Decision Matrix: When to Use Each<\/h2>\n<table>\n<thead>\n<tr>\n<th>Scenario<\/th>\n<th><code>@MappingTarget<\/code><\/th>\n<th><code>@TargetType<\/code><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>DTO \u2192 New Entity<\/td>\n<td>\u274c No<\/td>\n<td>\u2705 Factory methods<\/td>\n<\/tr>\n<tr>\n<td>Partial DTO Update<\/td>\n<td>\u2705 Always<\/td>\n<td>\u274c No<\/td>\n<\/tr>\n<tr>\n<td>Generic Collections<\/td>\n<td>\u274c No<\/td>\n<td>\u2705 Essential<\/td>\n<\/tr>\n<tr>\n<td>Lifecycle Processing<\/td>\n<td>\u2705 Target param<\/td>\n<td>\u2705 Type param<\/td>\n<\/tr>\n<tr>\n<td>Polymorphic Mapping<\/td>\n<td>\u274c No<\/td>\n<td>\u2705 Required<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>This <strong>pure Java<\/strong> example demonstrates both annotations in realistic enterprise scenarios, showing the clear distinction between new object creation and in-place updates.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>MapStruct&#8217;s @MappingTarget and @TargetType annotations solve distinct mapping challenges. @MappingTarget enables in-place updates of existing objects, while @TargetType provides runtime type information for dynamic object creation. Core Concepts @MappingTarget marks a parameter as an existing target for modification rather than creating a new instance. Use it for DTO-to-entity updates where the target object already exists [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[91],"tags":[],"_links":{"self":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2069"}],"collection":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2069"}],"version-history":[{"count":1,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2069\/revisions"}],"predecessor-version":[{"id":2070,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2069\/revisions\/2070"}],"wp:attachment":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2069"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2069"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2069"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}