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?
Related
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
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);
}
I have the following object:
#Data
#AllArgsConstructor
#NoArgsConstructor
public class MyComplexObject implements Serializable {
private static final long serialVersionUID = 1L;
private OuputObject ouput;
}
#Data
#AllArgsConstructor
#NoArgsConstructor
public class OuputObject implements Serializable {
private static final long serialVersionUID = 1L;
private InputObject input;
}
#Data
#AllArgsConstructor
#NoArgsConstructor
public class InputObject implements Serializable {
private static final long serialVersionUID = 1L;
private List<String> example;
}
When instantiating the object, the object is not initialized "OuputObject" and is always NULL. Why is the OuputObject object and the InputObject object not instantiated or initialized?
When I do a: getInputObject () I get a NullPointerException
(This is an example, it's fake data)
#Data creates getters and setter, #AllArgsConstructor creates constructor for all the fields, and #NoArgsConstructor creates default constructor. To have your object initialised, you need to use constructor, e.g.
MyComplexObject mco = new MyComplexObject(new InputObject());
Lombok doesn't initialize properties.
The annotation #Data only generates getters and setters for the class properties and overrides the toString, hashCode and equals method.
#Data
public class MyClass {
private String myString;
}
Generates the following code:
public class MyClass {
private String myString;
public String getMyString() {
return myString;
}
public void setMyString(String myString) {
this.myString = myString;
}
// equals, hashcode, toString
}
Have a look at the documentation
I am using Parceler for FragmentArgs with RealmObjects (and Lombok) and getting the following build error.
Error:(73, 70) error: Parceler: Unable to find read/write generator for type io.realm.RealmList<backend.model.Invitation> for backend.model.ProjectInfo#invitations
My class ProjectInfo looks like this
#Parcel(implementations = {ProjectInfoRealmProxy.class}, value = Parcel.Serialization.BEAN, analyze = {ProjectInfo.class})
public class ProjectInfo extends RealmObject
{
#Getter #Setter #PrimaryKey long projectId;
#ParcelPropertyConverter(RealmListParcelConverter.class) #Getter #Setter #Ignore RealmList<Invitation> invitations;
#ParcelPropertyConverter(RealmListParcelConverter.class) #Getter #Setter RealmList<Submission> submissions;
}
The Classes Submission and Invitation are both quite simple data structures that also extend RealmObject
The class RealmListParcelConverter looks like this:
public class RealmListParcelConverter implements TypeRangeParcelConverter<RealmList<? extends RealmObject>, RealmList<? extends RealmObject>>
{
private static final int NULL = -1;
#Override
public void toParcel(RealmList<? extends RealmObject> input, Parcel parcel)
{
parcel.writeInt(input == null ? NULL : input.size());
if(input != null)
{
for(RealmObject item : input)
{
parcel.writeParcelable(Parcels.wrap(item), 0);
}
}
}
#Override
public RealmList fromParcel(Parcel parcel)
{
int size = parcel.readInt();
RealmList list = new RealmList();
for(int i = 0; i < size; i++)
{
Parcelable parcelable = parcel.readParcelable(getClass().getClassLoader());
list.add((RealmObject) Parcels.unwrap(parcelable));
}
return list;
}
}
What am i doing wrong?
UPDATE 1
Invitation class
#Parcel(implementations = {InvitationRealmProxy.class}, value = Parcel.Serialization.BEAN, analyze = {Invitation.class})
public class Invitation extends RealmObject
{
#Getter #Setter long accountId;
#Getter #Setter long projectId;
#Getter #Setter boolean declined;
#Getter #Setter String name;
public Invitation() {}
}
And the Submission class
#Parcel(implementations = {SubmissionRealmProxy.class}, value = Parcel.Serialization.BEAN, analyze = {Submission.class})
public class Submission extends RealmObject
{
#Getter #Setter #PrimaryKey long id;
#Getter #Setter long creator;
#Getter #Setter float length;
public Submission() {}
}
UPDATE2
This happens only if i use
#Parcel(implementations = {ProjectInfoRealmProxy.class}, value = Parcel.Serialization.BEAN, analyze = {ProjectInfo.class})
If i use a simple
#Parcel
It crashes during runtime with the crash
org.parceler.ParcelerRuntimeException: Unable to find generated Parcelable class for io.realm.ProjectInfoRealmProxy
UPDATE 3
I am using the parcel within the #Args annotation like this
#Arg(bundler = ParcelerArgsBundler.class) ProjectInfo projectInfo;
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.