Deep copy with lombok toBuilder within a nested class - cleaner way? - java

#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder(toBuilder = true)
public class Example {
public ExampleTwo exampleTwo;
#lombok.Data
#AllArgsConstructor
#NoArgsConstructor
#Builder(toBuilder = true)
public static class ExampleTwo {
private SomeData someData;
private AnotherField anotherField;
}
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder(toBuilder = true)
public class SomeData {
private String specialId;
private String firstName;
private String lastName;
}
So I retrieved an Example instance and made a deep copy to duplicate it. But I want to be able to set one of the fields of the copied object which is specialId from the SomeData nested class. My current working implementation is this:
SomeData someData = example.getExampleTwo().getSomeData().toBuilder().specialID("SPE_1").build();
Example.ExampleTwo exampleTwo = example.getExampleTwo().toBuilder().someData(someData).build();
Example duplicateExample = example.toBuilder().exampleTwo(exampleTwo).build();
Do you have thoughts on ways to make this cleaner without having to go through these additional steps? Would prefer it to be easier to read. I'm avoiding Serializable implementation and declaring it as a Cloneable interface since I've read to avoid those. Last resort would be to use a library.

#With might be helpful:
Example example = Example.builder().build();
Example.ExampleTwo exampleTwo = example.getExampleTwo();
SomeData someData = exampleTwo.getSomeData();
return example.withExampleTwo(
exampleTwo.withSomeData(
someData.withSpecialId("SPE_1")
)
);

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;
}
}

How to create an index programmatically for a dynamic Mongo collection? ( Java Spring Data MongoDB )

I need help with this, here is my document class:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Document(collection = "#{#dateService.getCurrentDate()}")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class LogDocument {
private ObjectId _id;
private String log;
private String key;
}
As you can see my collection name is dynamic depending on date, e.g. 2020-10-05.
I want to create a unique index on the key. HOW?
When the collection name is fixed I can simply do the following in the mongo Configuration class:
mongoTemplate.indexOps("{collection name}").ensureIndex(indexDefinition.unique());
but since the collection is dynamic, I need a way to trigger a PostConstruct for every time a new collection is created so that I can create the index.
The answer is simply using annotations
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
//For Compound index:
#CompoundIndexes({
#CompoundIndex(name = "key_app", def = "{'key' : 1, 'app': 1}")
})
#Document(collection = "#{#dateService.getCurrentDate()}")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class LogDocument {
private ObjectId _id;
private String log;
//For a unique index on one field:
// #Indexed(unique = true)
private String key;
private String app;
}

Why doesn't BeanCopier work in copying properties?

I want use BeanCopier to do the property copying between the following two porous
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(callSuper = true)
#TableName("t_order")
public class Order extends BaseEntity {
private static final long serialVersionUID=1L;
private Long userId;
private Integer amount;
private Long productId;
}
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class OrderDTO {
private Long userId;
private Integer amount;
private Long productId;
}
for the following codes,
public static void main(String[] args) {
Order order = Order.builder().productId(3333L).userId(9999L).amount(32).build();
OrderDTO orderDTO = new OrderDTO();
BeanCopier orderCopier = BeanCopier.create(Order.class, OrderDTO.class, false);
orderCopier.copy(order, orderDTO, null);
JSONUtils.toJSONString(orderDTO);
}
the properties of orderDTO are not set, the fields of orderDTO are all null, what is wrong?
ohh there is simple thing missing , please add getters and setters , BeanCopier internally uses ReflectUtils to find getters and setters.
Please try and add those and then test.
There also a alternative -
you can simple use Spring's BeanUtils and it's copyProperties - there are multiple options available
Spring BeanUtils
You can simply use it as
BeanUtils.copyProperties( sourceBean , targetBean );
You can find different examples HERE

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.

Categories

Resources