Java dto to maps

Mapping DTOs in Spring Boot with MapStruct

This tutorial covers how to use MapStruct library to map automatically your Data Transfer Objects with your repository data. We will use the JPA layer of a Spring Boot application to access your data.

MapStruct in a nutshell

In its simplest definition a DTO is a serializable object that allows the flow of information between application layers. To achieve that, you would typically need to define a Java Bean which acts as DTO and a Mapper class which contains the logic to map the Bean with the Data.

Thanks to the MapStruct project, this can be simplified as you can get automatic mapping between the DTO and the Entity Bean by adding a simple interface. In the second part of this tutorial, we will show how to perform advanced mapping, in case the field names differ between the DTO and the Entity Bean.

Firstly, start adding an Entity object named Customer:

package com.mapstruct.demo.entities; import lombok.Getter; import lombok.Setter; import javax.persistence.*; @Getter @Setter @Entity @Table(name = «customer») public class Customer

Please notice that we are using Lombok annotations to have also automatic mapping of @Getter @Setter fields.

Next, it’s time to create the DTO objects that will be used in a REST applications. For the sake of this example, we will create two DTO objects: one will be used to transfer data upon a GET request and another which will be used to transfer data following up to a POST request:

Читайте также:  Python double to string format

@Getter @Setter public class CustomerGetDto < @JsonProperty("id") private int id; @JsonProperty("email") private String email; @JsonProperty("name") private String name; @JsonProperty("surname") private String surname; >@Getter @Setter public class CustomerPostDto

Next, in order to bind the Entity objects with the actual DTO objects, we will use a simple Interface which includes a Mapper annotation in it:

@Mapper( componentModel = «spring» ) public interface MapStructMapper

As field names are identical between Entity and DTOs, that’s all you need to have automatic binding.

Finally, add the Repository class to our project:

@Repository public interface CustomerRepository extends JpaRepository <>

To allow users interact with our application, let’s add a Controller class with a POST and GET method to create and return the list of Customer objects:

@RestController @RequestMapping("/customers") public class CustomerController < private MapStructMapper mapstructMapper; private CustomerRepository customerRepository; @Autowired public CustomerController( MapStructMapper mapstructMapper, CustomerRepository userRepository ) < this.mapstructMapper = mapstructMapper; this.customerRepository = userRepository; >@PostMapping() public ResponseEntity create( @Valid @RequestBody CustomerPostDto customerPostDto ) < customerRepository.save( mapstructMapper.customerPostDtoToCustomer(customerPostDto) ); return new ResponseEntity<>(HttpStatus.CREATED); > @GetMapping("/") public ResponseEntity getById( @PathVariable(value = "id") int id ) < return new ResponseEntity<>( mapstructMapper.customerToCustomerGetDto( customerRepository.findById(id).get() ), HttpStatus.OK ); > >

Complete the example adding an Application class to bootstrap the example:

package com.mapstruct.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import com.mapstruct.demo.controllers.CustomerController; @SpringBootApplication @EnableJpaRepositories("com.mapstruct.demo.repositories") @EntityScan(basePackages = < "com.mapstruct.demo.entities" >) public class MapStructDemoApplication < public static void main(String[] args) < SpringApplication.run(MapStructDemoApplication.class, args); >>

Building the Project

To build the example, we will add the required MapStruct and Lombok dependencies, plus the default SpringBoot starter libraries:

  org.springframework.boot spring-boot-starter-web 2.4.3  org.springframework.boot spring-boot-starter-data-jpa 2.4.3  org.springframework.boot spring-boot-starter-validation 2.4.3  com.h2database h2 runtime  org.projectlombok lombok 1.18.16 provided  org.mapstruct mapstruct 1.4.2.Final  org.projectlombok lombok  

We will also add the annotationProcessorPaths section to the configuration part of the maven-compiler-plugin plugin. The mapstruct-processor is used to generate the mapper implementation during the build:

 org.apache.maven.plugins maven-compiler-plugin 3.5.1 1.8 1.8  org.mapstruct mapstruct-processor 1.4.2.Final    

Running the application

You can run the Spring Boot application as usually with:

$ mvn clean install spring-boot:run

Next, you can test adding an Entity:

curl -i -X POST -H "Content-Type: application/json" -d "" http://localhost:8080/customers

And retrieving the Entity:

curl -s http://localhost:8080/customers/1 | jq

Well done. We just managed to create, deploy and test a project which uses MapStruct

MapStruct generated Mapping

Behind the hoods, when you build the project, a MapStructMapperImpl is automatically generate to map the CustomerDTO fields with the Customer Entity objects:

@Generated( value = "org.mapstruct.ap.MappingProcessor", date = "2021-07-13T14:45:56+0200", comments = "version: 1.4.2.Final, compiler: javac, environment: Java 11.0.4 (Oracle Corporation)" ) @Component public class MapStructMapperImpl implements MapStructMapper < @Override public CustomerGetDto customerToCustomerGetDto(Customer customer) < if ( customer == null ) < return null; >CustomerGetDto customerGetDto = new CustomerGetDto(); customerGetDto.setId( customer.getId() ); customerGetDto.setEmail( customer.getEmail() ); customerGetDto.setName( customer.getName() ); customerGetDto.setSurname( customer.getSurname() ); return customerGetDto; > @Override public Customer customerPostDtoToCustomer(CustomerPostDto customerPostDTO) < if ( customerPostDTO == null ) < return null; >Customer customer = new Customer(); customer.setId( customerPostDTO.getId() ); customer.setEmail( customerPostDTO.getEmail() ); customer.setPassword( customerPostDTO.getPassword() ); customer.setName( customerPostDTO.getName() ); customer.setSurname( customerPostDTO.getSurname() ); return customer; > >

As you can see, the mapping between source and target classes is straightforward. In some cases, however, the DTO fields can differ from the Repository fields. If this is the case, you can decorate the methods of the interface with the @Mappings annotation to define a custom source and target fields:

@Mapper( componentModel = "spring" ) public interface MapStructMapper < @Mappings(< @Mapping(target="Customerid", source="id") >) CustomerGetDto customerToCustomerGetDto( Customer customer ); @Mappings(< @Mapping(target="Customerid", source="id") >) Customer customerPostDtoToCustomer( CustomerPostDto customerPostDTO ); >

Источник

Оцените статью