Assuming two simple classes:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class Party {
protected Long id;
protected String status;
}
#Data
#SuperBuilder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(callSuper = true)
public class Person extends Party {
private String name;
private Long sex;
}
The compilation fails on the following error. Upon reading Lombok | #SuperBuilder I have no idea what I could miss.
C:\Dev\companyproject\src\main\java\com\companyproject\entity\Person.java:12
java: type com.companyproject.entity.Party.PartyBuilder does not take parameters
The issue here is the incorrect #Builder annotation on the parent class. The documentation for #SuperBuilder mentions:
Most importantly, it requires that all superclasses also have the #SuperBuilder annotation.
So the correct parent class would be:
#Data
#SuperBuilder // <- annotation replaced here
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class Party {
protected Long id;
protected String status;
}
Addendum:
A key difference between both annotations is that #SuperBuilder also creates a constructor of the class expecting a corresponding builder argument.
For Party it would look like:
protected Party(PartyBuilder<?, ?> b) {
this.id = b.id;
this.status = b.status;
}
and for Person:
protected Person(PersonBuilder<?, ?> b) {
super(b);
this.name = b.name;
this.sex = b.sex;
}
As you can see, the child class constructor wants to pass its own builder to the parent class constructor and this will only be possible if there is a matching constructor there, and #Builder wouldn't generate it.
Also PersonBuilder<> extends PartyBuilder<>, that is why calling super with the child type builder works fine here.
Related
I have two classes OAuth2Token and CachedOAuth2Token that extends a class called AbstractOAuth2Token.
AbstractOAuth2Token.java
#SuperBuilder
#Jacksonized
#JsonSubTypes({
#JsonSubTypes.Type(value = OAuth2Token.class),
})
#Getter
#Setter
#ToString
public abstract class AbstractOAuth2Token {
#JsonProperty("access_token")
private String accessToken;
#JsonProperty("token_type")
private String tokenType;
}
OAuth2Token.java
#Getter
#Setter
#SuperBuilder
#ToString(callSuper = true)
#JsonTypeName("OAuth2Token")
#Jacksonized
public class OAuth2Token extends AbstractOAuth2Token {
#JsonProperty("expires_in")
private int expiresIn;
}
CachedOAuth2Token.java
#Getter
#Setter
#SuperBuilder
#ToString(callSuper = true)
public class CachedOAuth2Token extends AbstractOAuth2Token {
private LocalDateTime expirationDate;
}
Unfortunately my Maven project doesn't build because AbstractOAuth2Token.java: Builders on abstract classes cannot be #Jacksonized (the builder would never be used).
Even if the code works as expected if the AbstractOAuth2Token isn't abstract, then I'm able to create an instance of it using the builder which indeed isn't what I want. Its constructor is protected, so no problem there.
The idea is that I want AbstractOAuth2Token to be abstract without losing any fuctionality in the children. I'm a fan of Lombok, so I want to be able to use the autogenerated builders but together with Jackson.
It's a Wildfly 11 project with Lombok 1.18.16
How can I solve this issue?
Don't add #Jacksonized to your abstract base class. Non-#Jacksonized #SuperBuilders are compatible with #Jacksonized #SuperBuilders. As Jackson will never use AbstractOAuth2Token's builder directly, there is no need to configure it for Jackson explicitly.
I am trying to map DTO to the corresponding #Entity in the service layer.
Condition may be of a set of types: Amount, Title, Date. Each condition, except Amount, has a unique predefined set of clauses.
TitleCondition: includes, startsWith
DateCondition: from, until
The idea is to use common Condition entity with #Inheritance(strategy= InheritanceType.TABLE_PER_CLASS).
The 2 problems I see with this code is:
It is unclear how to properly set data
data type is Object
Is there a way to use convenient Lombok's #Builder with given mapping? What would be the simpler and better way to map dto to entity?
Service:
#Service
public class FilterService {
private Condition convertConditionDtoToEntity(ConditionDto conditionDto) {
Type type = typeRepository.findFirstByName(conditionDto.getType())
.orElseThrow(UnsupportedOperationException::new);
Clause clause;
if (conditionDto.getClause() != null) {
clause = clauseRepository.findFirstByName().orElseThrow(UnsupportedOperationException::new);
}
if (conditionDto.getType().equals("amount")) {
return AmountCondition.builder().type(type).data(???).build();
} else if (conditionDto.getType().equals("title")) {
return TitleCondition.builder().type(type).clause(clause).data(???).build();
} else if (conditionDto.getType().equals("date")) {
return DateCondition.builder().type(type).clause(clause).data(???).build();
} else {
throw new UnsupportedOperationException();
}
}
}
Condition
#Getter
#SuperBuilder
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Inheritance(strategy= InheritanceType.TABLE_PER_CLASS)
public abstract class Condition {
#Id
#GeneratedValue
private long id;
#ManyToOne
private Filter filter;
#Getter
#ManyToOne
public Type type;
public abstract Object getData();
}
DateCondition
#Data
#SuperBuilder
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class DateCondition extends Condition {
#Column
int clauseId;
#Column
#Temporal(TemporalType.DATE)
Date date;
#Getter
#ManyToOne
private Clause clause;
#Override
public Object getData() {
return date;
}
}
TitleCondition
#Data
#SuperBuilder
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class TitleCondition extends Condition {
#Column
int clauseId;
#Column
String title;
#Getter
#ManyToOne
private Clause clause;
#Override
public Object getData() {
return title;
}
}
AmountCondition
#Data
#SuperBuilder
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class AmountCondition extends Condition {
#Column
int amount;
#Override
public Object getData() {
return amount;
}
}
Clause
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class Clause {
#Id
#GeneratedValue
private long id;
#Column
String name;
#OneToMany(mappedBy = "clause")
private Set<Type> types = new HashSet();
}
Type
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class Type {
#Id
#GeneratedValue
private long id;
#Column
String name;
#ManyToOne
private Clause clause;
#OneToMany(mappedBy = "type")
private Set<Condition> conditions;
}
I have two forms that I use a lot and save a lot of time.
first: transform your model into json and convert the json to object the Mapper class (this link will help https://www.baeldung.com/jackson-object-mapper-tutorial)
second: spring has some cool functions about it. an example would be the BeanUtils.copyProperties function (source, target);
data problem: the data field does not exist for this reason you do not need to set it.
Another problem that I was able to notice is that their properties are all defaulted so set them as private.
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.
I have a SpringBoot 2 app that uses using Couchbase as a database, Spring-Boot and Spring-Data and Lombok fot the getters and equals method
I have created this Repository
#ViewIndexed(designDoc = "bendicionesDoc")
public interface BenRepository extends CouchbaseRepository<BendicionesDoc, String> {
#Query("#{#n1ql.selectEntity} where #{#n1ql.filter} AND ANY uuid IN data.identifier.id SATISFIES uuid = $1 END")
List<BendicionesDoc<Item>> findById(String id);
}
and here all the objects created with Lombok library
public class BendicionesDoc<T>implements Serializable {
#Field
private T data;
}
and
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(NON_NULL)
public class Item {
private List<Identifier> identifier;
}
and
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(NON_NULL)
#EqualsAndHashCode
public class Identifier {
private String id;
private MasterServant idContext;
private MasterServant idScope;
}
and
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(NON_NULL)
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class MasterServant {
private String context;
#JsonValue
#EqualsAndHashCode.Include
private String value;
private Name valueDescription;
#JsonCreator
public MasterServant(String value) {
this.value = value;
}
}
But when I run the repository query I got always 0 results, even there are docs. in the DB:
You need to define your reference type in CouchbaseRepository<T, K> then simply add the reference type Item as CouchbaseRepository<BendicionesDoc<Item>, String> and just use Repository query keywords for findById(String id).
public interface BenRepository extends CouchbaseRepository<BendicionesDoc<Item>, String> {
List<BendicionesDoc<Item>> findById(String id);
}
I have this simple code:
#Data
#Builder
public class RegistrationInfo {
private String mail;
private String password;
public RegistrationInfo(RegistrationInfo registrationInfo) {
this.mail = registrationInfo.mail;
this.password = registrationInfo.password;
}
}
First I was using only the #Builder Lombok annotation and everything was fine. But I added the constructor and the code does not compile any more. The error is:
Error:(2, 1) java: constructor RegistrationInfo in class com.user.RegistrationInfo cannot be applied to given types;
required: com.user.RegistrationInfo
found: java.lang.String,java.lang.String
reason: actual and formal argument lists differ in length
So I have two questions:
Why is Lombok #Builder not compatible with this constructor?
How do I make the code compile taking into account that I need both the builder and the constructor?
You can either add an #AllArgsConstructor annotation, because
#Builder generates an all-args constructor iff there are no other
constructors defined.
(Quotting #Andrew Tobilko)
Or set an attribute to #Builder : #Builder(toBuilder = true) This give you the functionality of a copy constructor.
#Builder(toBuilder = true)
class Foo {
// fields, etc
}
Foo foo = getReferenceToFooInstance();
Foo copy = foo.toBuilder().build();
When you provide your own constructor then Lombok doesn't create a constructor with all args that #Builder is using. So you should just add annotation #AllArgsConstructor to your class:
#Data //try to avoid as it's an anti-pattern
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class RegistrationInfo {
//...
}
Presumably, #Builder generates an all-args constructor if there are no other constructors defined.
#Data
#Builder
#AllArgsConstructor(access = AccessLevel.PRIVATE)
class RegistrationInfo {
private String mail;
private String password;
private RegistrationInfo(RegistrationInfo registrationInfo) {
this(registrationInfo.mail, registrationInfo.password);
}
}