lombok call child class function for default builder - java

I have 2 classes, where parent needs some property from child class while building. Is there a way to support this using lombok builders?
Parent.java
import lombok.Builder;
import lombok.Getter;
import lombok.experimental.SuperBuilder;
#Getter
#SuperBuilder
public abstract class Parent {
#Builder.Default
private String requestType = getRequestTypeFromSubclass();
abstract String getRequestTypeFromSubclass();
}
Child.java
import lombok.Builder;
import lombok.Getter;
import lombok.experimental.SuperBuilder;
import java.util.List;
#Getter
#SuperBuilder
public class Child extends Parent {
#Override
String getRequestTypeFromSubclass() {
return "Child1";
}
}
The above fails in compilation with message
error: non-static method getRequestType() cannot be referenced from a
static context #SuperBuilder

As suggested in Baeldung use toBuilder=true and remove #Builder.Default
import lombok.Getter;
import lombok.experimental.SuperBuilder;
#Getter
#SuperBuilder(toBuilder=true)
public abstract class Parent {
private String requestType = getRequestType();
abstract String getRequestType();
}
With this, we can get rid of the double initialization
Change calling builder using toBuilder, Child:
import lombok.Getter;
import lombok.experimental.SuperBuilder;
#Getter
#SuperBuilder
public class Child extends Parent {
#Override
String getRequestType() {
return "Child1";
}
public static void main(String[] args) {
Child child = Child.builder().build();
System.out.println(child.getRequestType());
}
}

Builder.Default is defined in static scope and expression might not able to resolve the abstract method implementation.
Probably you want to try this instead:
#Getter
#SuperBuilder
public abstract class Parent {
private String reqType;
abstract String getRequestType();
public String getReqType() {
return Objects.isNull(reqType) ? getRequestType() : reqType;
}
}
Override the property method with different method name from actual attribute getter method and should resolve the issue.
Try with:
Parent parent = Child.builder().build();

