Map Entity to the correct subclass - java

I have the following classes Entity, BaseClass, SubClass1, SubClass2.
public class Entity {
private String field1;
private String field2;
private String type;
private String field3;
private String field4;
}
public class BaseClass {
private String field1;
private String field2;
private String type;
}
public class SubClass1 extends Base {
private String field3;
}
public class SubClass2 extends Base {
private String field4;
}
Entity is obtained from by call an API. Depending on the value of the type property of the BaseClass, I want to map the Entity to the corresponding subclass.
One option I can think of mapping is below,
#Mapper
public interface EntityMapper {
SubClass1 mapEntityToSubClass1(Entity entity);
SubClass2 mapEntityToSubClass2(Entity entity);
}
I will invoke either of the methods mapEntityToSubClass1 or mapEntityToSubClass2 conditionally based on the type in my business logic. Since I will be getting a List from the external API, looping through, checking the type in Entity doesn't seem good to me. In my business logic there are currently 4 subclasses and in future there can be more and the logic becomes more clumsy.
Is there a better way of implementing the same in mapstruct?

Not sure if I understand correctly, but you just want to pick which subclass to map to based on a field in the source? Don't do that in the business logic, simply use a default method. Something like this:
#Mapper
public interface EntityMapper {
default BaseClass map(Entity entity) {
if(entity.getType().equals("a"))
return mapEntityToSubClass1(entity);
if(entity.getType().equals("b"))
return mapEntityToSubClass2(entity);
throw new RuntimeException("Unsupported base type");
}
Sub1 mapEntityToSubClass1(Entity entity);
Sub2 mapEntityToSubClass2(Entity entity);
}

Related

Type-safe way to reuse defensive copy method given immutable class hierarchy

I have a base class and a derived class, both designed to be immutable (ctor and getters omitted):
public class PageQuery<T> {
private final T queryFilter;
private PageCond pageCond; // pagination parameter
// E withPageCond(PageCond newPageCond) {
// return a brand new copy of `this` with `pageCond` replaced with `newPageCond`
// that can be reused across class hierarchy
// }
}
public class PageSortQuery<T> extends PageQuery<T>{
private final SortCond sortCond; // sorting parameter
}
How to add a method that returns defensive copy of this to the base class so that all classes in this hierarchy can benefit from it? Is there a way to accomplish it without clone()?
I don't know if you mean something like this, in this case if you call the withFilter method the origin would not become changed.
#AllArgsConstructor
public class Query <T> {
#Getter
private final String name;
#Getter
private final Predicate<T> filter;
public Query<T> withFilter(Predicate<T> filter){
return new DelegatingQuery<T>(this){
#Override
public Predicate<T> getFilter() {
return Query.this.filter;
}
};
}
static class DelegatingQuery<T> extends Query<T>{
#Delegate
private final Query<T> query;
public DelegatingQuery(Query<T> query) {
super(query.name,query.filter);
this.query = query;
}
}
}

How do you send back a subset of a JPA entity that is owned by another entity?

I have an entity that owns another entity:
//psuedocode
public class ClassA{
private String name;
#OneToOne
private ClassB classb;
}
public class ClassB{
private String thing1;
private String thing2;
private String thing3;
}
When I retrieve ClassA objects, I don't want to see ClassB.thing3, but I do want to see thing1 and thing 2:
{
"name":"classa",
"classb":{
"thing1":"hi",
"thing2":"there"
}
}
But if I query for ClassB I want to see everything:
{"thing1":"hi",
"thing2":"there",
"thing3":"joseph"}
So I can't just put an ignore annotation over thing3, because then I'll ignore it on the second fetch. I tried a Converter<ClassB>, but that forces me to implement toString() and fromString() for the JSON, which dies on converting the JSON object to Java-side (the converter expects a String, but gets the object instead).
I want to avoid building/parsing the JSON object myself if possible to let my json provider do the work, if possible. I'm on Johnzon.
This is possible, you need to use #NamedEntityGraph,
This should help, http://www.thoughts-on-java.org/jpa-21-entity-graph-part-1-named-entity/
Something like this should be possible by querying using SELECT NEW, but you're going to need some new Classes for that ... and won't be passing your entities directly to JSON.
new Classes:
(pseudocode)
class ResultB {
String thing1;
String thing2;
public ResultB(ClassB classB) {
this.thing1 = classB.thing1;
this.thing2 = classB.thing2;
}
}
class ResultA {
String name;
ResultB resultB;
public ResultA(ClassA classA) {
this.name=classA.name;
this.resultB=new ResultB(classA);
}
}
Query:
select new ResultA(a) from ClassA a fetch join a.classB;
Then you can pass ResultA instead of ClassA to JSON.
PS: As mentioned in the comment above, I don't think NamedEntityGraphs are the way to go here
I would always fetch all the data from the database and let the filtering to be done by the JSON provider if you want to serialize it anyway. If you use Jackson you can simply add views to your fields:
public class ClassA {
#JsonView(Views.AlwaysInclude.class)
private String name;
#JsonView(Views.AlwaysInclude.class)
#OneToOne
private ClassB classb;
}
public class ClassB {
#JsonView(Views.AlwaysInclude.class)
private String thing1;
#JsonView(Views.AlwaysInclude.class)
private String thing2;
private String thing3;
}
public class Views {
public static class AlwaysInclude {
}
}
Later when you serialize your object you just need to use your view:
String result = mapper
.writerWithView(Views.AlwaysInclude.class)
.writeValueAsString(new ClassA());
When you want to serialize only ClassB then you shouldn't use views.
String result = mapper.writeValueAsString(new ClassB());

