Entity to DTO using Model Mapper in Spring Restful Web services
Entity to DTO using Model Mapper in Spring Restful Web services:
In spring restful webservices we can not directly expose our entity pojo classes as json responses as it has lot of issues like security/unwanted data’s exposure etc.
Data Transfer Object (DTO) is a design pattern which actually resolves this issue. I am going to do these sample in spring boot project.
In highlevel idea behind this is,
- Create the DTO pojo classes for all/required of your Entities.
2. Write a model mapper for pojo to entity and vice versa.
3. In controller do the converstions before starting any business.
Eg:
If you have a entity class called coupon with lot of fields & mappings like store/category/tag inside coupon entity class and you would want to just expose only the few fields of your coupon entity then create your DTO class named couponDTO and keep only the required fields inside it.
Say Coupon Entity has
coupon_id, coupon_name, coupon_desc, coupon_url, isActive, uploadedBy, uploadedDt, updatedBy, updatedDt
And you just want to expose only the
coupn_name, coupon_desc and coupon_url
then create the DTO class named CouponDTO and keep only these three fields and its getters setters.
Table of Contents
Step 1: Put the below entry in your pom.xml
[plain]
<dependency>
<groupId>org.modelmapper.extensions</groupId>
<artifactId>modelmapper-spring</artifactId>
<version>1.1.0</version>
</dependency>
[plain]
Step 2: Add the following @Bean in Spring Boot’s Main Application class.
This is to load the model mapper in spring, so that the same can be autowired whereever required.
[java]
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}
[/java]
Step 3: My Coupon Entity and CouponDTO pojo classes are:
- Here Model Mapper maps with the correct fields in most of the cases by matching the field hashcode, so even if the field names are different I was able to see that it mapped to the correct field only.
- If you face any issues then you can keep the same names in both the classes (entity & DTO).
Coupon Entity:
[java]
@Entity
@Table(name = “cpn”)
public class Coupon implements Serializable {
public Coupon() {
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = “id”)
private Long couponId;
@Column(name = “title”)
private String couponTitle;
@Column(name = “tc_desc”)
private String couponDesc;
@Column(name = “is_cpn_active”)
private char isCouponActive;
// and many other fields with getters / setters for all the fields.
}
[/java]
CouponDTO:
[java]
public class CouponDTO {
@Id
@NotNull
private Long id;
@NotNull
private String title;
private String desc;
}
[/java]
Step 4: Converting Entity to DTO and Vice Versa:
- Autowire the ModelMapper
- Write DTO to Entity and entity to DTO methods.
- Here I used Stream to iterate, convert and collect it in the list, which can be normally iterated and performed using loops too.
- I explained everything by keeping in controller layer only, use it in service layer(recommended) or anywhere you want.
[java]
@RestController
@RequestMapping(“/products”)
public class ProductController {
@Autowired
private ModelMapper modelMapper;
@GetMapping(“/list”)
public List<CouponDTO> getCoupons(){
// here my business logic to get the List<Coupon> and put it in couponList | Coupon is my entity class.
// Converting coupon entity to couponDTO entity
List<CouponDTO> couponDTOList = couponList.stream().map(eachCoupon -> convertToDto(eachCoupon))
.collect(Collectors.toList());
return couponDTOList;
}
private CouponDTO convertToDto(Coupon coupon) {
CouponDTO couponDTO = modelMapper.map(coupon, CouponDTO.class);
return couponDTO;
}
private Coupon convertToEntity(CouponDTO couponDTO) throws ParseException {
Coupon coupon = modelMapper.map(couponDTO, Coupon.class);
return coupon;
}
}
[/java]
Now by returning the CouponDTO instead coupon entity you can have the control over the fields which gets exposed in the web service.
Note:
- Further documentation and other stuffs related to modelmapper can be found here, And in Github.
- MapStruct is the another good alternative for the same requirement.
- We can also make use @JsonIgnore and other similar annotations to expose only the required fields from our entity beans. But the problem is we can’t have many different versions of the same beans.
- Eg: If suppose Downstream system A want 3 fields of Coupon and Downstream system B wants 5 fields of Coupon then if we make the changes in Coupon Entity directly with @JsonIgnore or any other Jackson provided annotations then it affects across the entity and the different version can’t ever be created.
- It is also difficult to add the bean validation annotations to bean classes, which will also be confusing if hibernate entity annotations and validation annotations remains in the same class. So dividing the entity and DTO can also make the validation process simplified, so that only the validation related annotations can be kept inside the DTO classes.
Feel free to post your comments / suggestions / tips in the below comments section.
Hope it helps!