Springboot 4 — MapStruct (Indonesia)
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.
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: 8080Buat 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
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









