Using SuperBuilder to extend parent class using Lombok - java

I had a DTO that was using Lombok functionaliy as shown below.But now due to some requirement I had to extend my DTO to a parent class which looks like below.How can I do minimal change in my DTO to support that.I tried using #SuperBuilder annotation but it failed.
DTO Before:
#Getter
#ToString
#EqualsAndHashCode
#Builder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public class RequestMessage {
private final String name;
}
Parent Class that needs to be extended
#Data
#SuperBuilder(toBuilder = true)
#JsonDeserialize(builder = MyDTO.Builder.class)
public abstract class MyDTO implements Serializable {
#JsonIgnore private final ObjectMapper objectMapper = new ObjectMapper();
protected String myAccountId;
protected MyDTO() {}
public static int hashCode(Object... objects) {
return Arrays.deepHashCode(objects);
}
public static boolean equal(Object o1, Object o2) {
// implementation of equals method
}
public abstract String emitSerializedPayload() throws JsonProcessingException;
#JsonPOJOBuilder(withPrefix = "")
protected abstract static class Builder<T extends MyDTO, B extends Builder<T, B>> {
protected T dtoInstance;
protected B builderInstance;
public Builder() {
dtoInstance = createDtoInstance();
builderInstance = returnBuilderInstance();
}
protected abstract T createDtoInstance();
protected abstract B returnBuilderInstance();
public B myAccountId(String accountId) {
dtoInstance.myAccountId = accountId;
return builderInstance;
}
public T build() {
return dtoInstance;
}
}
}
I tried to build RequestMessageClass manually and it works fine but there are lot of classes in my application and I dont want to change them manually, how can I change my existing RequestMessage class with annotations or some minimum change to get it working.
This is what I tried but I am getting compilation error when doing this
RequestMessage.Builder().name(myName).myAccountId(myAcId).build();
What I tried is like shown below:
#Getter
#ToString
#EqualsAndHashCode(callSuper = true)
#SuperBuilder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public class RequestMessage extends MyDTO {
private final String name;
#Override
public String emitSerializedPayload() throws JsonProcessingException {
// TODO Auto-generated method stub
return null;
}
}

You shouldn't mix lombok #Builder with static inner Builder class. If it is possible to get rid of Builder class, the next code should work.
RequestMessage:
#Getter
#ToString
#EqualsAndHashCode(callSuper = true)
#SuperBuilder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public class RequestMessage extends MyDTO {
private final String name;
#Override
public String emitSerializedPayload() throws JsonProcessingException {
return null;
}
public RequestMessage(String myAccountId, String name) {
super(myAccountId);
this.name = name;
}
}
MyDTO:
#Data
#SuperBuilder(toBuilder = true)
public abstract class MyDTO implements Serializable {
#JsonIgnore private final ObjectMapper objectMapper = new ObjectMapper();
protected String myAccountId;
protected MyDTO() {}
public MyDTO(String myAccountId) {
this.myAccountId = myAccountId;
}
public static int hashCode(Object... objects) {
return Arrays.deepHashCode(objects);
}
public static boolean equal(Object o1, Object o2) {
// implementation of equals method
return false;
}
public abstract String emitSerializedPayload() throws JsonProcessingException;
}
Test:
#Test
void name() {
String myName = "myName";
String myAccountId = "myAccountId";
var request = RequestMessage.builder().name(myName).myAccountId(myAccountId).build();
System.out.println("request = " + request);
RequestMessage requestMessage = new RequestMessage(myAccountId, myName);
}

Related

Q class not generating by query-dsl for nested embedded objects inside document class in spring data

