Introduction
As you know, you can create a copy of a Java Bean using a cloning or copy constructor. There are situations where you need to manipulate your source object to another form of destination object with the same data. It is not about a shallow or deep copy or the clone of an object. Basically you do something with a Java Bean from which you want the data of that bean to be exported to another Java Bean. In this article I will provide you an outline of the good framework called "Dozer" through which you can easily manipulate your Java Bean without writing manual Java code. It is done in a loosely coupled manner.
Technicalities
This article will provide you with the usage of the "Dozer" framework to copy a source bean contents to a destination bean. Before we start, let us think about a situation in which you have a source bean that contains many fields and the source bean belongs to a different project or module. Now you want to expose the bean to the outside world as a part of your web service or REST service development. It is not advisable to do it with your original data model. There may be the following reasons.
- The source system does not allow doing it because of security breach.
- The source bean is not serialized and a final class.
- The source bean has many fields; some of them are not required.
- The source bean is very heavy and contains many nested beans.
- The source bean has fields of various types that may not be required for the other system.
The preceding may be some of other specific reasons. Think about a situation in which you want to make a REST call or web service call to get the minimal account details of a person. But the source system has a bean called "AccountBean" that contains many sensitive information like person's internet banking password, profile password, pan numer or social security number, total balance and so on. You want to develop an application where you want to expose only an account's address details, name and home branch of the bank. This is a required situation where you want to have your custom defined bean that should be exposed to the outside based upon the account number. In this case you need to copy the original bean to your custom defined bean.
You can do that in the following ways.
- Write code to manually copy the contents of the source bean to destination bean.
- Use the Java Reflection or Introspection utility to copy from the source to the destination bean.
- Use an available framework that does the copy automatically.
In this case we will learn about a framework that helps us to copy the contents of a source bean to the destination bean. The framework called "Dozer" is very popular and flexible to use and integrate in the application. You will find more documentation on Dozer in various internet sites. In this article I will provide a very basic and practical usage of Dozer.
Let's quickly learn the usage of Dozer in the following scenarios.
- Copy source Plain/Flat bean to destination Plain/Flat bean
- Copy collections
- Bi-directional bean copy
- Copy flat bean to nested bean and vice versa
- Copy bean with custom conversion.
Copy source Plain/Flat bean to destination Plain/Flat bean
This is the simplest case where we have a source POJO and we want to copy it into a destination POJO. Let us consider the following Java code.
The following is the structure of the source POJO.
package com.ddlab.rnd.type1.srcpkg;
/**
* The Class Person is used as a source bean for Person that contains Address
* object.
*
* @author <a href="mailto:[email protected]">Debadatta Mishra</a>
* @since 2013
*/
public class Person {
/** The name. */
private String name;
/** The age. */
private int age;
/** The adrs. */
private Address adrs;
// All getter and setter method below
}
The following is the structure of the destination POJO.
package com.ddlab.rnd.type1.destnpkg;
/**
* The Class Person1 is used as destination bean for Person1 that contains
* Addrss1 object
*
* @author <a href="mailto:[email protected]">Debadatta Mishra</a>
* @since 2013
*/
public class Person1 {
/** The name1. */
private String name1;
/** The age1. */
private int age1;
/** The adrs1. */
private Address1 adrs1;
//All getter setter method below
}
Let us see the dozer mapping file called "dozerMapping.xml".
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/beanmapping.xsd">
<mapping map-id="a">
<class-a>com.ddlab.rnd.type1.srcpkg.Person</class-a>
<class-b>com.ddlab.rnd.type1.destnpkg.Person1</class-b>
<field>
<a>name</a>
<b>name1</b>
</field>
<field>
<a>age</a>
<b>age1</b>
</field>
<field>
<a>adrs.doorNo</a>
<b>adrs1.doorNo1</b>
</field>
<field>
<a>adrs.stName</a>
<b>adrs1.stName1</b>
</field>
</mapping>
</mappings>
The preceding XML configuration file looks very intuitive since <class-a > refers to the source bean and <class-b> refers to the destination bean. The other field <a> refers to the property of the source bean and <b> refers to the property of the destination bean. Let us see the final code that does the bean mapping.
List<String> list = new ArrayList<String>();
// Add the mapping configuration
list.add("dozerMapping.xml");
// Add to DozerMapper
Mapper mapper = new DozerBeanMapper(list);
mapper.map(p, p1, "a");
Now it is done, it looks very simple and provides many powerful features. You must remember the following few things for copying the bean using the "Dozer" framework.
-
Source bean
-
Destination bean
-
Dozer mapping file configuration
-
The Mapping configuration should contain the source POJO class with the proper package name
-
The Mapping configuration should contain the destination POJO class with the proper package name
-
The Mapping configuration should have the proper exact property name that you have defined in the Java class
The mapping configuration provides the following benefits.
-
You can change the property name as and when required and it does not require building the application.
-
You can add the property in the Java class as and when required.
-
You can have one or many property files for specific requirements.
-
You can edit and remove the mapping as and when required.
-
You can also configure the mapping in the Spring application, in other words the Spring configuration file.
Copy collections
There are certain occasions where you want to copy a list of source types to a list of destination types. The list may contain various types of objects. The following is typical Java code:
package com.ddlab.dozer.type2.srcpkg;
import java.util.List;
/**
* The Class AddressList is used as a source List object that contains object
* of type Address.
*
* @author <a href="mailto:[email protected]">Debadatta Mishra</a>
* @since 2013
*/
public class AddressList {
/** The adrs list. */
private List<Address> adrsList;
/**
* Gets the adrs list.
*
* @return the adrs list
*/
public List<Address> getAdrsList() {
return adrsList;
}
/**
* Sets the adrs list.
*
* @param adrsList
* the new adrs list
*/
public void setAdrsList(List<Address> adrsList) {
this.adrsList = adrsList;
}
}
In the preceding case the list contains a list of Address type objects. Let us learn how to achieve.
First of all create a mapping between Address objects from source to destination and then create a mapping between the source list to the destination list. The following is the mapping configuration file.
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/beanmapping.xsd">
<mapping map-id="k">
<class-a>com.ddlab.dozer.type2.srcpkg.Address</class-a>
<class-b>com.ddlab.dozer.type2.destnpkg.AddressBean</class-b>
<field>
<a>name</a>
<b>name</b>
</field>
<field >
<a>id</a>
<b>id</b>
</field>
</mapping>
<mapping map-id="q1" type="one-way" relationship-type="non-cumulative">
<class-a>com.ddlab.dozer.type2.srcpkg.AddressList</class-a>
<class-b>com.ddlab.dozer.type2.destnpkg.AddressBeanList</class-b>
<field map-id="k">
<a>adrsList</a>
<b>adrsList</b>
</field>
</mapping>
</mappings>
To copy the list object, you can use the same code that is in the first case.
Bi-directional bean copy
In this case you need to have a mapping that does a bean copy in both directions. Let us think about a situation where you want to convert the internal Java Bean to a custom bean to expose a web service and finally you receive the data in the custom bean and convert it into an internal bean. It is not advisable to write another mapping. "Dozer" provides attributes in the XML mapping configuration to copy in both directions based upon the requirements. I provide the attribute required to be specified in the dozer configuration.
<mapping map-id="k" type="bi-directional">
<!-- Other field mapping -->
</mapping>
The preceding attribute type (RED and BOLD) provides the flexibility of copying objects in both directions.
Copy flat bean to nested bean and vice versa
Based upon the project requirements, you may have a complex bean that contains nested other Java Beans. You may need to copy the complex bean to a simple type or vice versa. Let us see the mapping configuration.
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/beanmapping.xsd">
<mapping map-id="a">
<class-a>com.ddlab.dozer.type4.srcpkg.MyPerson</class-a>
<class-b>com.ddlab.dozer.type4.destnpkg.Person1</class-b>
<!-- Other field info -->
<field>
<a>doorNo</a>
<b>adrs1.doorNo1</b>
</field>
<field>
<a>stName</a>
<b>adrs1.stName1</b>
</field>
<field>
<a>cname</a>
<b>adrs1.country.name</b>
</field>
<field>
<a>ccode</a>
<b>adrs1.country.code</b>
</field>
<field>
<a>fd</a>
<b>adrs1.country.bd</b>
</field>
</mapping>
</mappings>
In the preceding case mark the line in red colour. To have a hands-on example, refer to the following mapping configuration file.
flat2NestedBeanMapping.xml
nestedBeanMapping2Flat.xml
Refer to the following packages for this.
com.ddlab.dozer.type4.destnpkg
com.ddlab.dozer.type4.srcpkg
You can download the complete Eclipse project from the following link.
https://www.dropbox.com/s/j6ep7wq7lalfe2k/dozerbeancopy.zip
Copy bean with custom conversion
This is a very typical and a complex condition that occurs during the time of development. Imagine that you have a Java Bean that contains specific fields like date in the JodaTime API, the object is of type UUID, the field of type BigInteger and so on and you have a Java Bean with all primitive type fields. In this case you need to use custom converters and inject into the "Dozer" mapping configuration file so that "Dozer" will automatically copy the Java Bean with the proper field types.
The following is the structure of the mapping file.
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/beanmapping.xsd">
<configuration>
<custom-converters>
<converter type="com.ddlab.dozer.type5.converters.Date2JodaDateConverter">
<class-a>org.joda.time.LocalDate</class-a>
<class-b>java.util.Date</class-b>
</converter>
</custom-converters>
</configuration>
<mapping map-id="k" type="bi-directional">
<class-a>com.ddlab.dozer.type5.srcpkg.SrcBean</class-a>
<class-b>com.ddlab.dozer.type5.destnpkg.DestnBean</class-b>
<!-- For UUID -->
<field copy-by-reference="true">
<a>idKey</a>
<b>uid</b>
</field>
<!-- For java.util.Date to org.joda.time.LocalDate -->
<field>
<a>utilDate</a>
<b>jodaDate</b>
</field>
<!-- For double to java.math.BigDecimal -->
<field>
<a>amount</a>
<b>bigDecimal</b>
</field>
</mapping>
</mappings>
Dozer provides an interface called "org.dozer.CustomConverter" that enables to do custom conversion based upon our requirements. I provide the following small code snippet.
package com.ddlab.dozer.type5.converters;
import org.dozer.CustomConverter;
import org.dozer.MappingException;
import org.joda.time.LocalDate;
/**
* The Class Date2JodaDateConverter is used as a Dozer custom converter for
* converting java.util.Date to Jodatime LocalDate.
*
* @author <a href="mailto:[email protected]">Debadatta Mishra</a>
* @since 2013
*/
public class Date2JodaDateConverter implements CustomConverter {
/*
* (non-Javadoc)
*
* @see org.dozer.CustomConverter#convert(java.lang.Object,
* java.lang.Object, java.lang.Class, java.lang.Class)
*/
@Override
public Object convert(Object existingDestinationFieldValue,
Object sourceFieldValue,
@SuppressWarnings("rawtypes") Class destinationClass,
@SuppressWarnings("rawtypes") Class sourceClass) {
if (sourceFieldValue == null) {
return null;
}
if (sourceFieldValue instanceof java.util.Date) {
java.util.Date utilDate = (java.util.Date) sourceFieldValue;
LocalDate localDate = new LocalDate(utilDate.getTime());
return localDate;
}
throw new MappingException("Misconfigured/unsupported mapping");
}
}
To learn more about that refer to the following packages.
com.ddlab.dozer.type5.converters
com.ddlab.dozer.type5.destnpkg
com.ddlab.dozer.type5.srcpkg
Configuration
To work with the "Dozer" framework, you need to use the following JAR files in your classpath.
commons-beanutils-1.7.jar, commons-lang-2.4.jar, commons-logging-1.1.1.jar, commons-logging-api-1.1.1.jar, dozer-5.3.2.jar, joda-time-2.2.jar, log4j-1.2.16.jar,log4j-over-slf4j-1.6.1.jar, slf4j-api-1.6.1.jar, slf4j-jdk14-1.6.1.jar, slf4j-simple-1.6.1.jar
You need to download the "Dozer" framework from the following link.
http://dozer.sourceforge.net/
Dozer also provides an Eclipse plugin to ease the work of mapping.
If you are using a Maven structure then you need to use the following dependency in you pom.xml file.
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.4.0</version>
</dependency>
Related Packages and Configuration File
Plain bean to Plain Bean
com.ddlab.rnd.type1.destnpkg (from src)
com.ddlab.rnd.type1.srcpkg (from src)
com.ddlab.rnd.type1 (from test)
dozerMapping.xml (Mapping Configuration)
Collections Copy
com.ddlab.dozer.type2.destnpkg (from src)
com.ddlab.dozer.type2.srcpkg (from src)
com.ddlab.dozer.type2 (from test)
list2ListMapping.xml (Mapping Configuration)
Bi-directional Bean copy
com.ddlab.dozer.type3 (from test)
dozerBidirectionalMapping1.xml (Mapping Configuration)
Flat to Nested Bean/Nested Bean to Flat Bean
com.ddlab.dozer.type4.destnpkg (from src)
com.ddlab.dozer.type4.srcpkg (from src)
com.ddlab.dozer.type4 (from test)
flat2NestedBeanMapping.xml (Mapping Configuration)
nestedBeanMapping2Flat.xml (Mapping Configuration)
Custom Converter
com.ddlab.dozer.type5.converters (from src)
com.ddlab.dozer.type5.destnpkg (from src)
com.ddlab.dozer.type5.srcpkg (from src)
com.ddlab.dozer.type5 (from test)
dozercustomconvertermapping.xml
Download
You can download the complete Eclipse project from the following dropbox site.
https://www.dropbox.com/s/j6ep7wq7lalfe2k/dozerbeancopy.zip
You can easily configure the Eclipse IDE easily by importing the project.
Conclusion
I hope you have enjoyed my small article about the usage of the Dozer framework for copying beans in Java. Download the complete project and go through the source code to understand the concept and its usage. For any kind of issues or errors you can contact me at [email protected].
Resources and References
There are other frameworks also available to provide bean manipulation in a more or less way. I found the "Dozer" framework suitable in various grounds. There is also a similar kind of framework called "Nomin" that provides mapping similar to that of "Dozer" by providing configuration in a properties file. I provide the following links for your references.
http://dozer.sourceforge.net/documentation/gettingstarted.html
http://nomin.sourceforge.net/
http://modelmapper.org/
http://code.google.com/p/orika/
http://morph.sourceforge.net/
https://code.google.com/p/omapper/