+3

Hướng dẫn sử dụng MapStruct + Lombok

Hướng dẫn sử dụng MapStruct + Lombok

Hướng dẫn mì ăn liền kết hợp 2 thư viện để thực hiện mapping entity và dto trong Java.


Các dependencies

  • Dependencies trong code SpringBoot(3.4.1), springweb,jpa, sql (ở đây là mysql, nhớ tạo db trước nha mấy bro).
  • Dependencies cho lombok và Mapstruct (thêm lombok-mapstruct-binding nếu dùng lombok+mapstruct).
<?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>3.4.1</version>  
        <relativePath/> <!-- lookup parent from repository -->  
    </parent>  
    <groupId>com.nhan</groupId>  
    <artifactId>myhibernate</artifactId>  
    <version>0.0.1-SNAPSHOT</version>  
    <name>myhibernate</name>  
    <description>myhibernate</description>  
    <url/>  
    <licenses>  
        <license/>  
    </licenses>  
    <developers>  
        <developer/>  
    </developers>  
    <scm>  
        <connection/>  
        <developerConnection/>  
        <tag/>  
        <url/>  
    </scm>  
    <properties>  
        <java.version>17</java.version>  
        <org.mapstruct.version>1.6.3</org.mapstruct.version>  
        <org.lombok.version>1.18.36</org.lombok.version>  
    </properties>  
    <dependencies>  
        <dependency>  
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-starter-data-jpa</artifactId>  
        </dependency>  
        <dependency>  
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-starter-web</artifactId>  
        </dependency>  
  
        <dependency>  
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-devtools</artifactId>  
            <scope>runtime</scope>  
            <optional>true</optional>  
        </dependency>  
        <dependency>  
            <groupId>com.mysql</groupId>  
            <artifactId>mysql-connector-j</artifactId>  
            <scope>runtime</scope>  
        </dependency>  
        <dependency>  
            <groupId>org.projectlombok</groupId>  
            <artifactId>lombok</artifactId>  
            <version>${org.lombok.version}</version>  
            <optional>true</optional>  
            <scope>provided</scope>  
        </dependency>  
        <dependency>  
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-starter-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>0.2.0</version>  
        </dependency>  
  
    </dependencies>  
  
    <build>  
        <plugins>  
            <plugin>  
                <groupId>org.apache.maven.plugins</groupId>  
                <artifactId>maven-compiler-plugin</artifactId>  
                <configuration>  
                    <source>${java.version}</source>  
                    <target>${java.version}</target>  
  
                    <annotationProcessorPaths>  
                        <path>  
                            <groupId>org.projectlombok</groupId>  
                            <artifactId>lombok</artifactId>  
                            <version>${org.lombok.version}</version>  
                        </path>  
  
                        <path>  
                            <groupId>org.mapstruct</groupId>  
                            <artifactId>mapstruct-processor</artifactId>  
                            <version>${org.mapstruct.version}</version>  
                        </path>  
                        <path>  
                            <groupId>org.projectlombok</groupId>  
                            <artifactId>lombok-mapstruct-binding</artifactId>  
                            <version>0.2.0</version>  
                        </path>  
                    </annotationProcessorPaths>  
                </configuration>  
            </plugin>  
            <plugin>  
                <groupId>org.springframework.boot</groupId>  
                <artifactId>spring-boot-maven-plugin</artifactId>  
                <configuration>  
                    <excludes>  
                        <exclude>  
                            <groupId>org.projectlombok</groupId>  
                            <artifactId>lombok</artifactId>  
                            <version>${org.lombok.version}</version>  
                        </exclude>  
                        <exclude>  
                            <groupId>org.mapstruct</groupId>  
                            <artifactId>mapstruct-processor</artifactId>  
                            <version>${org.mapstruct.version}</version>  
                        </exclude>  
                    </excludes>  
                </configuration>  
            </plugin>  
        </plugins>  
    </build>  
  
</project>

Application.properties

spring.application.name=myhibernate  
  