Mapping abstract class in dozer

I have the following class structure (it actually is a VO layer with Hibernate mappings):
public abstract class abstractClassVO {
private int id;
private String name;
}
public class concreteClassAVO extends abstractClassVO {
private String aAttribute;
}
public class concreteClassBVO extends abstractClassVO {
private Long bAttribute;
}
And the equivalent DTO objects:
public abstract class abstractClassDTO {
private int id;
private String name;
}
public class concreteClassADTO extends abstractClassDTO {
private String aAttribute;
}
public class concreteClassBDTO extends abstractClassDTO {
private Long bAttribute;
}
Then I have another object like this:
public class compositeObject {
private int anAttribute;
private abstractClassVO myInstance;
}
and its equivalent:
public class compositeObjectDTO{
private int anAttribute;
private abstractClassDTO myInstance;
}
How can I tell dozer to automatically map myInstance to the specific DTO that corresponds to the concrete class implementation in the VO layer?
Currently, out of the box, Dozer isn't even putting anything in the myInstance field of the compositeObjectDTO class. My guess is that it's due to the fact that abstractClassDTO it is an abstact class, and since it cannot determine the implementation, it does nothing. I am not getting any exceptions.
Dozer can't do it out of the box but you could write a helper that would determine destination class by source class. You can get this information from DozerBeanMapper.getMappingMetadata().getClassMappings* methods. These methods return list of ClassMappingMetadata that contains destination class. You just only need to chech whether destination class is inherited from abstractClassDTO. This check can be omitted if you only have one mapping for one VO.
For bi-directional mapping you should additionally check ClassMappingMetadata.MappingDirection field.

Conditional JSR-303 validation based on enclosed class type

How can I change validation rules based on which class given bean is enclosed in?
Example:
public class ParentA {
#Valid
private Child c;
}
public class ParentB {
#Valid
private Child c;
}
public class Child {
#NotNull // when in ParentA
#Null // when in ParentB
private String name;
}
There are validation groups, however, I do not know how to apply them in this case. Can I specify the following: if validating ParentA then apply GroupA for its fields, hopefully by some annotation and without instanceof? I really do not want to create two types ChildA and ChildB with different validation annotations. I am building REST service with spring 4. Thanks for any feedback.
Try this code
public class ParentA {
#Valid
#ConvertGroup(from=Default.class, to=ParentA.class)
private Child c;
}
public class ParentB {
#Valid
#ConvertGroup(from=Default.class, to=ParentB.class)
private Child c;
}
public class Child {
#NotNull(groups=ParentA.class)
#Null(groups=ParentB.class)
private String name;
}
You can find more info and other examples in hibernate validator reference guide

How to correctly implement class inheritance, annotated as #QueryResult in java neo4j

I'm using neo4j + spring data. To access the data I'm using interfaces, that extends GraphRepository<E>. For example
public interface EntryRepository extends GraphRepository<Entry> {
#Query("start parent=node({0}), entry=node({1}) "
+ "match parent-[*1..2{removed:false}]->entry "
+ "return distinct entry")
Entry findOne(Long parentId, Long entryId);
}
I'm trying to get data, that differs from my domain models. My custom models looks like that
#QueryResult
public class EntryBean {
#ResultColumn("id")
private Long id;
#ResultColumn("name")
private String name;
#ResultColumn("content")
private String content;
...
//getters and setters
}
#QueryResult
public class BoardBean {
#ResultColumn("id")
private Long id;
#ResultColumn("name")
private String name;
...
//getters and setters
}
Obviously, that it will be better to separate duplicate fields to Base class and inherit from it. So, i'm doing next steps
#QueryResult
public class BaseBean {
#ResultColumn("id")
private Long id;
#ResultColumn("name")
private String name;
...
}
#QueryResult
public class EntryBean extends BaseBean{
#ResultColumn("content")
private String content;
...
//getters and setters
}
And I don't need BoardBean anymore. But when I'm trying run query
public interface EntryRepository extends GraphRepository<Entry> {
#Query("start user=node({0}), board=node({1}) "
+ "... "
+ "return id(entry) as id, entry.name as name, entry.content as content")
List<EntryBean> getRelatedEntries(Long userId, Long boardId);
}
I get filled by data just fields that directly declared into EntryBean class (i. e. "content" field).
So, How I can correctly implement the #QueryResult class hierarcy?
This is a bug which has been present for almost two years (even in 2.3.5.RELEASE!) in the class in charge of converting annotated POJOs.
Indeed, it calls getDeclaredFields on the most concrete type thus skipping possibly inherited annotated fields.
Before the issue is fixed, my piece of advice would be to tolerate this superficial field duplication on your side and not relying on inheritance for now.

Categories