Sitemap
Arif Dwi Prasetya

Arif Dwi Prasetya

Springboot 4 — MapStruct (Indonesia)

5 min readApr 17, 2026

--

Press enter or click to view image in full size

Hari ke 5 konsisten nulis. Pengin clean arsitektur tapi males mapping dari entity ke dto? Tiap nambah property masa harus mapping lagi, mapping lagi. Ahh aku malas.

MapStuct hadir untuk masalah kemalasan anda, ditambah dengan lombok ter manjakalan diriku ini saja. Langung saja ya.

Press enter or click to view image in full size
Selalu mulai dari https://start.spring.io/

Buat project dummy-dummy manja dulu yee. Extract, open dengan IDE kesayangan anda.

Edit pom untuk memasukan libnya, tambah juga buat swagger biar enak ngetesnye ye

<properties>
..........
<org.mapstruct.version>1.6.2</org.mapstruct.version>
<version.mapstruct-lombok>0.2.0</version.mapstruct-lombok>
</properties>

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${version.mapstruct-lombok}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>3.0.0</version>
</dependency>

Masih di pom, tambahkan ini dibawah build>plugins>plugin>articatID (maven-compiler-plugin)

<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>

Contoh lengkap pomnya

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zarta</groupId>
<artifactId>mapasik</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name/>
<description/>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>21</java.version>
<org.mapstruct.version>1.6.2</org.mapstruct.version>
<version.mapstruct-lombok>0.2.0</version.mapstruct-lombok>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-h2console</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${version.mapstruct-lombok}</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>3.0.0</version>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</execution>
<execution>
<id>default-testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>

Sip lanjut ya, kita seting2 dulu buat jpa ama h2 nye:

application.yaml

spring:
application:
name: mapasik

datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password: ""

h2:
console:
enabled: true
path: /h2-console

jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: update
show-sql: true

server:
port: 8080

Buat model sama dtonya ye

Bank

@Data
@Entity
@Table(name = "bank")
public class Bank {

@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long id;
private String name;
private String account;
}

BankDto

@Data
public class BankDto {
private Long id;
private String name;
private String account;
}

Nah sekarang acara utamanya nih, kita buat untuk mappernya

@Mapper(
componentModel = "spring",
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface BankMapper {

BankDto toDto(Bank entity);

Bank toEntity(BankDto dto);

@Mapping(target = "id", ignore = true)
void update(@MappingTarget Bank target, BankDto source);

List<BankDto> toListDto(List<Bank> entities);

List<Bank> toDtoList(List<BankDto> dtos);

}
componentModel = "spring",

Ini supaya bisa diinject2 manja dan dijadikan springbean

nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)

Kalau ini supaya misal kita mapping ada yg null otomatis ngecek misal, sebelum mapper,

Bank bank = new Bank();
bank.setName(null)

// setelah tambah NullValuePropertyMappingStrategy.IGNORE
Bank bank = new Bank();
if (dto.getName != null){
bank.setName(dto.getName)
}

Jadi tidak ketiban, alias ketika null di ignore

@Mapping(target = "id", ignore = true)

Kalau ini supaya mapping dengan variable id selalu diignore, soalnya buat update kan. Jadi id itu tidak perlu ditiban dengan data baru.

Get Arif Dwi Prasetya’s stories in your inbox

Join Medium for free to get updates from this writer.

Catatan, supaya mapping otomatis nama variable di model dan dto harus sama. kalau tidak kita bisa pakai mapping kusus.

Misal di model Bank nama variablenya name, tapi di dto description. Bisa pakai ini

@Mapping(target = "description", source = "name")
OrganizationDto toDto(Organization organization);

Oke lanjut, kita bakal buat repo, service, dan implementasinya dulu

BankRepository

public interface BankRepository extends JpaRepository<Bank, Long> {
}

BankService

public interface BankService {
void save(BankDto dto);

void update(Long id, BankDto dto);

void delete(Long id);

List<BankDto> findAll();
}

BankServiceImpl

@Service
public class BankServiceImpl implements BankService {

private final BankRepository bankRepository;
private final BankMapper bankMapper;

public BankServiceImpl(BankRepository bankRepository, BankMapper bankMapper) {
this.bankRepository = bankRepository;
this.bankMapper = bankMapper;
}

@Override
public void save(BankDto dto) {
bankRepository.save(bankMapper.toEntity(dto));
}

@Override
public void update(Long id, BankDto dto) {
Bank entity = findById(id);
bankMapper.update(entity, dto);
bankRepository.save(entity);
}

@Override
public void delete(Long id) {
Bank entity = findById(id);
bankRepository.delete(entity);
}

@Override
public List<BankDto> findAll() {
return bankMapper.toListDto(bankRepository.findAll());
}

Bank findById(Long id) {
return bankRepository.findById(id).orElseThrow(() -> new RuntimeException("Id not found"));
}
}

Taraa lihat mapper tinggal kita inject saja pakai constructor injection, sip yaa. Trus lihat kita tidak perlu mapping toDto toEntity toDtoList, apalagi kalau propertynya banyak, beh males banget kan. Sekarang lebih clean kan yaa.

Lanjut kita buat controller nya

BankController

@RestController
@RequestMapping("/bank")
public class BankController {
private final BankService bankService;

public BankController(BankService bankService) {
this.bankService = bankService;
}

@PostMapping("/save")
public ResponseEntity<String> save(@RequestBody BankDto request) {
bankService.save(request);
return ResponseEntity
.status(HttpStatus.CREATED)
.body("Success created new bank");
}


@PutMapping("/update/{id}")
public ResponseEntity<String> findAll(@PathVariable Long id, @RequestBody BankDto request) {
bankService.update(id, request);
return ResponseEntity
.status(HttpStatus.OK)
.body("Update success");
}

@GetMapping("/findAll")
public ResponseEntity<List<BankDto>> findAll() {
return ResponseEntity
.status(HttpStatus.OK)
.body(bankService.findAll());
}

@DeleteMapping("/delete/{id}")
public ResponseEntity<String> findAll(@PathVariable Long id) {
bankService.delete(id);
return ResponseEntity
.status(HttpStatus.OK)
.body("Delete success");
}
}

Kalau sudah kita gasken aja lah, run springbootnya: http://localhost:8080/swagger-ui/index.html

Kita coba save dulu

Press enter or click to view image in full size

Trus kita cek dbnya yaa: http://localhost:8080/h2-console

Aman ya sudah ke save. Sisanya bisa cek sendiri2 yaa. Dari sini kita bisa hidup dengan kemalasan kita lagi horeee weer.

Terima kasih semoga bermanfaat.

— Arif Dwi Prasetya

--

--