How to tell MapStruct "not" to use the Lombok Builder? - java

I have the following classes and mapper to map them.
How can I configure Mapstruct to "not" use the Lombok builder? (without removing the #Builder annotation)?
When using the latest version of Lombok and mapstruct, mapstruct will automatically use the Builder when the #Builder annotation is used. I can not find a way to disable that, as I need the Instance in an #AfterMapping method as the builder doesn't expose all required methods (#SuperBuilder is not allowed in this use case)
#Entity(name = "user_details")
#Data
#Builder
public class User extends AuditableEntityBase {
#Version
#NotNull
private Integer version;
#NotNull
private String name;
#NotNull
private String email;
#NotNull
private Address address; // Just another Class containing another class that is mapped as well.
}
#Value
#Builder
public class UserDto extends AuditableEntityBaseDto {
#NotNull
private Integer version;
#NotNull
private String name;
#NotNull
private String email;
#NotNull
private AddressDto address;
}
#Mapper(componentModel = "spring")
class UserRestMapper {
public abstract UserDto map(User obj);
}
#AfterMapping
public void decorate(User source, #MappingTarget AuditableEntityBase target) {
// Method is never called.
// Method is called in case the second argument is: "#MappingTarget UserDto.UserDtoBuilder target"
}

If you want to disable using builders you can do so by adding #Builder(disableBuilder = true) to your #Mapper.
e.g.
#Mapper(componentModel = "spring", builder = #Builder(disableBuilder = true))
class UserRestMapper {
public abstract UserDto map(User obj);
}
Note #Builder is from the org.mapstruct package

No idea about disabling it, but why not do it like this?
#Mapper(componentModel = "spring")
abstract class UserRestMapper {
public abstract UserDto map(User obj);
public UserDto decoratedMap(User obj) {
UserDto mapped = map(obj);
// your after mapping logic
return mapped;
}
}

Related

Cannot map nested field with #Mapper

I'm trying to create a mapper using lombok #Mapper and I get the following error:
Property "delivery" has no write accessor in ClassA for target name "classBB_variable.delivery"
The code:
#Mapper
#Mapping(target = "classBB_variable.customerId", source = "classA_variable.customerId")
#Mapping(target = "classBB_variable.delivery", source = "classA_variable.classAA_variable.delivery")
ClassB toCreateRequest(ClassA classA_variable);
#Value
#Builder(toBuilder = true)
public class ClassA {
private UUID customerId;
private ClassAA classAA_variable;
}
#Data
#Builder
public class ClassAA {
private String delivery;
}
#Data
#Builder
public class ClassB {
private ClassBB classBB_variable;
}
#Data
#Builder
public class ClassBB {
private UUID customerId;
private String delivery;
}
As the map for customerId works fine, I'm assuming the issue is the additional level introduced with classA_variable.classAA_variable.delivery.
Has anyone already dealt with this situation? How can I solve it?

Change SuperBuilder method with Lombok

I have a class as follows:
#Data
#SuperBuilder
#NoArgsConstructor
#JsonInclude(Include.NON_NULL)
public class CustomObject extends Parent<CustomObject> {
...
#Accessors(
chain = true,
prefix = {"_"})
#Builder.Default
private Boolean _default = false;
}
This generates a builder with a default() method that is not usable due to it being a reserved word. Is there a way to change this behavior so the builder works?
Unfortunately, there is no nice way in this case. #Accessors affects getters, setters and builders, because this is what you need in the vast majority of the cases. You cannot switch it off only for builders.
This means the only way out here (apart from renaming the _default field) is to remove #Accessors and implement getters and setters manually:
#Data
#SuperBuilder
#NoArgsConstructor
#JsonInclude(JsonInclude.Include.NON_NULL)
public class CustomObject {
#Builder.Default
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private Boolean _default = false;
public Boolean getDefault() {
return _default;
}
public CustomObject setDefault(Boolean _default) {
this._default = _default;
return this;
}
}

Mapping nested list

I have a object with list of nested object
#Getter
#Setter
#NoArgsConstructor
public class Notification {
private Long id
private Long statusId;
private List <External> external;
}
#Getter
#Setter
#NoArgsConstructor
public class External{
private Long externalId;
private LocalDate date;
}
Dto
#Getter
#Setter
#NoArgsConstructor
public class NotificationPayload {
private Long id;
private Long statusId;
private List <ExternalReferencePayload> external;
}
#Getter
#Setter
#NoArgsConstructor
public class ExternalReferencePayload {
private Long externalReferenceId;
}
Mapper
#Mapper(componentModel = "spring")
public interface NotificationMapper{
public Notification dtoToNotification(NotificationPayload payload);
}
I search the way to map the nested list
In order to perform custom mapping for certain elements it is only needed to define a mapping method and MapStruct will take care of the rest. In your example:
#Mapper(componentModel = "spring")
public interface NotificationMapper{
public Notification dtoToNotification(NotificationPayload payload);
#Mapping(target = "externalId", source = "externalReferenceId")
public External dtoExternal(ExternalReferencePayload payload);
}
With this the nested list will use the dtoExternal mapping method to perform the mapping. With #Mapping you control how the mapping between externalId and externalReferenceId should be done