spring.datasource.url=${DATASOURCE_URL:jdbc:mysql://localhost:3306/myhibernate}  
spring.datasource.username=${DATASOURCE_USERNAME:nhan}  
spring.datasource.password=${DATASOURCE_PASSWORD:nhan}  
spring.data.jdbc.dialect=mysql  
spring.jpa.hibernate.ddl-auto=update  
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

Code implements

Ví dụ với 2 endpoint:

Endpoint Method Description Request Body Response Body
/customers POST Create new customer CustomerDto CustomerDto
/customers/all GET Get all customers Không List<CustomerDto>

Entity

Sử dụng Jpa để mapping tự động. Tạo entity đơn giản là Customer

  • Lớp Customer
@AllArgsConstructor  
@NoArgsConstructor  
@Getter  
@Setter  
@Entity  
public class Customer {  
  
    @Id  
    @GeneratedValue(strategy = GenerationType.IDENTITY)  
    private Long id;  
  
    private String name;  
    private String email;  
    private String gender;  
  
}

Dto Customer

public record CustomerDto(Long id, String name, String email, String gender) { }

MapStuct Mapper

@Mapper(componentModel = "spring")  
public interface CustomerMapper {  
  // Nếu muốn dùng instance thay vì inject
//    CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class);  
  
    @Mapping(target = "id", ignore = true)  
    Customer toEntity(CustomerDto customerDto);  
  
    CustomerDto toDto(Customer customer);  
  
}

Respository

public interface CustomerRepository extends JpaRepository<Customer, Long> {  
}

Service và impl

public interface CustomerService {  
    CustomerDto createCustomer(CustomerDto customerDto);  
  
    List<CustomerDto> getAllCustomers();  
}

// implement
@Service  
public class CustomerServiceImpl implements CustomerService {  
//    private CustomerMapper customerMapper = CustomerMapper.INSTANCE;  
    private final CustomerMapper customerMapper;  
    private final CustomerRepository customerRepository;  
  
    public CustomerServiceImpl(CustomerMapper customerMapper, CustomerRepository customerRepository) {  
        this.customerMapper = customerMapper;  
        this.customerRepository = customerRepository;  
    }  
  
    @Override  
    public CustomerDto createCustomer(CustomerDto customerDto) {  
        Customer customer = customerMapper.toEntity(customerDto);  
        customer = customerRepository.save(customer);  
        return customerMapper.toDto(customer);  
    }  
  
    @Override  
    public List<CustomerDto> getAllCustomers() {  
       return customerRepository.findAll().stream().map(customerMapper::toDto).toList();  
    }  
}

Controller

@RestController  
@RequestMapping("/customers")  
public class CustomerController {  
    private final CustomerService customerService;  
  
    public CustomerController(CustomerService customerService) {  
        this.customerService = customerService;  
    }  
  
    @PostMapping  
    public CustomerDto createCustomer(@RequestBody CustomerDto customerDto) {  
        return customerService.createCustomer(customerDto);  
    }  
  
    @GetMapping("/all")  
    public List<CustomerDto> getAllCustomers() {  
        return customerService.getAllCustomers();  
    }  
}

Test api

Ở đây mình dùng Httpie Desktop để test 2 endpoint trên

  1. Tạo mới Customer. Endpoint /customers, method post, body CusomerDto
{
  "id": 2,
  "name": "tran nhan",
  "email": "nhan@gmail.com",
  "gender": "male"
}

Response: (id là generation nên tạo cusomer truyền id không bị ảnh hưởng)

create cusomer response
  1. Lấy danh sách cusomer. Request: /cusomers/all, method get. Response:
get all cusomer response

Notes

  • Để hiểu rõ hơn các class được sinh ra xem các file được tự động mapping ở targets/classses/package_tương_ứng.
  • Đi một ngày đàng học một sàng khôn. Viết cho đã, đọc github, stackoverflow cho đã rồi up bài xong search ra bài của ông anh khác đã viết trên viblo là đảo lombok xuống dưới mapstruct là không cần thêm cái lombok-mapstruct-binding. Viết là để chia sẻ kiến thức nha anh em. Cái mới ra đời chưa chắc tốt hơn cái cũ nhưng chắc chắn là sẽ có cái tác dụng gì đó, rác còn có thể tái chế mà ae.

References


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.