Using Lombok's #SuperBuilder concept on a class extended from a 3rd party library - spring-boot

I have a class hierarchy as below.
Child --> Parent --> SuperParent
Since the Child class has extended the Parent class I have to use Lombok's #SuperBuilder annotation instead of the #Builder. Also, as far as I know, all the superclasses need to have the #SuperBuilder annotation. But in my case, the SuperParent class comes from an external library, where I cannot add the #SuperBuilder annotation. I am getting the below compilation error.
The constructor SuperParent(DocumentUploadedResponseDto.DocumentUploadedResponseDtoBuilder<capture#1-of ?,capture#2-of ?>) is undefined.
Any solution or an alternative for this? Thank you.

It's a bit ugly, but it's possible. You have to insert a helper class into your inheritance chain between Parent and SuperParent; let's call it SuperParentBuilderEnabler. In this class, you have to manually implement all neccessary builder elements. Especially, you have to write all setter methods for the fields from SuperParent.
This will allow the Parent and Child classes to simply use a #SuperBuilder annotation without any further modifications.
I assume that SuperParent has an int superParentField field, just to demonstrate how you can write such a setter method in the builder class. Furthermore, I assume that this field can be set via constructor argument. Here is what you have to do:
public abstract class SuperParentBuilderEnabler extends SuperParent {
public static abstract class SuperParentBuilderEnablerBuilder<C extends SuperParentBuilderEnabler, B extends SuperParentBuilderEnablerBuilder<C, B>> {
private int superParentField;
public B superParentField(int superParentField) {
this.superParentField = superParentField;
return self();
}
protected abstract B self();
public abstract C build();
}
protected SuperParentBuilderEnabler(SuperParentBuilderEnablerBuilder<?, ?> b) {
super(b.superParentField);
}
}
Now let Parent extend SuperParentBuilderEnabler and you're done.

I'm sharing how I applied the accepted answer for RepresentationModel.
abstract class BaseTypeParent<T extends BaseTypeParent<T>>
extends RepresentationModel<T> {
protected abstract static class BaseTypeParentBuilder<
T extends BaseTypeParent<T>,
C extends BaseTypeParent<T>,
B extends BaseTypeParentBuilder<T, C, B>
> {
protected abstract B self();
public abstract C build();
}
protected BaseTypeParent(final BaseTypeParentBuilder<T, ?, ?> b) {
super();
}
}

Related

Lombok's #With annotation on concrete class does not apply on abstract parent's class fields

Using Java spring boot, I'm trying to inherit from a super class and implement Lombok's #With annotation on child classes
However, I am not able to use Lombok's .with(property) methods on the abstract's class fields in order to get a new object of the child's instance.
#Getter
#EqualsAndHashCode
#ToString
#With
#SuperBuilder
public class Child extends Parent {
private float a;
private int b;
public Child(float a,int b, int c) {
super(c);
this.a = a;
this.b = b;
}
#AllArgsConstructor
#Getter
#SuperBuilder
#With
public abstract class Parent {
protected int c;
}
What am I missing?
Lombok can only automatically implement the #Width methods for fields of concrete classes, since part of the implementation is the instantiation of the class. This only works for specific classes. For fields of abstract classes you have to implement the method manually. Did you take that into account?
The following works and can be generated automatically, therefore lombok can use the constructor of the class directly to instantiate:
public Child withA(float a) {
return this.a == a ? this : new Child(a, this.b, this.c);
}
The following does not work because the class is abstract:
public Parent withC(int c) {
return this.c == c ? this : /* WHAT TO DO HERE?!; new Parent(...) IS WRONG */;
}

Storing objects by superclass in MongoDB

Is there some way I can store a collection of objects by a common superclass within a user class? My class structure is basically the following:
public abstract class Project { }
public class FooProject extends Project { }
public class BarProject extends Project { }
public class ApplicationUser {
private Collection<Project> projects;
public Collection<Project> getProjects() { ... }
public void setProjects(...) { ... }
}
public interface ApplicationUserRepository extends MongoRepository<ApplicationUser, Long> { }
If I try to add a FooProject, e.g., to the projects collection and save the user, I start getting errors like org.springframework.data.mapping.MappingException: No property user found on entity class org.example.FooProject to bind constructor parameter to!. I guess that's because there's no setter in the ApplicationUser class to handle FooProject.
I've seen some questions / examples where subclasses are given the same #Collection annotation, but I'm not sure what that really buys me. And in those examples, a separate repository interface was declared for each type. And even then, how would I associate a reference to a FooProject from a different repository with a user? Ideally, I'd have just one repository for ApplicationUser objects, or maybe two, for ApplicationUser and superclass Project. Each project will only ever be associated with one user.

Comparator implementation with compare() methods

Here my class name is MyComparator2 but how can I use it here as a methods or object( am not sure ) MyComparator2() in another class name ComparaterDemo to define customized sorting . Can any one help to to make it clear regarding how one class can be used on another ??Thanks in advance
import java.util.Comparator;
import java.util.TreeSet;
public class ComparaterDemo {
public static void main(String[] args) {
TreeSet<Integer> treeSetCOllection = new TreeSet<Integer>(new MyComparator2());
treeSetCOllection.add(900);
treeSetCOllection.add(10);
treeSetCOllection.add(40);
treeSetCOllection.add(100);
treeSetCOllection.add(350);
System.out.println(treeSetCOllection);
}
}
class MyComparator2 implements Comparator{
public int compare(Object obj1, Object obj2){
Integer objectNumber1 = (Integer)obj1;
Integer objectNumber2 = (Integer)obj2;
if(objectNumber1<objectNumber2) return 1;
else if (objectNumber1>objectNumber2) return -1;
else return 0;
}
}
What's happening here is that you are constructing the TreeSet with the specified Comparator. How this works is, for each element added using the add() method, it will automatically sort the set. See here,
In order to make your class visible to other classes you need to specify its visibility. The three types of visibility in Java (I assume that's what you're using) are:
public
private
protected
They are defined as follows:
Public means that that class is visible to all classes regardless of where the file is located.
Private classes are classes inside of classes. For example, if MyComparator2 was defined inside of the ComparaterDemo class then ComparaterDemo could see it, but no other classes could.
Protected classes function the same as private classes except subclasses can also access them. So if MyComparator2 was defined inside of ComparaterDemo and SubComparaterDemo subclassed ComparaterDemo then SubComparaterDemo could access MyComparator2. In this example, no other classes except for ComparaterDemo and its subclasses could see it.
In your question, MyComparator2 is set to protected by default. If you change the line: "class MyComparator2 implements Comparator" to "public class MyComparator2 implements Comparator" you should have no more problems.
I hope this answers your question

CDI events and generics

I'm trying to send events and do this generically. I mean - create one abstract base DAO class with generic type and fire the event from its method. This should work for all descendants. This works if I define the exact type, but doesn't - if I use generics. What I mean:
AbstractDAO (with generics - doesn't fire the event):
public abstract class AbstractDAO<T extends Persistable> implements Serializable {
#Inject #PostSaveEvent Event<T> postSaveEvent;
public T saveOrUpdate(T object) throws DatabaseException {
T obj = em.merge(object);
postSaveEvent.fire(obj);
}
}
AbstractDAO (no generics, just simple class cast - fires the event):
public abstract class AbstractDAO<T extends Persistable> implements Serializable {
#Inject #PostSaveEvent Event<Polis> postSaveEvent;
public T saveOrUpdate(T object) throws DatabaseException {
T obj = em.merge(object);
postSaveEvent.fire((Polis)obj);
}
}
PolisDAO class, which extends AbstractDAO and defines the generic type:
#Stateless
#Named
#PolisType
public class PolisDAO extends AbstractDAO<Polis> {
// some methods (saveOrUpdate is not overriden!)
}
My observer class:
#Stateless
#Named
public class ProlongationService {
public void attachProlongationToPolisOnSave(#Observes #PostSaveEvent Polis polis) throws DatabaseException {
// ... DO smth with polis object. This is NOT called in the first case and called in the second
}
THis is very strange for me, as "fire()" method for CDI event should define the event type on runtime, not during compilation or deployment... When I debug, I see, that
postSaveEvent.fire(obj);
from the first sample operates exactly with Polis entity. But no event is fired nevertheless...
Upd. I tried the base generic class, but no luck:
#Inject #PostSaveEvent Event<Persistable> postSaveEvent;
Thanks.
This should, in theory, work, however in practice inspecting the type of generic objects at runtime with Java Reflection is, at times, impossible. This is due to type erasure. IIRC the type of the concrete sub class isn't erased, so it should be possible to reconnect this, but I guess the implementation isn't doing this right now.
File this as a bug in the http://issues.jboss.org/browse/WELD issue tracker (if you are using Weld), with the classes you provide as an example and we can try to fix it.
To work around, try injecting the event into the concrete subclass, and passing it as an argument, or using an accessor method, to get it into the abstract super class.

Find persisted object by getters

Suppose I have 2 models
Public class Bus extends Model{
#OneToMany(mappedBy="bus")
private List<Passenger> passengers;
public Bus(passengers){
this.passengers=passengers;
}
public List<Passenger> getPassengers(){
return passengers;
}
}
public Class Passenger extends Model{
#ManyToOne
private Bus bus;
public Passenger(Bus bus){
this.bus=bus;
}
}
Can I use a controller method that finds all the passengers in a bus using the getter. eg:
public static void getPassengers(Long busId){
Bus bus = Bus.findById(busId);
List<Passengers> pList = bus.getPassengers();
}
I tried it in a real play web application but the List size returned is always 0.
Thanks in advance.
Can I use a controller method that finds all the passengers in a bus using the getter?
Yes, you can. But I think the problem isn't using a getter, it is that you have not constructed your models correctly.
There are 3 problems I see in your example:
In your controller action getPassengers(Long busId) you need to change List<Passengers> to List<Passenger> (singular "Passenger")
In your Bus model class, you need to either make passengers public or declare a public setter. The "Play!" way is to just declare fields as public and let the framework generate the getters and setters for you. The only reason you would declare your own getters and setters is if you needed to add some logic.
In your Passenger model class, you need to make bus public.
Here's how I would fix your code:
public class Bus extends Model{
#OneToMany(mappedBy="bus")
public List<Passenger> passengers;
public Bus(passengers){
this.passengers=passengers;
}
// No need for getters or setters, but we'll add a getter for the sake of this question:
public List<Passenger> getPassengers() {
return this.passengers;
}
}
public class Passenger extends Model{
#ManyToOne
public Bus bus;
public Passenger(Bus bus){
this.bus = bus;
}
}
And your controller action:
public static void getPassengers(Long busId){
Bus bus = Bus.findById(busId);
List<Passenger> pList = bus.getPassengers();
render(pList); // or whatever rendering you want to do
}
If this doesn't fix your problem, then you are not loading your data correctly. You should post the code you are running to actually create the records - whether that be loading a YAML file or whatever.

Resources