Exclude different named field in class mapping using Dozer - java

I have a
class A with field "mapEntries" - it is a list,
class B with "entries" - it is a wrapper class around list.
I need to map class A to class B. While mapping I need to exclude the "entries" field.
How can I do this? I have tried both one way and both way exclude but it's not working.
The full(both side) exclude seems to work only when the fields have same name in both the src and dest classes.
One Way exclude:
<field-exclude type="one-way">
<a>mapEntries</a>
<b>entries</b>
</field-exclude>
Both way exclude:
<field-exclude>
<a>mapEntries</a>
<b>entries</b>
</field-exclude>
I have lot of other fields as well in these classes hence using wildcard is not an option.
<mapping map-null="false">
<class-a>ClientClass</class-a>
<class-b>ProductClass</class-b>
<field-exclude>
<a>mapEntries</a>
<b>entries</b>
</field-exclude>
</mapping>
With this mapping, fields don't get excluded. The product class has getter/setter as setMapEntries() and getMapEntries(). This seems to be the cause. The control does reach in getMapEntries(). I cannot change product code.
If I rename Client field as entries, keeping both client and product field same, it works properly and the mapping gets excluded.
Any help would be appreciated.

Related

How to extend non-modifiable model to use with JPA?

What's the best practice to create persistence (say via Spring Boot or just JPA or Hibernate itself) for a data model coming from a non-modifiable dependency? Typical limitations like not being able to override a field or what patterns like Decorator allow and what not slowed my progress down. I tried some things, but I always end up with the result that it would be necessary to either modify the source model (like adding annotations to make it natively compatible -> the fork I don't want) OR write a ton of wrapper code which would replicate the original model too much - but even this isn't working right now:
I tried
Creating a JpaRepository for the original class. Doesn't work, because casting the extended class to its parent class is not working.
Extend the original class with a custom class that gets necessary annotations like #Entity can be used in such a repository. But problems here were
that the original class is missing an #Id annotation, which could be fixed by using a new ID in the extended class, but
the given model also has a non-simple architecture, including lists of other classes that are part of the model itself. So other annotations like #ElementCollection might be necessary, which can't be added because overriding of fields is not possible.
Hiding it with creating a new field with the same name in the new class is not working:
An error like Could not determine type for: java.util.List, at table: yeah_this_one, for columns:[org.hibernate.mapping.Column(objects)] indicates that the original field can't be hidden completely (changed table and column name in new class to verify that).
So of course adding #ElementCollection (which is said to solve that) isn't helping here, too.
#AttributeOverride is also not working to override annotations to set the ID or other settings, only the name and column can be changed.
I'm stuck at this state and am wondering if this is even the right approach at all.
The setup or what I would expect to work from my understanding:
The general idea is based on this Spring Boot REST tutorial, which I tried to expand with a model from a dependency.
Let's assume there is the original model class Model from a dependency that can not be modified. The ModelEntity would be the extended class to act as way to pull the model into Spring persistence.
In the scope of the dependency the original class would be like:
// Given dependency, not modifiable
#Some existing annotation
public class Model extends AnotherClassFromDep {
#more annotations
private IdLikeClassFromDep modelId;
//more complex attribute
#Nullable
private List<RefClassFromDep> objects = new ArrayList<>();
// more attributes, getter, setter etc.
}
In the scope of my program:
In combination with this little additional orm.xml it is possible to annotate the original Model as MappedSuperclass without modifying it (according to https://stackoverflow.com/a/2516951/1844976).
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0">
<mapped-superclass class="package.name.of.original.Model">
</mapped-superclass>
</entity-mappings>
This allows to create a class like this, which extends the original POJO model to add JPA annotations:
#Entity
public class ModelEntity extends Model {
// some #Id attribute is necessary, which should correspond to
// the already existing ID attribute from the original `Model`
// in the best case, but an additional one would work too
private #Id #GeneratedValue Long id;
// Different approaches to solve the List error from above, for
// instance hiding the original attribute
#ElementCollection
private List<RefClassFromDep> objects;
public ModelEntity(){
super();
}
}
At the current state the issues are blocking me from going further. But, altogether I would expect this to work with a JpaRepository:
// of course, creating a JpaRepository with original `Model` wouldn't
// work, because it has no `#Entity`
public interface ModelRepository extends JpaRepository<ModelEntity, IdLikeClassFromDep> {
}
In a way that actually accessing it like that is possible:
#Configuration
public class LoadDatabase {
#Bean
CommandLineRunner initDatabase(ModelRepository modelRepository) {
return args -> {
// depending on the implementation above, either create a
// Model and cast it or directly create a ModelEntity, set
// attriubtes and save it through the JpaRepository
modelRepository.save(model);
};
}
}
Both more abstract and specific code-related ideas and comments would help me. Thanks!
In the old days, Jpa/Hibernate were configured via XML.
You needed to provide persistence.xml for general configuration. In this file, you added <mapping-file> tag pointing to another file orm.xml In this file you configured mapping for your entities (which is done via JPA annotations these days).
See https://vladmihalcea.com/how-to-use-external-xml-mappings-files-outside-of-jar-with-jpa-and-hibernate/
While the methods described above are considered legacy, they are still supported. LocalContainerEntityManagerFactoryBean has method setMappingResources allowing you to point to the orm.xml file. There is some funkiness about search paths and default locations, but it is well documented:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.html#setMappingResources-java.lang.String...-
Note that the third-party class you are configuring this way needs to conform to Java Beans conventions (no-args constructor, getters and setters)

Annotation based hibernate mapping for external classes

Is it possible to provide annotation based hibernate mapping informations for external classes?
I have a model class (for example Credit) in an external package (without any dependencies to hibernate) which I can't / wan't edit and now I wan´t to define mapping informations for this class.
It's no problem to define these mapping informations in a xml file but I am looking for a possibility to define these informations via annotations.
I can extend the model class but then the child class is mapped but i wan't to map the superclass.

Dozer generic mapping from multiple packages (wildcard?)

Can I write converter for classes that exist in multiple packages? For example I have ClassA which exists in com.package.a.1, com.package.a.2 etc. ClassB exists in com.package.b.1, com.package.b.2 etc. Every copy of ClassA and ClassB has the same definition.
I've tried something like this, but no success
<converter type="com.package.MyConverter">
<class-a>com.package.a.*.ClassA</class-a>
<class-b>com.package.b.*.ClassB</class-b>
</converter>
I don't think you can. If you consider it from a code point of view, how would it know what object to turn the class into? However, you can re-use the converter. e.g.
<converter type="com.package.MyConverter">
<class-a>com.package.a.p1.ClassA</class-a>
<class-b>com.package.b.p1.ClassB</class-b>
</converter>
<converter type="com.package.MyConverter">
<class-a>com.package.a.p2.ClassA</class-a>
<class-b>com.package.b.p2.ClassB</class-b>
</converter>
The only other possibility I can think of, is that if the attributes all have the same names, you don't need to specify the converter, or list them in the mapping, Dozer will just automatically convert everything with identical names.
(I could be wrong with what i've said, but this is what i've dealt with in my experience with Dozer)
You could use an Interface for all your Classes you want to be mapped by your custom com.package.MyConverter mapper. So you only have do define the mapping once:
<converter type="com.package.MyConverter">
<class-a>com.package.IMyConverterMappable</class-a>
<class-b>com.package.IMyConverterMappable</class-b>
</converter>

JAXB add grouping element to a list, .NET like

I'm working with a xml file that is generated and used in a .NET application.
I need to deserialize this file in Java and chose to use JAXB.
However, after trying to create the matching class I figured out one major difference.
When having a
#XmlRootElement
public class SomeClass{
List<NestedClass> classes;
}
I get the following structure:
<SomeClass>
...
<NestedClasses>
...
</NestedClasses>
<NestedClasses>
...
</NestedClasses>
</SomeClass>
As oppesed to the required existing structure:
<SomeClass>
...
<NestedClasses>
<NestedClass>
...
</NestedClass>
<NestedClass>
...
</NestedClass>
</NestedClasses>
</SomeClass>
Is there is a way to get my required structure without adding a class that will only contain the list of the nested class?
You will need to have a class that wraps the collection. To get the desired mapping, you can use the #XmlElementWrapper annotation to specify that a grouping element should be used. Then you can use the #XmlElement annotation to specify the name of the element that represents the items in the collection:
#XmlRootElement
public class SomeClass{
#XmlElementWrapper
#XmlElement(name="NestedClass")
List<NestedClass> classes;
}
For More Information
http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html

Dozer deep mapping not working

I am trying to use dozer 4.1 to map between classes. I have a source class that looks like this:
public class initRequest{
protected String id;
protected String[] details
}
I have a destination class that looks like this:
public class initResponse{
protected String id;
protected DetailsObject detObj;
}
public class DetailsObject{
protected List<String> details;
}
So essentially i want the string in the details array to be populated into the List in the Details object.
I have tried a mapping like this:
<mapping wildcard="true" >
<class-a>initRequest</class-a>
<class-b>initResponse</class-b>
<field>
<a is-accessible="true">details</a>
<b is-accessible="true">detObj.details</b>
</field>
</mapping>
But I get this error:
Exception in thread "main" net.sf.dozer.util.mapping.MappingException: java.lang.NoSuchFieldException: detObj.details
at net.sf.dozer.util.mapping.util.MappingUtils.throwMappingException(MappingUtils.java:91)
at net.sf.dozer.util.mapping.propertydescriptor.FieldPropertyDescriptor.<init>(FieldPropertyDescriptor.java:43)
at net.sf.dozer.util.mapping.propertydescriptor.PropertyDescriptorFactory.getPropertyDescriptor(PropertyDescriptorFactory.java:53)
at net.sf.dozer.util.mapping.fieldmap.FieldMap.getDestPropertyDescriptor(FieldMap.java:370)
at net.sf.dozer.util.mapping.fieldmap.FieldMap.getDestFieldType(FieldMap.java:103)
at net.sf.dozer.util.mapping.util.MappingsParser.processMappings(MappingsParser.java:95)
at net.sf.dozer.util.mapping.util.CustomMappingsLoader.load(CustomMappingsLoader.java:77)
at net.sf.dozer.util.mapping.DozerBeanMapper.loadCustomMappings(DozerBeanMapper.java:149)
at net.sf.dozer.util.mapping.DozerBeanMapper.getMappingProcessor(DozerBeanMapper.java:132)
at net.sf.dozer.util.mapping.DozerBeanMapper.map(DozerBeanMapper.java:94)
How can i map this so that it works?
This works for me. I am using 5.2.1 version
<mapping wildcard="true" >
<class-a>initRequest</class-a>
<class-b>initResponse</class-b>
<field>
<a>details</a>
<b is-accessible="true">detObj.details</b>
</field>
</mapping>
Note that "is-accessable" is not required for . Hope it helps
Problem solved...
is-accesible allows an object to be updated regardless of access modifier and presence of getters/setters (essential for objects generated using JAXB)
"dot" notation for deep mapping works to access nested objects
Combining the two is a feature that does not work in Dozer (maybe it does in a newer version)
solution...
modify the xsd such that the deep mapping is not required. This is not my ideal solution but its better than writing a custom converter for every object
In case of JaxB, use can download and use the plugin for generating the setters. Refer to this link for more details, https://jaxb2-commons.dev.java.net/collection-setter-injector/
I would guess that accessors (getter / setter) are missing.
By the way I think that you'll also need to provide an empty constructor for DetailsObject so dozer can instanciate it.
<b is-accessible="true">detObj.details</b>
Should be replaced with
<b is-accessible="true">DetailsObject.details</b>
While it does seem you cannnot use the "is-accessible" and dot notation together another approach is to break your deep mapping into smaller mappings.
We ran into this situation with JAX-WS generated code. You have lists that have no setter methods and in our case were deeply nested. We found our solution by simply breaking the large deep mapping into smaller mappings that "walked" our way to what we wanted. I tried to explain this in my blog here:
http://btarlton.blogspot.com/2014/08/dozer-deep-nestinga-different-approach.html
But the trick is just walking the object tree by smaller mappings and using is-accessible="true" when necessary to access the list with no setter and using "this" as the property name to keep passing the source along.
Hope this helps!
To summarize there are following options for this issue
1) Use JaxB pluggin to enable setters as discussed by Naveen
2) Use is-accessible for such properties
I believe using first approach unnecessarily exposes setters for collections/lists as you can risk of setting them with null.
We decided to enable is-accessible for such fields (and not to entire class) to avoid any side effects.
I have discussed the solution at Dozer Mapping Class level is-accessible

Categories

Resources