I have below collection and embedded documents
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(JsonInclude.Include.NON_NULL)
#Document(collection = "person_details")
public class PersonDetails {
#MongoId(FieldType.OBJECT_ID)
private String managerId;
.
.
#QueryInit("addressDetails.*")
private List<ContactDetails> contactDetails;
.
}
And ContactDetails.java looks like below:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ContactDetails {
private AddressDetails addressDetails;
private StateDetails stateDetails;
}
StateDetails look like below:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(JsonInclude.Include.NON_NULL)
#Document(collection = "statedetails")
public class StateDetails {
#MongoId(FieldType.OBJECT_ID)
private String stateId;
private String stateName;
}
And AddressDetails look like below:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(JsonInclude.Include.NON_NULL)
public class AddressDetails {
private String address;
#Field(targetType = FieldType.STRING)
private AddressTypeEnum addressType;
}
After building , I am not getting Q class for AddressDetails, due to which I am facing issues in building Predicate. Below is my generated QPersonDetail class
#Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QPersonDetails extends EntityPathBase<PersonDetails> {
private static final long serialVersionUID = 184263140L;
private static final PathInits INITS = new PathInits("*", "contactDetails.addressDetails.*");
public static final QPersonDetails managerDetails = new QPersonDetails("managerDetails");
public final ListPath<ContactDetails, QContactDetails> contactDetails = this.<ContactDetails, QContactDetails>createList("contactDetails", ContactDetails.class, QContactDetails.class, INITS.get("contactDetails"));
public QPersonDetails(String variable) {
this(PersonDetails.class, forVariable(variable), INITS);
}
public QPersonDetails(Path<? extends PersonDetails> path) {
this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS));
}
public QPersonDetails(PathMetadata metadata) {
this(metadata, PathInits.getFor(metadata, INITS));
}
public QPersonDetails(PathMetadata metadata, PathInits inits) {
this(PersonDetails.class, metadata, inits);
}
}
And below is generated QContactDetails:
#Generated("com.querydsl.codegen.DefaultEmbeddableSerializer")
public class QContactDetails extends BeanPath<ContactDetails> {
private static final long serialVersionUID = 1039545873L;
private static final PathInits INITS = PathInits.DIRECT2;
public static final QContactDetails contactDetails = new QContactDetails("contactDetails");
public final SimplePath<AddressDetails> addressDetails = createSimple("addressDetails", AddressDetails.class);
public QContactDetails(String variable) {
this(ContactDetails.class, forVariable(variable), INITS);
}
public QContactDetails(Path<? extends ContactDetails> path) {
this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS));
}
public QContactDetails(PathMetadata metadata) {
this(metadata, PathInits.getFor(metadata, INITS));
}
public QContactDetails(PathMetadata metadata, PathInits inits) {
this(ContactDetails.class, metadata, inits);
}
}
I referred many threads including
https://querydsl.com/static/querydsl/5.0.0/reference/html/ch03s03.html but still not able to figure out issue. Even i tried with
#QueryInit("*.*")
annotation on contactDetails, above. Any idea, where I am missing. I dont want AddressDetails to be annotated as #Document.
I found this thread Querydsl 4 StringExpression of a field inside a SimplePath
This solves theproblem, but I am still confused with the concept, how #QueryEmbeddable works

Single Table for Class with Abstract Property

I'm trying to create an entity class where one of the properties is an abstract class, and I want it stored with the properties as a single row in a single table.
I get one of the issues is mapping it back to the correct implementation of the abstract property class, but I know this can at least be solved when the abstract class is the entity. I just can't seem to get it to work properly when that abstract class is a property of the entity.
SQL:
create table my_entity(
id bigint not null,
version int not null,
property_type varchar(255) not null,
property1 varchar(255),
property2 varchar(255),
primary key(id)
);
MyEntity.java:
#Entity
#Table(name = "my_entity")
public final class MyEntity extends ConcurrencySafeEntity {
#Id
private final Long id;
#Embedded
private final MyProperty property;
public MyEntity(Long id, MyProperty property) {
this.id = Objects.requireNonNull(id);
this.property = Objects.requireNonNull(property);
}
private MyEntity() {
this.id = null;
this.property = null;
}
public long getId() {
return id;
}
public MyProperty getProperty() {
return property;
}
}
MyProperty.java:
#Embeddable
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "property_type", discriminatorType = DiscriminatorType.STRING)
public abstract class MyProperty {
private final String propertyType;
public MyProperty(String propertyType) {
this.propertyType = Objects.requireNonNull(propertyType);
}
protected MyProperty() {
this.propertyType = null;
}
public String getType() {
return propertyType;
}
}
MyProperty1.java:
#DiscriminatorValue("PROPERTY_1")
public final class MyProperty1 extends MyProperty {
private final String property1;
public MyProperty1(String property1) {
super("PROPERTY_1");
this.property1 = Objects.requireNonNull(property1);
}
private MyProperty1() {
this.property1 = null;
}
public String getProperty1() {
return property1;
}
}
MyProperty2.java:
#DiscriminatorValue("PROPERTY_2")
public final class MyProperty2 extends MyProperty {
private final String property2;
public MyProperty2(String property2) {
super("PROPERTY_2");
this.property2 = Objects.requireNonNull(property2);
}
private MyProperty2() {
this.property2 = null;
}
public String getProperty2() {
return property2;
}
}
MyEntityRepository.java:
#Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
}
Code to produce error:
#Autowired
private MyEntityRepository repo;
#Transactional(propagation = Propagation.NOT_SUPPORTED)
public void will_produce_error() {
final MyEntity myEntity = new MyEntity(1337L, new MyProperty1("123"));
repo.save(myEntity);
final String myProperty = repo.findById(1337L)
.map(MyEntity::getProperty)
.filter(MyProperty1.class::isInstance)
.map(MyProperty1.class::cast)
.map(MyProperty1::getProperty1)
.orElseThrow();
// myProperty should be "123"
}
Produces the following error:
org.springframework.orm.jpa.JpaSystemException: Cannot instantiate abstract class or interface: : MyProperty; nested exception is org.hibernate.InstantiationException: Cannot instantiate abstract class or interface: : MyProperty
The error happens on this line:
repo.save(myEntity);
I'm probably missing some annotation or using one incorrectly or something. Anyone know how to get this to work properly? I don't want to have to change the structure of the class, only apply the correct annotations etc to make it work properly.