While lombok is a great tool for many things, in this very instance, you might want to avoid usign the builder pattern. The effect you desire (accessing a child's method at parent-initialization time is possible inside of standard POJO constructors.
The following works for me:
#Getter
public abstract class Parent {
private String requestType = defineRequestType();
protected abstract String defineRequestType();
}
#Getter
public class Child extends Parent {
protected String defineRequestType() {
return "child1";
}
}
and the following test case:
#Test
public void test_lombokBuilderPolymorphism() {
Child child1 = new Child();
assertEquals("child1", child1.getRequestType());
}
So unless you definitely require the builder pattern for some other reasons, the default POJO's capabilities seem to be enough here.
On a side note: You should name the defining method other than get... so it does not collide with the generated getter (getRequestType).

Related

Migrate Spring hateos ResourceAssembler to RepresentationModelAssembler

According to this post ResourceAssembler is changed to RepresentationModelAssembler
I have this code which is using Spring HATEOAS 1.0:
import org.springframework.hateoas.ResourceAssembler;
public class BaseAssembler<T extends BaseTransaction, D extends BaseResource>
implements ResourceAssembler<T, D> {
...
}
After migration to implementation 'org.springframework.boot:spring-boot-starter-hateoas:2.6.4'
I changed it to:
public class BaseAssembler<T extends BaseTransaction, D extends BaseResource>
implements RepresentationModelAssembler<T, D> {
.........
}
But I get error:
Type parameter 'D' is not within its bound; should extend 'org.springframework.hateoas.RepresentationModel<?>'
Do you know how I can fix this issue?
The compiler is reporting that the type parameter D is not within its bound in your definition:
public class BaseAssembler<T extends BaseTransaction, D extends BaseResource>
implements RepresentationModelAssembler<T, D> {
.........
}
In other words, it means that you cannot use D extends BaseResource to implement RepresentationModelAssembler<T, D> (note the type parameter D here) because that type should extend 'org.springframework.hateoas.RepresentationModel<?>'.
RepresentationModelAssembler gives you the ability to convert between domain types, your entities, to RepresentationModels, a based class conceived to enrich your DTOs to collect links.
It is defined as follows:
public interface RepresentationModelAssembler<T, D extends RepresentationModel<?>>
Note again the definition of the type parameter D.
In your code you need to use something like:
public class BaseAssembler<T extends BaseTransaction, D extends RepresentationModel<?>>
implements RepresentationModelAssembler<T, D> {
.........
}
Please, consider read for instance some this or this other article, they provide a great variety of examples and uses cases about showcasing how you can implement the desired behavior.
For example, given the following entity, extracted from one of the cited articles:
#Entity
public class Director {
#Id
#GeneratedValue
#Getter
private Long id;
#Getter
private String firstname;
#Getter
private String lastname;
#Getter
private int year;
#OneToMany(mappedBy = "director")
private Set<Movie> movies;
}
And the following DTO:
#Builder
#Getter
#EqualsAndHashCode(callSuper = false)
#Relation(itemRelation = "director", collectionRelation = "directors")
public class DirectorRepresentation extends RepresentationModel<DirectorRepresentation> {
private final String id;
private final String firstname;
private final String lastname;
private final int year;
}
Your RepresentationModelAssembler will look like:
#Component
public class DirectorRepresentationAssembler implements RepresentationModelAssembler<Director, DirectorRepresentation> {
#Override
public DirectorRepresentation toModel(Director entity) {
DirectorRepresentation directorRepresentation = DirectorRepresentation.builder()
.id(entity.getId())
.firstname(entity.getFirstname())
.lastname(entity.getLastname())
.year(entity.getYear())
.build();
directorRepresentation.add(linkTo(methodOn(DirectorController.class).getDirectorById(directorRepresentation.getId())).withSelfRel());
directorRepresentation.add(linkTo(methodOn(DirectorController.class).getDirectorMovies(directorRepresentation.getId())).withRel("directorMovies"));
return directorRepresentation;
}
#Override
public CollectionModel<DirectorRepresentation> toCollectionModel(Iterable<? extends Director> entities) {
CollectionModel<DirectorRepresentation> directorRepresentations = RepresentationModelAssembler.super.toCollectionModel(entities);
directorRepresentations.add(linkTo(methodOn(DirectorController.class).getAllDirectors()).withSelfRel());
return directorRepresentations;
}
}
In terms of your interfaces and object model:
#Entity
public class Director extends BaseTransaction{
#Id
#GeneratedValue
#Getter
private Long id;
#Getter
private String firstname;
#Getter
private String lastname;
#Getter
private int year;
#OneToMany(mappedBy = "director")
private Set<Movie> movies;
}
public class DirectorRepresentationAssembler
extends BaseAssembler<Director, DirectorRepresentation>
implements RepresentationModelAssembler<Director, DirectorRepresentation> {
//... the above code
}
DirectorRepresentation is the same as presented above.
The Spring HATEOAS reference guide itself provides some guidance as well about the changes performed in Spring HATEOAS 1.0 and about how to migrate from the previous version. It even includes a script that may be of help.
In any case, as indicated above, in your use case you only need to modify the BaseAssembler interface to be defined in terms of the type D extends RepresentationModel<?>; then try relating in some way BaseResource to RepresentationModel or get rid of BaseResources and use RepresentationModels instead.
For example, you couild try defining BaseResource as follows:
public class BaseResource extends RepresentationModel<BaseResource>{
// your implementation
}
Then, the bound will be right:
public class BaseAssembler<T extends BaseTransaction, D extends BaseResource>
implements RepresentationModelAssembler<T, D> {
// your implementation
}
With these changes, DirectorRepresentation will extend BaseResource:
public class DirectorRepresentation extends BaseResource {
}
And you can extend BaseAssembler like this:
public class DirectorRepresentationAssembler
extends BaseAssembler<Director, DirectorRepresentation>
implements RepresentationModelAssembler<Director, DirectorRepresentation> {
// your implementation
}
In my opinion, the code you published in your repository is mostly fine. I think the only problem is in this line of code, as I mentioned before, I think you need to provide the type parameter when defining your BaseResource class. For instance:
package com.hateos.test.entity.web.rest.resource;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModelProperty;
import org.joda.time.DateTime;
import org.springframework.hateoas.RepresentationModel;
import java.util.UUID;
public class BaseResource extends RepresentationModel<BaseResource> {
#JsonProperty
#ApiModelProperty(position = 1, required = true)
public UUID id;
#JsonProperty
public DateTime creationTime;
#JsonProperty
public DateTime lastUpdatedTime;
}
Please, note the inclusion of the code fragment RepresentationModel<BaseResource> after the extends keyword.
I am not sure if it will work but at least with this change every compiles fine and it seems to work properly.

Assign custom lombok builder method as singular handler

I have the following table class which makes usage of #Builder Lombok annotation with a custom builder class:
#Builder
public class MyTable {
...
public static class MyTableBuilder {
public void entry(final MyEntry myEntry) {
...
}
}
}
In another class that is composed by MyTable I would like to make usage of #Builder + #Singular annotations so that I can build TableOwner instances specifying entry by entry.
#Builder
public class TableOwner {
#Singular("entry")
private final MyTable entries;
}
TableOwner.builder()
.entry(...)
.entry(...)
.entry(...)
.build()
However the #Singular annotation on entries results the error "Lombok does not know how to create the singular-form builder methods for type 'MyTable'; they won't be generated.".
Is there a way I can point to MyTableBuilder#entry method as the singular handler for entries?

Mapstruct Invoking implicitly other mapper with multiple parameter

Given the following classes and a mapper that takes mulitple source arguments
(I use lombok to keep source as short as possible.)
#Getter
#Setter
public class MySourceOne {
private String src1;
}
#Getter
#Setter
public class MySourceTwo {
private String src2;
}
#Getter
#Setter
public class MyTargetObject {
private String prop1;
private String prop2;
}
#Mapper
public interface MyTargetObjectMapper {
#Mapping(target="prop1", source="a")
#Mapping(target="prop2", source="b")
public MyTargetObject mapMyObject(String a, String b);
}
#Getter
#Setter
public class MyComplexTargetObject {
private MyTargetObject myTargetObject;
}
I am trying to create a mapper for MyComplexTargetObject that will invoke implicitly the MyTargetObjectMapper .
But the "source" won't allow to map multiple parameter like this
#Mapper(uses= {MyTargetObjectMapper.class})
public interface MyComplexTargetObjectMapper {
#Mapping(target="myTargetObject", source="one.src1, two.src2")
public MyComplexTargetObject convert(MySourceOne one, MySourceTwo two);
}
So I am trying to use an expression="..." instead of source, but nothing works so far.
Any thoughts a clean way to do this without calling the MyTargetObjectMapper in a concrete method?
MapStruct does not support selection of methods with multiple sources.
However: you can do target nesting to do this.
#Mapper
public interface MyComplexTargetObjectMapper {
#Mapping(target="myTargetObject.prop1", source="one.src1" )
#Mapping(target="myTargetObject.prop2", source="two.src2")
public MyComplexTargetObject convert(MySourceOne one, MySourceTwo two);
}
And let MapStruct take care of generating the mapper. Note: you can still use a MyComplexTargetObjectMapper to do single source to target to achieve this.

override #XmlElement annotation with FIELD accessor type

I have a class with #XmlAccessorType(XmlAccessType.FIELD) annotation, and each private and protected field is annotated with #XmlElement(name='...').
The challenge: I may want to rename one of the xml element names in a later stage. This leads me to the question. Is there a way to override/redefine these annotations, if I create a sub-class ?
I believe that some implementations of JaxB allow for XML configuration to override the annotations. In this case this may actually be possible. Here is an article from Eclipslink explaining how this can be done http://www.eclipse.org/eclipselink/documentation/2.4/solutions/jpatoxml004.htm
In my opinion you can just build an XML configuration for the JaxB file you want to override.
I tried first with the #XmlAccessorType(XmlAccessType.FIELD) and to hide with #XmlTransient. This only works, if you mark the field in the superclass and in the child class with #XmlTransient. But I assume, this is not what you want.
As second approach I've tried with more restrictive #XmlAccessorType(XmlAccessType.PROPERTY) in the superclass and #XmlAccessorType(XmlAccessType.NONE) in the child class. See here my example:
package com.so.example;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/myresource")
public class MyResource {
#GET
#Path("/car")
#Produces(MediaType.APPLICATION_XML)
public Car getCar() {
Car car = new Car();
car.setWheels(4);
return car;
}
#GET
#Path("/suv")
#Produces(MediaType.APPLICATION_XML)
public Suv getSuv() {
Suv suv = new Suv();
List<String> bigWheels = new ArrayList<>();
bigWheels.add("left front wheel");
bigWheels.add("right front wheel");
bigWheels.add("left rear wheel");
bigWheels.add("right rear wheel");
suv.setBigWheels(bigWheels);
return suv;
}
}
Class Car:
package com.so.example;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlRootElement
public class Car {
protected Integer wheels;
public Car() {
}
#XmlElement(name = "wheels", nillable = true)
public Integer getWheels() {
return wheels;
}
public void setWheels(Integer wheels) {
this.wheels = wheels;
}
}
Class Suv (Child):
package com.so.example;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
public class Suv extends Car {
#XmlTransient
private Integer wheels;
private List<String> bigWheels;
public Suv() {
}
#Override
#XmlTransient
public Integer getWheels() {
return wheels;
}
#Override
public void setWheels(Integer wheels) {
this.wheels = wheels;
}
#XmlElement
public List<String> getBigWheels() {
return bigWheels;
}
public void setBigWheels(List<String> bigWheels) {
this.bigWheels = bigWheels;
}
}
One way to "hide" the element wheels of the superclass would be to mark it as "nillable=true" and not use primitive types. In this case, the field wheels will be marshalled to <wheels xsi:nil="true"/>
If it's possible for you to not use the parent class for marshalling and you are only using child classes, you could use the approach described here:
http://blog.bdoughan.com/2011/06/ignoring-inheritance-with-xmltransient.html
Also you could use moxy and specify a custom binding:
http://www.eclipse.org/eclipselink/documentation/2.4/moxy/runtime003.htm
Whilst in java, to my knowledge, overriding an annotation #XmlElement(name='...') to change the name property is not possible; you can create a global variable in your code and either pass it through your classes or your functions following the #XmlElement(name='...').
In the code below I created a single class but it contains the setter and getter methods required if you want to pass it through to another class
#XMLAccessorType(XMLAccessType.FIELD)
public class YourClass {
#XmlTransient
private String string = ""; //This can be replaced with whatever variable you are manipulating
//That could be an int or a file or anything really
#XmlElement(name = "your_name")
private void doSomething() {
String temp = getString(); //This variable is normally used to pass between different
//classes but may as well use it if you have one
//Your code which manipulates the String
setString(temp); //This variable is normally used to pass between different classes but
//may as well use it if you have one
}
#XmlElement(name = "your_other_name")
private void doSomethingElse() {
String temp = getString();
//Your code which manipulates the String
setString(temp);
}
public void getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
}
I would reccomend looking at the Java Docs for #XmlTransient and these two other relevant SO questions.
How to override JAXB #XMLAccessorType(XMLAccessType.FIELD) specified at a Class level with #XMLElement on a getter method for a property?
Jaxb - Overriding the XMLElement name attribute

Generics and the Play Framework

I am using the Play Framework in it's current version and my model classes extend play.db.jpa.JPABase.
Today I tried to make an often used type of query generic and define a static helper method to construct it.
I wrote the following:
import play.db.jpa.Model;
import play.libs.F;
public class GenericQueries {
public static <T extends Model> F.Option<T> firstOption(
Class<T> clazz,
String query,
Object... parameters){
final T queryResult = T.find(query,parameters).first();
return (queryResult == null) ?
F.Option.<T>None() :
F.Option.Some(queryResult);
}
}
However, I get the following error:
Execution exception
UnsupportedOperationException occured : Please annotate your JPA model with #javax.persistence.Entity annotation.
I debugged into the method, at runtime T seems to be correctly set to it's corresponding Model class. I even see the annotation.
I suspect some class enhancing voodoo by the play guys responsible for this, but I am not entirely sure.
Any ideas?
Update: added Model class as Requested
Here is a shortened Version of one of the Model classes I use.
package models;
import org.apache.commons.lang.builder.ToStringBuilder;
import play.data.validation.Required;
import play.db.jpa.Model;
import play.modules.search.Field;
import play.modules.search.Indexed;
import javax.persistence.Column;
import javax.persistence.Entity;
import java.util.Date;
#Entity #Indexed
public class FooUser extends Model {
#Required
public Date firstLogin;
#Field
#Required(message = "needs a username")
#Column(unique = false,updatable = true)
public String name;
#Field
public String description;
#Required
public boolean isAdmin;
#Override
public String toString(){
return new ToStringBuilder(this)
.append("name", name)
.append("admin", isAdmin)
.toString();
}
}
In Play entites should extend play.db.jpa.Model and use #Entity annotation (class level).
For what you say I understand that you are extending play.db.jpa.JPABase.
This may be the reason of the issue, as Play (as you point) dynamically enhances classes and it may be clashing with your inheritance.
EDIT: I tested the issue
The problem is that Play is not enhancing the object T. This means that the find method called id the one of GenericModel (parent of Model) whose implementation is to throw an exception with the message.
The enhancer seems to detect only the classes with #Entity.
Even the solution of mericano1 doesn't work, the enhancer doesn't pick it. So I feel you won't be able to use that method in Play.
The best way to do that is to use a base class that extends play.db.jpa.Model with just the static methods that will be shared by the subclasses.
Add the #Entity annotation to the base class and no class fields.
import play.db.jpa.Model;
import play.libs.F;
public class BaseModel extends Model {
public static <T extends Model> F.Option<T> firstOption(
Class<T> clazz,
String query,
Object... parameters){
final T queryResult = T.find(query,parameters).first();
return (queryResult == null) ?
F.Option.<T>None() :
F.Option.Some(queryResult);
}
}
And then
#Entity
public class FooUser extends BaseModel {
....
}

Categories

Resources