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.
Related
This is my class:
#Builder
#Value
public class A {
int id;
String name;
#NonNull String lastName;
}
The Lombok #Builder will add the all args constructor.
I need to deserialise a string into a POJO object.
I created the following Jackson mixin containing all three properties:
public abstract class AMixin {
public AMixin(#JsonProperty("name") String name,
#JsonProperty("id") int id,
#JsonProperty("lastName") String lastName) {
}
#JsonProperty("name")
abstract String getName();
#JsonProperty("id")
abstract int getId();
#JsonProperty("lastName")
abstract String getLastName();
}
I deserialise like this:
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(A.class, AMixin.class);
String ss = "{\"id\":1,\"name\":\"some name\",\"lastName\":\"some name\"}\n";
A c = mapper.readValue(ss, A.class);
}
but I get this error:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.bla.test.A` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"id":1,"name":"some name","lastName":"some name"}
"; line: 1, column: 2]
I found the answer.
Add lombok.config file with content:
lombok.anyConstructor.addConstructorProperties=true
The issue here is that Jackson expects a no-argument constructor or some other configured way of creating the object.
As of Lombok v1.18.14, the #Jacksonized annotation can be added to the class with the #Builder annotation to automatically configure the builder to be used for Jackson deserialization.
#Jacksonized
#Builder
#Value
public class A {
int id;
String name;
#NonNull String lastName;
}
The Lombok documentation for #Jacksonized describes this annotation in more detail:
The #Jacksonized annotation is an add-on annotation for #Builder and #SuperBuilder. It automatically configures the generated builder class to be used by Jackson's deserialization. It only has an effect if present at a context where there is also a #Builder or a #SuperBuilder; a warning is emitted otherwise.
[...]
In particular, the annotation does the following:
Configure Jackson to use the builder for deserialization using #JsonDeserialize(builder=_Foobar_._Foobar_Builder[Impl].class)) on the class (where Foobar is the name of the annotated class, and Impl is added for #SuperBuilder). (An error is emitted if such an annotation already exists.)
Copy Jackson-related configuration annotations (like #JsonIgnoreProperties) from the class to the builder class. This is necessary so that Jackson recognizes them when using the builder.
Insert #JsonPOJOBuilder(withPrefix="") on the generated builder class to override Jackson's default prefix "with". If you configured a different prefix in lombok using setterPrefix, this value is used. If you changed the name of the build() method using using buildMethodName, this is also made known to Jackson.
For #SuperBuilder, make the builder implementation class package-private.
Note: This issue has nothing to do with the usage of a mixin, which can be verified by moving Jackson configuration from the mixin to the class itself and observing that the issue is still present.
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.
What is the difference between these two usage of #Builder?
import lombok.*;
class BuilderAnnotationOnConstructor{
Integer fieldOne, fieldTwo;
#Builder
public BuilderAnnotationOnConstructor(int fieldOne, int fieldTwo) {
this.fieldOne = fieldOne;
this.fieldTwo = fieldTwo;
}
}
#Builder
class BuilderAnnotationOnClass{
Integer fieldOne, fieldTwo;
public BuilderAnnotationOnClass(int fieldOne, int fieldTwo) {
this.fieldOne = fieldOne;
this.fieldTwo = fieldTwo;
}
}
They both compile and run fine but I have noticed weird behaviors in the second case when the annotation is on the class declaration (e.g. in a large project of mine, attributes values assigned to wrong/swapped variable names; unfortunately I was not able to reproduce these behaviors in this simple example)
From Lombok doc :
*Now that the "method" mode is clear, putting a #Builder annotation on a constructor functions similarly; effectively, constructors are just static methods that have a special syntax to invoke them: Their 'return type' is the class they construct, and their type parameters are the same as the type parameters of the class itself
Finally, applying #Builder to a class is as if you added #AllArgsConstructor(access = AccessLevel.PACKAGE) to the class and applied the #Builder annotation to this all-args-constructor. This only works if you haven't written any explicit constructors yourself. If you do have an explicit constructor, put the #Builder annotation on the constructor instead of on the class. Note that if you put both #Value and #Builder on a class, the package-private constructor that #Builder wants to generate 'wins' and suppresses the constructor that #Value wants to make. *
from https://projectlombok.org/features/Builder
I like to make my objects immutable based on this article (Why objects must be immutable).
However, I am trying to parse an object using Jackson Object Mapper. I was initially getting JsonMappingException: No suitable constructor found for type [simple type, class ]: cannot instantiate from JSON object.
I could fix it as mentioned here, by providing a default constructor and making my fields non-final.
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
#AllArgsConstructor
// #NoArgsConstructor(access = AccessLevel.PRIVATE)
#Builder
#Data
public class School {
#NonNull
private final String schoolId;
#NonNull
private final String schoolName;
}
What is a good programming style that I should follow to overcome this problem? Is the only way around is to make my objects mutable?
Can I use a different mapper that does not use the default constructor?
You can use a Jackson factory (method annotated with #JsonCreator) that reads fields off a map and calls your non-default constructor:
class School {
//fields
public School(String id, String name) {
this.schoolId = id;
this.schoolName = name;
}
#JsonCreator
public static School create(Map<String, Object> object) {
return new School((String) object.get("schoolId"),
(String) object.get("schoolName"));
}
//getters
}
Jackson will call the create method with a Map version of the json. And this effectively solves the problem.
I believe your question looks for a Jackson solution, rather than a new pattern/style.
TL;DR: using lombok and avoiding a default constructor
make immutable data class using #Value
annotate all your fields with #JsonProperty("name-of-property")
add lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty to your lombok.config to copy those to generated constructors
create an all-args constructor annotated with #JsonCreator
example:
#Value
#AllArgsConstructor(onConstructor_ = #JsonCreator)
class School {
#JsonProperty("schoolId")
String schoolId;
#JsonProperty("schoolName")
String schoolName;
}
long answer
There is an imo better alternative to a static factory method annotated with #JsonCreator, and that is having a constructor for all Elements (as is required for immutable classes anyway). Annotate that with #JsonCreator and also annotate all parameters with #JsonProperty like this:
class School {
//fields
#JsonCreator
public School(
#JsonProperty("id") String id,
#JsonProperty("name") String name) {
this.schoolId = id;
this.schoolName = name;
}
//getters
}
Those are the options the #JsonCreator annotation gives you. It describes them like this in its documentation:
Single-argument constructor/factory method without JsonProperty annotation for the argument: if so, this is so-called "delegate creator", in which case Jackson first binds JSON into type of the argument, and then calls creator. This is often used in conjunction with JsonValue (used for serialization).
Constructor/factory method where every argument is annotated with either JsonProperty or JacksonInject, to indicate name of property to bind to
You might not even need to explicitly specify the parameter name under some circumstances. The documentation regarding that for #JsonCreator further states:
Also note that all JsonProperty annotations must specify actual name (NOT empty String for "default") unless you use one of extension modules that can detect parameter name; this because default JDK versions before 8 have not been able to store and/or retrieve parameter names from bytecode. But with JDK 8 (or using helper libraries such as Paranamer, or other JVM languages like Scala or Kotlin), specifying name is optional.
Alternatively this will also work nicely with lombok version 1.18.3 or up, where you can add lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty to your lombok.config and therefore have it copy the JsonProperty annotations to the constructor, given that you do annotate all fields with it (which one should do anyway imo). To put the #JsonCreator-annotation on the constructor, you can use the experimental onX feature. Using lombok's #Value for immutable data classes, your DTO then might just look like this (untested):
#Value
//#AllArgsConstructor(onConstructor = #__(#JsonCreator)) // JDK7 or below
#AllArgsConstructor(onConstructor_ = #JsonCreator) // starting from JDK8
class School {
#JsonProperty("schoolId")
String schoolId;
#JsonProperty("schoolName")
String schoolName;
}
I have a Spring MVC project using JPA which I have worked on for some time in the past without this issue. But now for some reason (likely an environmental issue as I have switch to a new laptop since I last worked on it) I am getting this weird error.
The project is essentially a tool for creating and performing surveys which are just a set of questions. There are multiple types of question such as "auto complete question", "multiple choice question", "integer question", etc which collect different types of data. Each of this question types is modeled by a subclass which extends an abstract class called DdmQuestion which looks something like this:
#Entity
#Table(name = "ddm_question")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING, name = "question_type")
#JsonIgnoreProperties({"dataType"})
#JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#question_type")
#JsonSubTypes(value = { #Type(DdmTextQuestion.class),#Type(DdmDateQuestion.class),#Type(DdmTimeQuestion.class),#Type(DdmNumberIntegerQuestion.class),#Type(DdmChoiceMultiQuestion.class),#Type(DdmAutoCompleteQuestion.class) })
public abstract class DdmQuestion {
#Id
#GeneratedValue
#Column(name = "question_id")
private int questionId;
#Column(name = "name")
private String name;
public int getQuestionId() {
return questionId;
}
public void setQuestionId(int questionId) {
this.questionId = questionId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#JsonIgnore
public abstract String getDataType();
}
Note the getDataType() method.
Then, for each question type, I have a subclass extending this which looks something like this:
#Entity
#DiscriminatorValue("ddm_question_date")
public class DdmDateQuestion extends DdmQuestion {
final private String DATA_TYPE = "Long"; // this is the line with the error
#Override
public String getDataType() {
return DATA_TYPE;
}
}
Now, I've never encountered this error before (that I can recall) but Eclipse is throwing up an error here that says:
"The Java field for attribute "DATA_TYPE" is final". That's all it
says.
If I remove the #Entity annotation from the class, this error disappears so evidently something in JPA doesn't like something about this but I never had this error before so I'm thinking something changed in a newer version. My POM is not particularly explicit with dependency versions so this would not be surprising.
Can anyone explain to me why this is happening and what the correct resolution is? I could just remove the "final" from the field declaration but this seems wrong to me as it is definitely a final value...
Thanks in advance.
If it is a field that should not be persisted in the database you usually should take advantage of the transient annotation which would tell the persistence provider to ommit that field in its processing.:
#Transient
final private String DATA_TYPE = "Long";
If Eclipse is smart enough, it should stop highlighting the error altogether.
in this linkshttp://docs.oracle.com/javaee/5/tutorial/doc/bnbqa.html#Entities;
An entity class must follow these requirements:
The class must be annotated with the javax.persistence.Entity annotation.
The class must have a public or protected, no-argument constructor. The class may have other constructors.
The class must not be declared final. No methods or persistent instance variables must be declared final.
If an entity instance be passed by value as a detached object, such as through a session bean’s remote business interface, the class must implement the Serializable interface.
Entities may extend both entity and non-entity classes, and non-entity classes may extend entity classes.
Persistent instance variables must be declared private, protected, or package-private, and can only be accessed directly by the entity class’s methods. Clients must access the entity’s state through accessor or business methods.