lombok - #Builder pattern in multiple shots

I use #Builder of lombok project, so consider I have this example:
#Builder
public class Client {
private #Getter #Setter Integer id;
private #Getter #Setter String name;
}
Which is equivalent to:
public class Client {
private #Getter #Setter Integer id;
private #Getter #Setter String name;
public static class Builder {
private Integer id;
private String name;
private Builder() {
}
public Builder id(final Integer value) {
this.id = value;
return this;
}
public Builder name(final String value) {
this.name = value;
return this;
}
public Client build() {
return new Client(this);
}
}
public static Client.Builder builder() {
return new Client.Builder();
}
private Client(final Builder builder) {
this.id = builder.id;
this.name = builder.name;
}
}
When I try to set all the the fields in one shot there are no problem:
public static void main(String[] args) {
Client client = Client.builder()
.id(123)
.name("name")
.build();
}
Output:
Client{id=123, name=name}
Now, consider I want to make it in multiple shots. For example:
public static void main(String[] args) {
Client client = Client.builder()
.id(123)//<-------------------------- Set just the id
.build();
client = Client.builder()
.name("name")//<--------------------- Set name
.build();
}
This logically return null for id:
Client{id=null, name=name}
Generally, without lombok, I solve this issue with adding a new constructor in the Builder class which take the same object:
public static class Builder {
// ...
public Builder(Client client) {
this.id = client.id;
this.name = client.name;
}
// ...
}
Then I pass my object to that constructor:
Client client = Client.builder()
.id(123)
.build();
client = new Client.Builder(client)//<---------------- Like this
.name("name")
.build();
This solves my issue, but I can't arrive to solve it with lombok. Is there any way to solve this issue?
You can use the toBuilder property to do that.
#Builder(toBuilder=true)
public class Client {
private #Getter #Setter Integer id;
private #Getter #Setter String name;
}
and then you can use it like that
public void main(String[] args){
Client client = Client.builder()
.id(123)
.build();
client = client.toBuilder()
.name("name")
.build();
}
Depending on your use case, you can also keep a reference to an existing builder. You can have multiple to the build method.
Disclosure: I am a lombok developer.

Using lombok's #Builder with inheritance and Jackson