JsonDeserialize set inherited properties also when objectmapper readvalue

JsonDeserialize not working when objectmapper readvalue for inherited properties.
Vehicle Class
#Getter
#Setter
#JsonDeserialize(builder = Vehicle.VehicleBuilder.class)
#Builder(builderClassName = "VehicleBuilder", toBuilder = true)
public class Vehicle{
private String name;
private String noOfTyres;
#JsonPOJOBuilder(withPrefix = "")
public static class VehicleBuilder{
}
}
Car class
#Getter
#Setter
#JsonDeserialize(builder = Car.CarBuilder.class)
#Builder(builderClassName = "CarBuilder", toBuilder = true)
public class Car extends Vehicle {
private String carType;
#JsonPOJOBuilder(withPrefix = "")
public static class CarBuilder extends VehicleBuilder {
}
}
I don't want to create #NoArgsConstructor ,#AllArgsConstructor in both classes.
My issue when Car car = om.readValue(jsonValue,Car.class);
When I parse Json to java object the parent class properties are not setting properly.
As of now I'm using #NoArgsConstructor ,#AllArgsConstructor for work around for the use case.
Is there any way to use it along with #JsonDeserialize and #JsonPOJOBuilder?
The problem with the code is that it assumes that builders in inherited classes will set the parent properties as well. Unfortunately, they don't do that out of the box. However, this is something that can be achieved with Lombok but requires some additional code, as described in this post.
A complete solution could look as follows.
Parent Class
#Getter
#Setter
#JsonDeserialize
#Builder(builderClassName = "VehicleBuilder", builderMethodName = "vehicleBuilder")
public class Vehicle {
private String name;
private String noOfTyres;
}
Child Class
#Getter
#Setter
#JsonDeserialize(builder = Car.CarBuilder.class)
public class Car extends Vehicle {
private String carType;
#Builder
public Car(String name, String noOfTyres, String carType) {
super(name, noOfTyres);
this.carType = carType;
}
#JsonPOJOBuilder(withPrefix = "")
public static class CarBuilder extends VehicleBuilder {
}
}
Notice that the builder on the extending class is achieved by supplying a constructor with the #Builder annotation. Also take notice that the extending class does not set annotation parameter toBuilder=true as that will require access to parent properties which are private. This can be achieved by setting parent class properties to protected.

Can't generate mapping method with no input arguments with Mapstruct

I’m starting my very first steps with Mapstruct mapper. I want to map a JPA data entity class to a DTO class. This is my source class:
#Entity
#Data
#Table(name = "projects")
public class Project {
#Id
private Long Id;
private String projectName;
private String description;
#OneToMany(mappedBy = "project")
List<Sprint> sprints;
#OneToMany(mappedBy = "project")
List<Epic> epics;
#OneToMany(mappedBy = "project")
List<Story> stories;
public Project(Long id, String projectName, String description) {
Id = id;
this.projectName = projectName;
this.description = description;
}
}
This is my target class:
#Data
#AllArgsConstructor
public class ProjectDTO {
private Long Id;
private String projectName;
private String description;
}
The #Data annotation is from Lombok.
I want to make a mapper to map the Project to ProjectDTO, the attributes like sprints, epics, stories SHOULD NOT be included in ProjectDTO. This is my mapper interface:
#Mapper
public interface ProjectMapper extends Mapper {
ProjectMapper INSTANCE = Mappers.getMapper(ProjectMapper.class)
ProjectDTO projectToProjectDTO(Project project);
}
When I try to build it, this is the error message I got:
[ERROR] Can't generate mapping method with no input arguments.
I guess it’s related to the missing properties in ProjectDTO, but don’t know to solve it. With the #Mapping, I cannot do it like:
#Mapping(source=“sprints”, target= null)
Any help would be appreciated!
Add the '#NoArgConstructor' as well. MapStruct cannot (yet) deal with constructing objects via constructor. Another option would be using '#Builder' in stead if your objects are truly immutable
You should not extend the annotation Mapper. It is enough when you just use it at the type declaration level of your interface

Categories

Resources