Dozer generic mapping from multiple packages (wildcard?) - java

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>

Related

JAXB ordering multiple properties

In my project, I would like to achieve specific order of properties in XML. In java object is represented as:
public class Plan {
private List<Act> act;
private List<Leg> leg;
...
An output object should look similar to this one:
<plan>
<act x="-25000" y="0"/>
<leg mode="car">
...
</leg>
<act x="10000" y="0"/>
<leg mode="car">
...
</leg>
</plan>
Is JAXB able to set up specific order for such case where I need to put items in order:
act.get(0)
leg.get(0)
act.get(1)
leg.get(1)
...
..
.
I know JAXB is able to save specific order of parameters like firstly act, then all legs, using #XmlType (propOrder={"prop1","prop2",..."propN"}) but it is not the case of this project as the 3rd party application which reads this xml's read them in pairs and propOrder would print them one by another.
Ok, I approach the problem from a different side and I solved it... Previously I thought it was a sorting problem - in fact, it is a problem with Java POJO class construction and JAXB annotations.
The solution for that is to use
#XmlElements({
#XmlElement(name="leg", type=Leg.class),
#XmlElement(name="act", type=Act.class)
})
#XmlElementWrapper(name="plan")
public List<Plan> getPlan() {
return plan;
}
and then items must be put one by another.
more details can be found in this answer:
Marshalling a List of objects implementing a common interface, with JaxB

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)

JPA using variable Schema name in annotations

I'm using the Java Persistence API to describe tables from my database that i will manipulate in my code.
However, the schema used is not be the same depending on where my project will be installed. So, when I use the annotations, I would like that the SCHEMA field was a variable, but I can't make it:
#Entity
#Table(name = "TABLE_NAME", schema = schemaVariable, catalog = "")
How can I achieve that?
Is it possible with the persistence.xml file?
No, this is not possible. You can only use compile-time constants (which are all primitives and String) in annotations.
You can use final variables:
public class DatabaseMetadata {
public static final SCHEMA = "MySchema";
}
and then use it in annotation:
#Table(name = "TABLE_NAME", schema = DatabaseMetadata.SCHEMA, catalog = "")
but I think it's not what you wanted.
PS. On the other hand, there can be find examples of using i.e. Spring EL in annotations (see #Value annotation), but this requires custom annotation processor. AFAIK none of JPA providers gives you such ablility.
Putting schema information (like table, column, schema names) in java classes is a bad idea any time IMHO (forcing recompile if you want to deploy elsewhere). You could put that info in orm.xml and just deploy a different orm.xml dependent on your deployment requirement.
As for persistence.xml you would be dependent on your JPA provider having a persistence property that defined the default schema/catalog. I know DataNucleus JPA (what I use) has this, but no idea for Hibernate
If you know that you would be using different schemas, I'd suggest to use 2 mapping files and define
<entity-mappings>
<persistence-unit-metadata>
<persistence-unit-defaults>
<schema>HR</schema>
</persistence-unit-defaults>
</persistence-unit-metadata>
...
</entity-mappings>
In this way you will be able to easily change schemas, without any changes in the application code.

Exclude different named field in class mapping using Dozer

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.

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