Lombok #SuperBuilder hide parent fields - java

I have the following classes
The first level of hierarchy:
abstract class Parent2 {
private P2_Param1 p2_param1;
private P2_Param2 p2_param2;
protected Parent2(P1_Param1 p1_param1) {
p2_param1 = p1_param1.something()
p2_param2 = p1_param1.somethingElse()
}
}
Second level:
abstract class Parent1 extends Parent2 {
private P1_Param1 p1_param1;
protected Parent1(P1_Param1 p1_param1) {
super(p1_param1);
this.p1_param1 = p1_param1;
}
}
And hundreds of classes that look like this:
class Child extends Parent1 {
private C_Param1 c_param1;
private C_Param2 c_param2;
private C_Param3 c_param3;
private C_Param4 c_param4;
// ... many more parameters here
public Child(P1_Param1 p1_param1) {
super(p1_param1);
}
}
These classes were used like this for a long time - child fields were used only to represent schema.
Now things changed and Child fields need to hold values.
I'd like to create as few changes in ChildX classes as possible and avoid implementing constructors that take all child parameters + p1_param1.
Ideally, it'd be great to use Lombok annotations or add some code in the parent classes.
I was thinking about instantiating the child class as it was before and using toBuilder = true to copy and fill values:
var child = new Child(p1_param1)
.toBuilder()
.c_param1(c_param1)
.build();
I tried to use the #SuperBuilder(toBuilder = true) annotation, but now I have an access to fill fields from parent classes (e.g. p2_param2) and I would like to avoid that.
Is my approach valid? Can I somehow make the parent fields not accessible via public child builder methods?

I found a solution to my question:
The answer is to use the Delombok option and then remove all methods from the generated (#SuperBuilder) parents' builders.

Related

Can I use the Lombok #Builder passing the parent class as parameter?

I want to create a new Child instance passing a Parent and other additional parameters.
For example if I have:
public class Parent {
public String param1;
public String param2;
// many parameters
public String paramN;
}
public class Child extends Parent {
public String subValue;
}
With lombok, is there a builder that lets me create a Child instance passing the Parent and the missing value as parameters?
Would be easier if I could write something like:
Parent p = Parent.builder()
.param1("a")
.param2("b")
// many parameters
.paramN("b")
.build();
Child c = Child.builder(p).subValue("c").build();
Other answers don't truly make your client code simply reuse the parent instance you already have. But this is doable. You have two options:
The hard one is to write your custom annotation that does what you want. You can even make it generic so that it works for any classes the have parent/child hierarchy. Have a look at this example. If you feel brave you can raise a feature request on Lombok's github page.
Option two would be to write your custom builder for the child. See example here. In your custom builder in the init step you would be reading a passed in Parent instance, and setup the inherited fields only.
The regular #Builder is not sufficient here, because you are dealing with a class hierarchy. However, #SuperBuilder was made exactly for such a case.
#SuperBuilder generates complex code loaded with generics. That makes this solution difficult to understand without in-depth knowledge about the code #SuperBuilder generates. You should think about whether this is worth it.
Here's the solution (with Lombok >= 1.18.16):
#SuperBuilder(toBuilder = true)
public static class Parent {
public String param1;
public String param2;
// many parameters
public String paramN;
public abstract static class ParentBuilder<C extends Parent, B extends Parent.ParentBuilder<C, B>> {
protected B $fillValuesFromParent(Parent instance) {
$fillValuesFromInstanceIntoBuilder(instance, this);
return self();
}
}
}
#SuperBuilder(toBuilder = true)
public static class Child extends Parent {
public String subValue;
public static ChildBuilder<?, ?> toBuilder(Parent p) {
return new ChildBuilderImpl().$fillValuesFromParent(p);
}
}
The new toBuilder method on Child creates a new ChildBuilderImpl (which will create a Child instance when calling build()). To fill the values from the given Parent p, it calls the new $fillValuesFromParent method from ParentBuilder. This method further delegates the call to the method $fillValuesFromInstanceIntoBuilder, which is generated by Lombok and performs the actual copying of the field values to the new builder instance.
Also note the $ prefix on the methods. This basically says: I'm an implementation detail; don't use me unless you know what you are doing, I might break on the next Lombok version without further notice.
I would suggest you use #SuperBuilder
#SuperBuilder was introduced as experimental feature in lombok v1.18.2.
The #SuperBuilder annotation produces complex builder APIs for your
classes. In contrast to #Builder, #SuperBuilder also works with fields
from superclasses. However, it only works for types. Most importantly,
it requires that all superclasses also have the #SuperBuilder
annotation.
#Getter
#SuperBuilder
public class Parent {
public String name;
public String value;
}
#Getter
#SuperBuilder
public class Child extends Parent {
public String subValue;
}
Then all you need to do is
Child.builder().name("a").value("b").subValue("c").build();

Add Java class to existing inheritance hierarchy in JPA with Hibernate

I have an abstract base class with existing subclasses that is mostly used for defining a number of common fields and associated methods. I have a separate concrete class that "organically evolved" (i.e., bad design due to unforeseen feature requests) to end up with all the same fields defined in that abstract subclass.
Is there any way of having that separate class extend the abstract class and carry over the data of existing stored instances of that separate class? I would like to use InheritanceType.SINGLE_TABLE, but if another strategy makes it easier or possible, I guess that's fine too.
Also, those are entities referenced in other entities (OneToMany). Is that a problem? Hibernate uses only one global sequence for assigning entity ids - so it should in theory be possible to not break those references even if the data is moved to another table, right?
Already tried a few things, but no luck so far (e.g., add the "extend" to the separate class, hard-code it to use the same table as the base class, manually add a field for the discriminator...).
I am also happy about any pointers to examples/docs on how to carry out class hierarchy changes and other data model changes with JPA/Hibernate without losing data!
So, here's a simplified example of the situation. Base is the abstract base class that already has sub-classes.
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name = "Base")
public abstract class Base {
private long persistenceId;
private String privateField;
#Id
#GeneratedValue
public long getPersistenceId() {
return persistenceId;
}
public void setPersistenceId(long persistenceId) {
this.persistenceId = persistenceId;
}
[...]
}
#Entity
public class SubclassToBe {
private long persistenceId;
private String privateField;
private String someFieldNotInBaseClass;
#Override
#Id
#GeneratedValue
public long getPersistenceId() {
return persistenceId;
}
#Override
public void setPersistenceId(long persistenceId) {
this.persistenceId = persistenceId;
}
[...]
}
The goal would be to have SubclassToBe inherit from Base, removing the definitions of shared fields but keeping the information stored there. And at the same time, not break references to the persistence ids of SubclassToBe objects that are used in other objects as part of OneToMany relations.

