☕
【MapStruct】Javaでオブジェクトの構成が複雑な場合のコピー(マッピング)方法
はじめに
この記事では、Javaのアノテーションプロセッサ型ライブラリである「MapStruct」を使う上での設定方法や実装例を載せています。
MapStructを使用するメリット
マッピング処理を1から手書きする必要が無く、高速かつ型安全に自動生成が実現可能!
開発環境
・Java:21
・SpringBoot:3.4.5
・MapStruct:1.5.5.Final
・Maven
pom.xmlの設定
<properties>
<java.version>21</java.version>
<mapstruct.version>1.5.5.Final</org.mapstruct.version>
</properties>
<!-- MapStruct用の依存関係 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
動作確認用のリクエスト内容
{
"sampleNo": "サンプル_001",
"requestA": {
"requestNo": "A_001",
"requestC" :{
"requestNo": "C_001"
}
},
"requestB": [
{
"requestNo": "B_001"
},
{
"requestNo": "B_002"
}
]
}
実装
パッケージ構成(サンプル)
src/main/java
| - com/example/demo
| - application
| | - dto
| | | - RequestADto.java
| | | - RequestBDto.java
| | | - RequestCDto.java
| | | - SampleRequestDto.java
| | - service(中身は脱線するため割愛)
| - mapper
| | - SampleMapper.java
| - presentation
| | - dto
| | | - RequestA.java
| | | - RequestB.java
| | | - RequestC.java
| | | - SampleRequest.java
| | - SampleController.java
| - SampleMapStructApplication.java
マッピングをする上で超重要なこと
コピー元オブジェクト内の変数名と、コピー先オブジェクト内の変数名を一致させる必要がある。
例)
・コピー元オブジェクトのフィールド
private RequestA requestA;
・コピー先オブジェクトのフィールド
private RequestADto requestA;
コピー元オブジェクト
・SampleRequest.java
package com.example.demo.presentation.dto;
import java.util.List;
import lombok.Data;
@Data
public class SampleRequest {
private String sampleNo;
private RequestA requestA;
private List<RequestB> requestB;
}
・RequestA.java
package com.example.demo.presentation.dto;
import lombok.Data;
@Data
public class RequestA {
private String requestNo;
private RequestC requestC;
}
・RequestB.java
package com.example.demo.presentation.dto;
import lombok.Data;
@Data
public class RequestB {
private String requestNo;
}
・RequestC.java
package com.example.demo.presentation.dto;
import lombok.Data;
@Data
public class RequestC {
private String requestNo;
}
コピー先オブジェクト
・SampleRequestDto.java
package com.example.demo.application.dto;
import java.util.List;
import lombok.Data;
@Data
public class SampleRequestDto {
private String sampleNo;
private RequestADto requestA;
private List<RequestBDto> requestB;
}
・RequestADto.java
package com.example.demo.application.dto;
import lombok.Data;
@Data
public class RequestADto {
private String requestNo;
private RequestCDto requestC;
}
・RequestBDto.java
package com.example.demo.application.dto;
import lombok.Data;
@Data
public class RequestBDto {
private String requestNo;
}
・RequestCDto.java
package com.example.demo.application.dto;
import lombok.Data;
@Data
public class RequestCDto {
private String requestNo;
}
マッピング用ファイル
package com.example.demo.mapper;
import org.mapstruct.Mapper;
import com.example.demo.application.dto.SampleRequestDto;
import com.example.demo.presentation.dto.SampleRequest;
@Mapper(componentModel = "spring")
public interface SampleMapper {
SampleRequestDto toSampleRequestCopyTo(SampleRequest sampleRequest);
}
使用方法の例(呼び出し元)
・SampleController.java
package com.example.demo.presentation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.application.dto.SampleRequestDto;
import com.example.demo.mapper.SampleMapper;
import com.example.demo.presentation.dto.SampleRequest;
@RestController
public class SampleController {
@Autowired
SampleMapper sampleMapper;
@PostMapping("/test-map-struct")
public void testMapStruct(@RequestBody SampleRequest request) {
SampleRequestDto sampleRequestDto = sampleMapper.toSampleRequestCopyTo(request);
// Serviceなどへの後続処理
}
}
アプリ起動に失敗する場合、コンパイルが必要な可能性あり!?
理由:
使用しているIDEや設定によっては、アノテーションプロセッサが動かないか、もしくはMapStructが自動で効かない場合があるため
・Eclipseの場合
アプリ上で右クリック → 実行 → Maven Build → ゴールに「clean compile」を設定 → 実行
<イメージ>
・成功するとtargetフォルダ配下に実装クラスが自動生成される
<イメージ>
自動生成された実装クラス
・SampleMapperImpl.java
package com.example.demo.mapper;
import com.example.demo.application.dto.RequestADto;
import com.example.demo.application.dto.RequestBDto;
import com.example.demo.application.dto.RequestCDto;
import com.example.demo.application.dto.SampleRequestDto;
import com.example.demo.presentation.dto.RequestA;
import com.example.demo.presentation.dto.RequestB;
import com.example.demo.presentation.dto.RequestC;
import com.example.demo.presentation.dto.SampleRequest;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.processing.Generated;
import org.springframework.stereotype.Component;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2025-05-10T21:42:36+0900",
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 21.0.3 (Eclipse Adoptium)"
)
@Component
public class SampleMapperImpl implements SampleMapper {
@Override
public SampleRequestDto toSampleRequestCopyTo(SampleRequest sampleRequest) {
if ( sampleRequest == null ) {
return null;
}
SampleRequestDto sampleRequestDto = new SampleRequestDto();
sampleRequestDto.setSampleNo( sampleRequest.getSampleNo() );
sampleRequestDto.setRequestA( requestAToRequestADto( sampleRequest.getRequestA() ) );
sampleRequestDto.setRequestB( requestBListToRequestBDtoList( sampleRequest.getRequestB() ) );
return sampleRequestDto;
}
protected RequestCDto requestCToRequestCDto(RequestC requestC) {
if ( requestC == null ) {
return null;
}
RequestCDto requestCDto = new RequestCDto();
requestCDto.setRequestNo( requestC.getRequestNo() );
return requestCDto;
}
protected RequestADto requestAToRequestADto(RequestA requestA) {
if ( requestA == null ) {
return null;
}
RequestADto requestADto = new RequestADto();
requestADto.setRequestNo( requestA.getRequestNo() );
requestADto.setRequestC( requestCToRequestCDto( requestA.getRequestC() ) );
return requestADto;
}
protected RequestBDto requestBToRequestBDto(RequestB requestB) {
if ( requestB == null ) {
return null;
}
RequestBDto requestBDto = new RequestBDto();
requestBDto.setRequestNo( requestB.getRequestNo() );
return requestBDto;
}
protected List<RequestBDto> requestBListToRequestBDtoList(List<RequestB> list) {
if ( list == null ) {
return null;
}
List<RequestBDto> list1 = new ArrayList<RequestBDto>( list.size() );
for ( RequestB requestB : list ) {
list1.add( requestBToRequestBDto( requestB ) );
}
return list1;
}
}
Discussion