I am facing issue when I am trying to use ModelMapper to convert nested java objects into nested DTO's. Getting null for child dto's in parent dto object. Following are the code snippets.
Entity Classes :
public class User {
private String name;
private Address address;
private Product product;
}
public class Address {
private String area;
private String city;
}
public class Product {
private Integer productId;
private String productName;
private Double productPrice;
}
DTO's Classes :
public class UserDTO {
private String name;
private AddressDTO address;
private ProductDTO product;
}
public class AddressDTO {
private String area;
private String city;
}
public class ProductDTO {
private Integer productId;
private String productName;
private Double productPrice;
}
here is the mapper code :
ModelMapper mapper = new ModelMapper();
mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.LOOSE);
UserDTO userDTO = mapper.map(user, UserDTO.class);
System.out.println("Output User DTO : " + userDTO );
Output :
Output User DTO : UserDTO [name=xyz, address=null, product=null]
Here I want to convert User entity into UserDTO dto.
I am getting null values for address and product DTO's. What exactly I am missing here ? Does anyone have any idea ?
Note : I have added getters, Setters and toString() methods in entity and DTO's.
Here I found solution for nested DTO to Entity and vice versa conversions, I was missing some ModelMapper configurations that I have added below.
mapper.getConfiguration()
.setFieldMatchingEnabled(true)
.setFieldAccessLevel(AccessLevel.PRIVATE)
.setMatchingStrategy(MatchingStrategies.STANDARD);
Related
I am trying to use MapStruct for a structure similar to the following:
#Data
public class ClassAEntity {
private int id;
private String name;
private String numT;
private List<ClassBEntity) bs;
}
#Data
public class ClassBEntity {
private int id;
private String name;
private String numT;
private List<Other> oc;
}
#Data
public class ClassA {
private int id;
private String name;
private List<ClassB) bs;
}
#Data
public class ClassB {
private int id;
private String name;
private List<Other> oc;
}
In the interface I have added the following mapping:
ClassAEntity map(ClassA classA, String numT)
I get a warning because it can't map numT to classBEntity.numT and I can't add it with #Mapping in the following way:
#Mapping(source = "numT", target = "bs[].numT")
On the other hand I need to ignore the parameter oc of classBEntity because "Other" object contains classAEntity and forms a cyclic object. (because I use oneToMany JPA). I have tried the following:
#Mapping(target = "bs[].oc", ignore = true)
Thank you for your help
MapStruct does not support defining nested mappings for collections. You will have to define more explicit methods.
For example to map numT into bs[].numT and ignore bs[].oc you'll need to do something like:
#Mapper
public MyMapper {
default ClassAEntity map(ClassA classA, String numT) {
return map(classA, numT, numT);
}
ClassAEntity map(ClassA classA, String numT, #Context String numT);
#AfterMapping
default void setNumTOnClassBEntity(#MappingTarget ClassBEntity classB, #Context String numT) {
classB.setNumT(numT);
}
#Mapping(target = "oc", ignore = "true")
ClassBEntity map(ClassB classB);
}
In the project currently I'm working on, has a Project class,
public class project {
private Integer projectId;
private String projectName;
//getters & Setters
}
This project is using as a Set in User class
public class User {
private Integer userId;
private Set<Project> projects;
//getters & Setters
}
I need to map this User class to UserDto. UserDto is like below.
public class UserDto {
private Integer userId;
private Set<ProjectDto> projectDtos;
//getters & Setters
}
The ProjectDto is also same as Project class.
public class projectDto {
private Integer projectId;
private String projectName;
//getters & Setters
}
To map User and UseDto, I tried below code. But it's giving an error.
private UserDto map(User user) {
UserDto userDto = new UserDto();
userDto.setUserId(user.getUserId());
Set<ProjectDto> projectDto= new HashSet<>();
Type setType = new TypeToken<HashSet<ProjectDto>>(){}.getType();
projectDto = modelMapper.map(user.getProjects(), setType);
userDto.setProjectDto(projectDto);
return userDto;
}
Following error is returned:
failed to convert org.hibernate.collection.internal.PersistentSet to
java.util.Set
It'd be great if you could support me for this. Answers without using ModelMapper are also welcome.
Thanks in advance!
first add a constructor to ProjectDto class:
public class ProjectDto {
private Integer projectId;
private String projectName;
public ProjectDto(Integer projectId, String projectName) {
this.projectId = projectId;
this.projectName = projectName;
}
//getters & Setters
}
then with java 8 stream API, it can be done like this:
private UserDto map(User user) {
UserDto userDto = new UserDto();
userDto.setUserId(user.getUserId());
userDto.setProjects(user.getProjects().stream()
.map(project -> new ProjectDto(project.getProjectId(), project.getProjectName()))
.collect(Collectors.toSet()));
return userDto;
}
I am struggling to map a string object from source(Relation.class) and to a List of target(RelationListDTO.class) .
Relation.java
public class Relation {
private String name;
private String email;
private String completeAddress;
// getters and setters
}
RelationListDTO.java
public class RelationListDTO {
private String name;
private String email;
private List<Address> address;
// getters and setters
}
Address.java
public class Address{
private String street;
private String city;
// getters and setters
}
Mapper class
#Mapper
public interface RelationMapper {
#Mapping(source = "completeAddress", target = "address.get(0).city")
RelationListDTO relationToListDto(Relation relation);
}
But it is not working. Could anyone please help.
What you are trying to do using MapStruct is not possible. Because MapStruct doesn't work with run time objects. MapStruct only generated plain java code for mapping two beans. And I find your requirement is little unique. You have a list of Addresses but want to map only city from source object? You can still do like this
#Mapping( target = "address", source = "completeAddress")
RelationListDTO relationToListDto(Relation relation);
// MapStruct will know to use this method to map between a `String` and `List<Address>`
default List<Address> mapAddress(String relation){
//create new arraylist
// create new AddressObject and set completeAddress to address.city
// add that to list and return list
}
Not sure if this was possible at the time of the accepted answer but I had the same problem as you and ended up doing it this way.
#Mapper(imports = Collections.class)
public interface RelationMapper {
#Mapping(expression = "java(Collections.singletonList(relation.getCompleteAddress()))", target = "address")
RelationListDTO relationToListDto(Relation relation);
}
In the code below, class Address is nested in Entity User. I wonder if all the attributes of Address are private, do we need getter and setter for each of the field in Address? Notice there is a List<String>, so I'm not sure if Room will work well with #TypeConverter in this case.
public class Address {
public String street;
public String state;
public List<String> city;
#ColumnInfo(name = "post_code")
public int postCode;
}
#Entity
public class User {
#PrimaryKey
public int id;
public String firstName;
#Embedded
public Address address;
}
You can easily add getter/setters with #Ignore annotation and the converter will ignore these methods.
#Ignore
public List<String> getCity() {
return city;
}
You can refer here
Create the entity
What is the best solution for creating POJO, at controller level or method level.
For example I have EmployeeController which contains below methods.
getAllEmployees()
addEmployee(AddEmployeeRequest employee)
updateEmployee(UpdateEmployeeRequest employee)
removeEmployee(RemoveEmployeeRequest employee)
//Method level classes
public class AddEmployeeRequest
{
private String name;
private Date dateOfBirth;
private String Address;
}
public class UpdateEmployeeRequest
{
private long id;
private String Address;
}
public class RemoveEmployeeRequest
{
private long id;
}
or
getAllEmployees()
addEmployee(EmployeeRequest employee)
updateEmployee(EmployeeRequest employee)
removeEmployee(EmployeeRequest employee)
//Controller level class
public class EmployeeRequest
{
private long id;
private String name;
private Date dateOfBirth;
private String Address;
}
If I have method level models then do I have to create the respective sevice level DTO models also ?
In fact, if you are using spring, it is neccessary to use a single POJO because spring use reflection for accessing the ClassName, DeclaredFields etc. Using multiple POJO will be annoying for Spring.
See here some more details about reflection: https://crunchify.com/create-simple-pojo-and-multiple-java-reflection-examples/