Java #entity nested relationships

I have a parent entity that has child entities (A) who in turn have their own child entities (B).
#Entity
public class Parent {
#OneToMany
Set<ChildA> childrenA;
}
#Entity
public class ChildA {
#OneToMany
Set<ChildB> childrenB;
}
I'm trying to display the data via a JSF dataTable. I would like to show the following.
Parent1 | NumberOfRelatedChildrenB
Parent2 | NumberOfRelatedChildrenB
To generate the rows in the dataTable I'm using a MangagedBean which gets a List of the Parents via a ParentFacade.findAll(), but I can't figure out how I can get a List of all the associated ChildBs. I guess I could add a #OneToMany ChildB relationship to the Parent entity, but I was hoping there would be a way to get them via the ChildA relationship?
Thanks in advance & sorry for the poor explanation!
No, I suggest to avoid creating an additional relationship in this case. One way is to create a method in the managed bean that returns the number of related ChildB given an input Parent:
#ManagedBean
public class MyManagedBean {
private List<Parent> parentList;//+getter
private Map<Long, Long> relatedChildrenB = new HashMap<Long,Long>();//+getter
#EJB
private ParentFacade parentFacade;
#PostConstruct
public void init() {
parentList = parentFacade.findAll();
for (Parent parent : parentList) {
relatedChildrenB.put(parent.getId(), parentFacade.getNumberOfRelatedChildrenB(parent));
}
}
and in the facelets page:
<h:dataTable value="#{myManagedBean.parentList}" var="parent">
...
#{myManagedBean.relatedChildrenB[parent.id]}
</h:dataTable>
and implement the corresponding queries in the facade service class.
Note that passing an object using parenthesis () in the previous revision in an EL expression requires EL 2.2 and thus either a Servlet 3.0 compatible container or applying some workaround. This solution does not need method call with parameters.
Finally, note that in my final edit I have followed the wise suggestion of skuntsel to avoid db calls in getter methods.
Simple solution would be to put a method in Parent that returns children count and then use it in dataTable column e.g.
#Transient
public int getAllChildrenCount() {
// iterate through children list and count
}
On view:
#{parent.allChildrenCount}

JDO in Google App Engine: How should polymorphic relationships be implemented?

I'm working on GAE-based applications, which uses JDO to access datastore. I need to implement polymorphic relationship between persisted objects.
There's abstract parent class:
#PersistenceCapable
#Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
public abstract class Parent {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
String id;
// ....
And several child classes:
#PersistenceCapable (identityType = IdentityType.APPLICATION)
public class Child extends Parent {
// ....
Also, there's one more class, which should have reference to one of child classes. According to "Polymorphic Relationships" section of "Entity Relationships in JDO" article, the best way to implement such relationship is to store key of an object, so this class looks in the following way:
#PersistenceCapable (identityType = IdentityType.APPLICATION)
public class OtherClass {
#Persistent
private String reference;
// ....
I retrieve string key of referenced object from instance of OtherClass. Then I would like to obtain referenced object itself: it's an instance of one of Parent subclasses. BUT:
If I do it with pm.getObjectById(oid) method:
Object object = pm.getObjectById(reference);
JDOObjectNotFoundException exception is thrown (javax.jdo.JDOObjectNotFoundException: No such object FailedObject:...).
If I do it with getObjectById(class, key) method:
Parent object = pm.getObjectById(Parent.class, reference);
FatalNucleusUserException exception is thrown (org.datanucleus.store.appengine.FatalNucleusUserException: Received a request to find an object of kind Parent but the provided identifier is the String representation of a Key for kind Child)
What is correct way to retrieve instance of one of subclasses referenced in another entity?
UPDATE: I found this thread in GAE google group, but frankly it did not help me a lot.
I found the same problem with JDO and App Engine, so I started a project that implements a workaround for this. https://code.google.com/p/datanucleus-appengine-patch/
My first test with the code I have now looks okay, feel free to try it out at give me some feedback.
Actually my workaround may solve your problem 2 ways.
I implemented a getObjectById(class, id) that also looks for kinds that are instances of the provided class.
I implemented a getObjectById(oid) that does some special handling of lookup if oid is of type com.google.appengine.api.datastore.Key, then it will figure out the correct class to return.
I added a new annotation #PolymorphicRelationship that will make is easy to handle to workaround that App Engine describes, with storing the keys. Sample shown below:
#Persist
public Collection<Key> myChildKeys;
#NotPersistent
#PolymorphicRelationship(keyField ="myChildKeys")
public Collection<TestChild> myChildren;
I'm using this rather cancerous and smelly anti-pattern to get around this limitation of JDO/App Engine.
#JsonIgnore
#Persistent(mappedBy="account")
private List<XProvider> xProviders;
#JsonIgnore
#Persistent(mappedBy="account")
private List<YProvider> yProviders;
// TODO: add extra providers here and in getProviders() below...
And then to get the collection:
public List<XProvider> getXProviders() {
if (xProviders == null) {
xProviders = new ArrayList<XProvider>();
}
return xProviders;
}
//etc with other getters and setters for each collection.
public List<Provider> getProviders() {
List<Provider> allProviders = new ArrayList<Provider>();
// TODO: add extra providers here...
allProviders.addAll(getXProviders());
allProviders.addAll(getYProviders());
return allProviders;
}
It's a bad solution, but any port in a storm...
(Also relates a little to this bug, using interfaces as the collection type http://code.google.com/p/datanucleus-appengine/issues/detail?id=207)
App Engine's JDO layer doesn't currently support polymorphism. In fact, I'm not sure if JDO supports it in general or not.

Polymorphism in JAX-RPC web services

I have a JAX-RPC (Java) web service that needs to return a complex polymorphic value. To be more specific, the class structure is something like this:
abstract class Child {
}
class Question extends Child {
private String name;
// other fields, getters, and setters
}
class Section extends Child {
private String label;
private Child[] children;
// getters and setters
}
class Quiz {
private Child[] elements;
// getter and setter
}
My web service has a method that returns a Quiz, which of course may contain Questions and Sections which may contain Questions and other Sections, and so on and so forth. However, when I generate the WSDL, only Child and Quiz make it in. When I call the web service, I get back a Quiz element with the right number of children, but they're all Child elements, and they're all empty.
Is there a good way to make this work, short of just returning XML as a String?
Before anyone asks, due to circumstances beyond my control, I cannot use JAX-WS.
I don't think JAX-RPC supports polymorphism in that way. I had a similar problem, and had to work around it by creating a class that had just two members - one for each of the two classes that could possibly be returned - and only populating one depending on the type I wanted to return. So in your case:
class Child
{
private Section section;
private Question question;
// Constructor, etc...
}
class Question
{
private String name;
// other fields, getters, and setters
}
class Section
{
private String label;
private Child[] children;
// getters and setters
}
class Quiz
{
private Child[] elements;
// getter and setter
}
Which requires the client to check which member of child is populated, and is horribly ugly, I know.
Maybe someone is still looking for it, it can be done in axis 1.4:
Add next line into your section of axis web service deployment file (wsdd):
<parameter name="emitAllTypesInWSDL" value="true" />
Modify your task in ant build file to include 'extraClasses':
<axis-java2wsdl ... extraClasses="..."></axis-java2wsdl>
In extraClasses mention all classes which will be passed, since axis aren't able to guess which childs you'll be passing/returning as parameters.
Done, now you can pass derivated classes in methods accepts parent classes. Etc:
// server side class A { ...}
class B extends A {...}
class C extends A {...}
// ws
class wsAxis { public void processPolymorphCall(A obj); }
// client side
wsAxis.processPolymorphCall(new C());
// this will work now, as much as returning derivated classes in place of base class.

Categories

Resources