I am trying to use lombok's #Builder with inheritance and Jackson.
I was building things from https://reinhard.codes/2015/09/16/lomboks-builder-annotation-and-inheritance/, https://gist.github.com/pcarrier/14d3a8e249d804cfbdee and Builder pattern with inheritance
Here is what i have
UserInput.java
#JsonDeserialize(builder = UserInput.UserInputBuilder.class)
#Builder
#Data
public class UserInput {
private int userId;
private UsersChoice usersChoice;
private ChoiceAttributes choiceAttributes;
#JsonPOJOBuilder(withPrefix = "")
public static final class UserInputBuilder {
}
public enum UserChoice {
CHOICE1,
CHOICE2
}
}
Based on user choice, corresponding ChoiceAttributes builder should be used.
ChoiceAttributes.java
public abstract class ChoiceAttributes {
//nothing to do here
public static class ChoiceAttributesBuilder {
}
public static ChoiceAttributesBuilder getMeMyBuilderBasedOnUserChoice(UserChoice userChoice)
{
ChoiceAttributesBuilder choiceAttributesBuilder = null;
switch(userChoice){
case CHOICE1:
choiceAttributesBuilder = new ChoiceAttributesForChoice1.ChoiceAttributesForChoice1Builder(); //err!! stuck?
}
}
ChoiceAttributesForChoice1.java
#JsonDeserialize(builder = ChoiceAttributesForChoice1.ChoiceAttributesForChoice1Builder.class)
#Builder
#Data
public class ChoiceAttributesForChoice1 extends ChoiceAttributes {
private int x;
//and so on
#JsonPOJOBuilder(withPrefix = "")
public static class ChoiceAttributesForChoice1Builder extends ChoiceAttributesBuilder {
}
}
ChoiceAttributesForChoice2.java
#JsonDeserialize(builder = ChoiceAttributesForChoice2.ChoiceAttributesForChoice2Builder.class)
#Builder
#Data
public class ChoiceAttributesForChoice2 extends ChoiceAttributes {
private float y;
//and so on
#JsonPOJOBuilder(withPrefix = "")
public static class ChoiceAttributesForChoice2Builder extends ChoiceAttributesBuilder {
}
}
Is there a better way to achieve this? How can i build "UserInput" using its builder and ChoiceAttributes based on UsersChoice?

Get data from a repository using Spring

Ok so I am new to spring and don't really know how this works. I have been trying a few things and think its close to doing it but not getting any data from the server and giving me this error
Unsatisfied dependency expressed through constructor argument with index 4 of type [jp.co.fusionsystems.dimare.crm.service.impl.MyDataDefaultService]: : Error creating bean with name 'MyDataDefaultService' defined in file
My end point
//mobile data endpoint
#RequestMapping(
value = API_PREFIX + ENDPOINT_MyData + "/getMyData",
method = RequestMethod.GET)
public MyData getMyData() {
return MyDataDefaultService.getData();
}
My Object
public class MyData {
public MyData(final Builder builder) {
videoLink = builder.videoLink;
}
private String videoLink;
public String getVideoLink()
{
return videoLink;
}
public static class Builder
{
private String videoLink = "";
public Builder setVideo(String videoLink)
{
this.videoLink = videoLink;
return this;
}
public MyData build()
{
return new MyData(this);
}
}
#Override
public boolean equals(final Object other) {
return ObjectUtils.equals(this, other);
}
#Override
public int hashCode() {
return ObjectUtils.hashCode(this);
}
#Override
public String toString() {
return ObjectUtils.toString(this);
}
}
The Repository
public classMyServerMyDataRepository implements MyDataRepository{
private finalMyServerMyDataJpaRepository jpaRepository;
private final MyDataConverter MyDataConverter = new MyDataConverter();
#Autowired
publicMyServerMyDataRepository(finalMyServerMyDataJpaRepository jpaRepository) {
this.jpaRepository = Validate.notNull(jpaRepository);
}
#Override
public MyData getData() {
MyDataEntity entity = jpaRepository.findOne((long) 0);
MyData.Builder builder = new MyData.Builder()
.setVideo(entity.getVideoLink());
return builder.build();
}
The DefaultService that gets called by the endpoint
public class MyDataDefaultService {
private static final Logger logger = LoggerFactory.getLogger(NotificationDefaultService.class);
private finalMyServerMyDataRepository repository;
#Autowired
public MyDataDefaultService(MyServerMyDataRepository repository) {
this.repository = Validate.notNull(repository);
}
//Get the data from the server
public MobileData getData()
{
logger.info("Get Mobile Data from the server");
//Get the data from the repository
MobileData mobileData = repository.getData();
return mobileData;
}
}
The Converter
public class MyDataConverter extends AbstractConverter<MyDataEntity, MyData>
{
#Override
public MyData convert(MyDataEntity entity) {
MyData.Builder builder = new MyData.Builder()
.setVideo(entity.getVideoLink());
return builder.build();
}
}
My Entity
#Entity
#Table(name = “myServer”)
public class MyDataEntity extends AbstractEntity{
#Column(name = "video_link", nullable = true)
private String videoLink;
public String getVideoLink() {
return videoLink;
}
public void setVideoLink(final String videoLink) {
this.videoLink = videoLink;
}
}
Thank you for any help with this
Hibernate entity should have default constructor defined and implement Serializable interface as well, assume AbstractEntity matches the requirement. Hibernate won't accept an entity without a primary key so you have to define the one too:
#Entity
#Table(name = “myServer”)
public class MyDataEntity implements Serializable {
#Id
#GeneratedValue
private Long id;
#Column(name = "video_link", nullable = true)
private String videoLink;
public MyDataEntity() {
}
...setters&getters
}
MyData object represents the JSON server response, you can use Jackson annotations to control the result JSON properties:
public class MyDataResponse {
#JsonProperty("video_link")
private String videoLink;
public MyDataResponse() {
}
public MyDataResponse(String videoLink) {
this.videoLink = videoLink;
}
...setters&getters
}
Spring has an awesome project so called Spring Data that provides the JPA repositories, so there's no even the #Repository annotation ever needed:
public class MyDataRepository extends CrudRepository<MyDataEntity, Long> {
}
The Builder class represents the Service layer:
#Service
public class MyDataService {
#Autowired
private MyDataRepository myDataRepository;
public MyDataResponse getMyData(Long id) {
MyDataEntity entity = myDataRepository.findOne(id);
...rest logic, copy necessary data to MyDataResponse
}
}
Then a controller is:
#RestController // #ResponseBody not needed when using like this
public MyDataController {
#Autowired
private MyDataService myDataService;
#RequestMapping("/getMyData") // no need to specify method for GET
public MyDataResponse getMyData(#RequestParam("ID") Long myDataId) {
... validation logic
return myDataService.getMyData(myDataId); // return response
}
}
Now it should work, don't forget to add required dependencies to your classpath.

Categories

Resources