I have a parent class and a child class and I am using #SuperBuilder to build the child object, but seemingly it is not initializing at all.
My parent class looks like this:
#Getter
#Setter
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
#SuperBuilder
public class MessageResponse {
String responseMessage;
}
My child class looks like this:
#Getter
#Setter
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
#SuperBuilder
public class ListResponse extends MessageResponse {
List<Item> itemList;
int itemCount;
}
Where Item is a Serializable class.
Initially the ListResponse is
itemList = null
itemCount = 0
responseMessage = null
When I try to build ListResponse using builder, it does not change the ListResponse object at all. I am trying to build as
//listResponse is #Autowired from Spring and is initially as shown above.
List<Item> itemList = getItems(); // It returns a list of 15 items, i have checked in debugger, It does.
listResponse.builder()
.bucketList(itemList)
.responseMessage("Item List.")
.bucketCount(itemList.size())
.build();
Even after execution of .build() the contents of this listResponse object is still (null, 0 , null).
I tried to search other references regarding #SuperBuilder and #Builder but got no result. Can someone please point out what is going wrong here?
A builder always creates a new instance. This is the purpose of the builder pattern, and it is how builders work, whether you use Lombok's #SuperBuilder, #Builder, or a manual builder implementation.
You can see that the builder() method is a static method, so it has no access to the instance (typically your IDE should give you a warning here, advising to write ListResponse.builder() instead).
If you want to create a new instance using a builder that is pre-filled with the fields from an existing instance, you can use toBuilder = true as annotation parameter on #(Super)Builder. Then call listResponse.toBuilder().
If you want to modify an instance, the builder pattern is not the right choice. Use setters instead. Those can be generated by Lombok also in a fluent, chainable style; see #Accessors for details.
Related
Is there any annotation to let us get another value if original value is null?
#Getter
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class SomeDto extends BaseDto {
private BigDecimal a;
#GetOrAlternative(alternative = "a")
private BigDecimal b;
For example, I want to get value a if b is null on above code.
I want to use that like dto.getBOrAlternative().
Is that possible?
If that is not possible, how can I implement that?
I tried to make custom annotation and processor following this: https://www.happykoo.net/#happykoo/posts/256
However, it didn't work. I think it is because that is not a field level.
Given the following classes with the Lombok annotations #Data and #SuperBuilder
#Data
#SuperBuilder
public abstract class Parent {
protected final String userId;
protected final Instant requestingTime;
}
#Data
#SuperBuilder
public class Child extends Parent {
private final Instant beginningDate;
private final Instant endingDate;
private final Collection<String> fields;
}
I am getting the following error appearing over the #Data annotation in the Child class:
Implicit super constructor Parent() is undefined. Must explicitly invoke another constructor.
Is there a way to configure a non-default constructor on the Child class's #Data annotation in order to have all final fields on both the Child and Parent classes initialized when invoking the Builder?
I have tried a few different combinations of the #Data, #Getter, #Setter annotations with the #SuperBuilder annotation on both the child and parent classes, but haven't found a working solution yet. I am using Lombok 1.18.10.
For reference, this question is related
EDIT
This is effectively the constructor that Lombok should be constructing and invoking on the SuperBuilder.build() operation.
public Child(
final String userId,
final Instant requestingTime,
final Instant beginningDate,
final Instant endingDate,
final Collection<String> fields) {
super(userId, requestingTime);
this.beginningDate = beginningDate;
this.endingDate = endingDate;
this.fields= fields;
}
As requested, this is how I would expect to invoke the builder on the Child object.
final Child child = Child.Builder()
.userId(<value>)
.requestingTime(<value>)
.beginningDate(<value>)
.endingDate(<value>)
.fields(<value>)
.build();
AFAIK, #Data generates a #NoArgsConstructor, which is just wrong. Actually, #Data is wrong per se, as it's meant for mutable classes; #Value would be better, but it can't deal with the super constructor either.
So remove #Data, add #Getter, #EqualsAndHashCode, #ToString and whatever you need. Don't forget to add callSuper=true in the subclass.
This is effectively the constructor that Lombok should be constructing and invoking on the SuperBuilder.build() operation.
public Child(
final String userId,
final Instant requestingTime,
final Instant beginningDate,
final Instant endingDate,
final Collection<String> fields) {
super(userId, requestingTime);
this.beginningDate = beginningDate;
this.endingDate = endingDate;
this.fields= fields;
}
No, that's not how SuperBuilder works. This is actually Lombok can't do as it can't see the super fields. Instead, the builder uses something like
public Child(ChildBuilder b) {
super(b);
this.beginningDate = b.beginningDate;
this.endingDate = b.endingDate;
this.fields= b.fields;
}
You can believe what Jan Rieke says, he wrote it.
#Data annotation implicitly generate code for below mentioned functionalities:
setter
getter
toString
equallAndHashCode
constructor(for required arguments only)
It means constructor declaration of loombok will generate code for Parent class will be as mentioned below:
Person(String userId, Instant requestingTime)
Similarly for Child class:
Child(Instant beginningDate, Instant endingDate, Collection fields)
Now as your program is throwing exception that
Parent()
is undefined in parent class.
Please annotate your class with :
#NoArgsConstructor
This will generate required default constructor.
I'm wondring if there's a way to configure spring data to use a builder to deserialise a document. Something like JsonPOJOBuilder or JsonDeserialize(builder = ADocumentBuilder) for jackson ?
In fact I'm using lombok with #SuperBuilder annotation like :
#Data
#RequiredArgConstrcutor
#SuperBuilder
public abstract class AttachementBase {
private String a;
private final String b; //maybe final
private String c;
}
#Value
#SuperBuilder
#Document
public class Attachement extend AttachementBase{
private String d;
}
Evething's works fine with the builder and how to use it, except when calling the AttachementRepository.findXX() Spring trys to call a constructor but it cannot find the appropriate one.
Lombok generates AttachementBuilder in the Attachement class and I want to use it to deserialise this document.
I do not want this post be related to lombok, i juste illustrated the use case. The question is how to use a builder to deserialize a document in Spring-data-mongodb ?
I am trying to use Mapstruct to map source object to a target list. What should be a clean mapstruct way of doing this?
Below are my DTO's.
Source DTO
#Data
class Source
{
String a;
String b;
String C;
}
Target DTO
#Data
class Target
{
String name;
List<Child> customList;
}
#Data
class Child
{
String attr1;
boolean attr2;
}
I am facing issues with Mapper Class. Trying to achieve something like below.
public interface CustomMapper
{
#Mapper(target="customList" expression="java(new Child(a,false))"
#Mapper(target="customList" expression="java(new Child(b,true))"
#Mapper(target="customList" expression="java(new Child(c,false))"
Target sourceToTarget(Source source);
}
I don't want to use qualifiedBy function like below to achieve this, as all conversion needs to be coded for each element.
List<Child> toList(Source source)
{
List<Child> customList = new ArrayList<Child>();
customList.add(new Child(source.getA(),false));
customList.add(new Child(source.getB(),true));
customList.add(new Child(source.getC(),false));
return customList;
}
I have used an expression to solve this problem. The expression is to perform the mapping (for objects, straightforward for Strings), and then convert it to a list.
#Mapping(target = "names", expression = "java(Arrays.asList(map(source.getName())))")
TargetObject map(SourceObject source);
TargetName map(SourceName source)
You need to import "Arrays" class in the #Mapper definition as below.
#Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = "spring", imports = {Arrays.class})
I had similar use case where i want to convert and Single Object to List of objects.
As these are very custom requirements, will be hard for mapstruct to provide some API for use cases like this.
I ended up implementing using default method like this
#Mapper(componentModel = "spring")
interface MyMapper {
default List<SomeObject> from(SourceObject sourceObject) {
//Add Mappig logic here
//return the list
}
}
//If you want to use some mapper for mapping
#Mapper(componentModel = "spring")
public abstract class SomeArrayMapper {
#Autowired
SomeMapper mapper;
public SomeUser[] from(SingleObject singleObject) {
SomeUsers[] Users = new SomeUser[1];
Users[0] = mapper.toUser(singleObject);;
return Users ;
}
}
In some cases Decorators can also be useful take a look at here
There is no clean way of doing this at the moment in MapStruct. MapStruct is considerring bean tot map mapping. See here: https://github.com/mapstruct/mapstruct/pull/1744 which might come in helpful once implemented.
However, if you really have a lot of properties and this is a recurring problem, and you dislike reflection - like I do - you might want to give code generation an attempt and DIY. I posted an example some while ago for generating a mapper repository here: https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-mapper-repo but, it's a bit of an steep learning curve.. sorry
I have a PivotModel class which I will initialise using the new keyword.
PivotModel pivotModel = new PivotModel()
When pivotModel gets initialised, all the dependant fields(model1, model2,cell1,cell2) should get initialised with new object but not to null.
I wanted to initialise all the fields and the fields of dependant classes without using new constructor. I don't want to have boilerplate code.
If you have any standard practice of way doing it, post it here. I am also using lombok in my project.
public class PivotModel {
#Getter
#Setter
private Model1 model1;
#Getter
#Setter
private Model2 model2;
private Model3 model3 = new Model3() -----> Dont want to initialise this way for these fields
}
public class Model1 {
private Map<String,Cell> cell1;
private Map<String,Cell> cell2;
private Map<String,Cell> cell3;
------ will have some 10 fields here
}
It seems that you are using Lombok project in your java project you can add #Getter #Setter above your class Scope, Lombok also provides Constructor Annotation, so Just type above your class Scope #AllArgsConstructor
so you class Should be like this
#Getter
#Setter
#AllArgsConstructor
public class PivotModel {
private Model1 model1;
private Model2 model2;
}
#Getter
#Setter
#AllArgsConstructor
public class Model1 {
private Map<String,Cell> cell1;
private Map<String,Cell> cell2;
private Map<String,Cell> cell3;
}
For initialization, I would recommended Builder Pattern.
//keep your initialization logic in builder class and use build()/create() wherever required. Let's say:
Class Pivot{
// Note: this have only getters for members
//inner builder class
PivotModelBuilder{
//Note: all setter will be part of builder class
/**
* method which return instantiated required object.
*/
public PivotModel build(){
return new PivotModel(this);
}
}
}
//access initilization code as:
PivotModel pivot = new Pivot.PivotModelBuilder().build()
Adding referral link: https://www.javaworld.com/article/2074938/core-java/too-many-parameters-in-java-methods-part-3-builder-pattern.html
(You can search more about builder pattern and it's implementation online)
Limitations:
However, it's good way to initialize/create bean, but, you might find duplication of member fields in both Parent and builder class.