7.7. Bean Mapping (Dozer)¶
Table of Contents
7.7.1. Overview¶
Bean mapping is a process to copy the field values of one Bean to another Bean.
AccountForm
object of application layer is converted to Account
object of domain layer.AccountForm
object is Bean mapped to Account
object and Account
object is used in the domain layer.Code examples with/without using Dozer are given below.
Example of a complex process that results in the program readability deterioration
User user = userService.findById(userId); XxxOutput output = new XxxOutput(); output.setUserId(user.getUserId()); output.setFirstName(user.getFirstName()); output.setLastName(user.getLastName()); output.setTitle(user.getTitle()); output.setBirthDay(user.getBirthDay()); output.setGender(user.getGender()); output.setStatus(user.getStatus());
Example when Dozer is used
User user = userService.findById(userId); XxxOutput output = beanMapper.map(user, XxxOutput.class);
How to use Dozer is explained below.
Warning
Classes added by JSR-310 Date and Time API cannot be used in default state due to generation of exceptions, and hence a custom converter must be created.
7.7.2. How to use¶
Dozer is a mapping function library of JavaBean. Values are copied recursively (nested structure) from conversion source Bean to conversion destination Bean.
7.7.2.1. Bean definition for using Dozer¶
Create an instance of org.dozer.Mapper
when Dozer is to be used independently as follows.
Mapper mapper = new DozerBeanMapper();
Creating instance of Mapper each time deteriorates efficiency,
hence org.dozer.spring.DozerBeanMapperFactoryBean
provided by Dozer should be used.
Define org.dozer.spring.DozerBeanMapperFactoryBean
, a Factory class that creates Mapper in Bean definition file (applicationContext.xml).
<bean class="org.dozer.spring.DozerBeanMapperFactoryBean">
<property name="mappingFiles"
value="classpath*:/META-INF/dozer/**/*-mapping.xml" /><!-- (1) -->
</bean>
Sr. No. | Description |
---|---|
(1)
|
Specify mapping definition XML files in mappingFiles.
org.dozer.spring.DozerBeanMapperFactoryBean maintains org.dozer.Mapper as an interface. Therefore, specify Mapper for @Inject .In this example, all the (any value)-mapping.xmls in any folder of /META-INF/dozer under the class path
are read. The contents of these XML files are explained later.
|
Mapper
should be injected in the class to perform Bean mapping.
@Inject
Mapper beanMapper;
7.7.2.2. Mapping when the field name and the type between Beans is same¶
Dozer can perform mapping by default without creating mapping definition XML file if the field name between the Beans is same.
Bean definition of conversion source
public class Source {
private int id;
private String name;
// omitted setter/getter
}
Bean definition of conversion destination
public class Destination {
private int id;
private String name;
// omitted setter/getter
}
Perform Bean mapping using map
method of Mapper
as given below.
After executing the method given below, a new Destination object is created and each field value of source is copied to the created Destination object.
Source source = new Source();
source.setId(1);
source.setName("SourceName");
Destination destination = beanMapper.map(source, Destination.class); // (1)
System.out.println(destination.getId());
System.out.println(destination.getName());
Sr. No. | Description |
---|---|
(1)
|
Pass the object to be copied as the first argument and the Bean class where it is to be copied as the second argument.
|
The output of the above code is as given below. The value of object to be copied is set in the created object.
1
SourceName
To copy the field of source
object to already existing destination
object, perform the following
Source source = new Source();
source.setId(1);
source.setName("SourceName");
Destination destination = new Destination();
destination.setId(2);
destination.setName("DestinationName");
beanMapper.map(source, destination); // (1)
System.out.println(destination.getId());
System.out.println(destination.getName());
Sr. No. | Description |
---|---|
(1)
|
Pass the object to be copied as the first argument and the object where it is to be copied as the second argument.
|
The output of the above code is as given below. The value of object to be copied is reflected in the destination where it is to be copied.
1
SourceName
Note
The value that does not exist in Source
class does not change before and after copying to the field of Destination
class.
Bean definition of conversion source
public class Source { private int id; private String name; // omitted setter/getter }
Bean definition of conversion destination
public class Destination { private int id; private String name; private String title; // omitted setter/getter }
Example of Mapping
Source source = new Source(); source.setId(1); source.setName("SourceName"); Destination destination = new Destination(); destination.setId(2); destination.setName("DestinationName"); destination.setTitle("DestinationTitle"); beanMapper.map(source, destination); System.out.println(destination.getId()); System.out.println(destination.getName()); System.out.println(destination.getTitle());
The output of the above code is as given below. Since there is no title
field in the Source
class,
the value of title
field of Destination
object remains the same as the field value before copying.
1 SourceName DestinationTitle
7.7.2.3. Mapping when the field name is the same and type between Beans is different¶
When field type of Bean is different in copy source and copy destination, mapping of the type where type conversion is supported can be done automatically.
The conversion given below is an example where it is possible to convert without a mapping definition XML file.
Example: String -> BigDecimal
Bean definition of conversion source
public class Source {
private String amount;
// omitted setter/getter
}
Bean definition of conversion destination
public class Destination {
private BigDecimal amount;
// omitted setter/getter
}
Example of Mapping
Source source = new Source();
source.setAmount("123.45");
Destination destination = beanMapper.map(source, Destination.class);
System.out.println(destination.getAmount());
The output of the above code is as given below. The value can be copied even when the type is different.
123.45
Refer to Manual for the supported type conversions.
7.7.2.4. Mapping when the field name between Beans is different¶
When field name of copy source is different from the copy destination, creating mapping definition XML file and defining the field for Bean mapping enables conversion.
Bean definition of conversion source
public class Source {
private int id;
private String name;
// omitted setter/getter
}
Bean definition of conversion destination
public class Destination {
private int destinationId;
private String destinationName;
// omitted setter/getter
}
To define Bean definition for using Dozer, create mapping definition XML file called (any value)-mapping.xml in the src/main/resources/META-INF/dozer folder.
<?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>
<class-a>com.xx.xx.Source</class-a><!-- (1) -->
<class-b>com.xx.xx.Destination</class-b><!-- (2) -->
<field>
<a>id</a><!-- (3) -->
<b>destinationId</b><!-- (4) -->
</field>
<field>
<a>name</a>
<b>destinationName</b>
</field>
</mapping>
</mappings>
Sr. No. | Description |
---|---|
(1)
|
Specify fully qualified class name (FQCN) of copy source Bean in
<class-a> tag. |
(2)
|
Specify fully qualified class name (FQCN) of copy destination Bean in
<class-b> tag. |
(3)
|
Specify field name for mapping of copy source Bean in
<a> tag of <field> tag. |
(4)
|
Specify field name for mapping of copy destination Bean corresponding to (3) in
<b> tag of <field> tag. |
Example of Mapping
Source source = new Source();
source.setId(1);
source.setName("SourceName");
Destination destination = beanMapper.map(source, Destination.class); // (1)
System.out.println(destination.getDestinationId());
System.out.println(destination.getDestinationName());
Sr. No. | Description |
---|---|
(1)
|
Pass object to be copied as the first argument and the Bean class where it is to be copied as the second argument. (no difference with basic mapping.)
|
The output of the above code is as given below.
1
SourceName
Mapping definition XML file existing under META-INF/dozer of class path is read in mappingFiles
property in accordance with the setting of Bean definition for using Dozer.
File name should be (any value)-mapping.xml.
The settings are applied if mapping between Source
class and Destination
class is defined in any file.
Note
It is recommended to create a mapping definition XML file in each Controller and name the file as -mapping.xml (value obtained by removing Controller from Controller name). For example, mapping definition XML file of TodoController is created in src/main/resources/META-INF/dozer/todo-mapping.xml.
7.7.2.5. One-way/Two-way mapping¶
The mapping defined in mapping XML is two-way mapping by default.
In the above example, mapping is done from Source
object to Destination
object however,
mapping can also be done from Destination
object to Source
object.
To specify only one-way mapping, set "one-way"
in the type
attribute of <mapping>
tag in the mapping field definition.
<?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">
<!-- omitted -->
<mapping type="one-way">
<class-a>com.xx.xx.Source</class-a>
<class-b>com.xx.xx.Destination</class-b>
<field>
<a>id</a>
<b>destinationId</b>
</field>
<field>
<a>name</a>
<b>destinationName</b>
</field>
</mapping>
<!-- omitted -->
</mappings>
Bean definition of conversion source
public class Source {
private int id;
private String name;
// omitted setter/getter
}
Bean definition of conversion destination
public class Destination {
private int destinationId;
private String destinationName;
// omitted setter/getter
}
Example of Mapping
Source source = new Source();
source.setId(1);
source.setName("SourceName");
Destination destination = beanMapper.map(source, Destination.class);
System.out.println(destination.getDestinationId());
System.out.println(destination.getDestinationName());
The output of the above code is as given below.
1
SourceName
If one-way is specified, an error does not occur even if the mapping is done in the reverse direction. The copy process is ignored.
This is because Source
field corresponding to Destination
field does not exist if mapping is not defined.
Destination destination = new Destination();
destination.setDestinationId(2);
destination.setDestinationName("DestinationName");
Source source = new Source();
source.setId(1);
source.setName("SourceName");
beanMapper.map(destination, source);
System.out.println(source.getId());
System.out.println(source.getName());
The output of the above code is as given below.
1
SourceName
7.7.2.6. Mapping of nested fields¶
It is possible to map the field of Bean to be copied to the field with Nested attributes of the Bean where it is to be copied. (In Dozer terminology, it is called Deep Mapping .)
Bean definition of conversion source
public class EmployeeForm {
private int id;
private String name;
private String deptId;
// omitted setter/getter
}
Bean definition of conversion destination
public class Employee {
private Integer id;
private String name;
private Department department;
// omitted setter/getter
}
public class Department {
private String deptId;
// omitted setter/getter and other fields
}
Example: Define as below to map the deptId
with EmployeeForm
object to deptId
of Department
with Employee
object.
<?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">
<!-- omitted -->
<mapping map-empty-string="false" map-null="false">
<class-a>com.xx.aa.EmployeeForm</class-a>
<class-b>com.xx.bb.Employee</class-b>
<field>
<a>deptId</a>
<b>department.deptId</b><!-- (1) -->
</field>
</mapping>
<!-- omitted -->
</mappings>
Sr. No. | Description |
---|---|
(1)
|
Specify the field of
Employee object for deptId of Employee form. |
Example of Mapping
EmployeeForm source = new EmployeeForm();
source.setId(1);
source.setName("John");
source.setDeptId("D01");
Employee destination = beanMapper.map(source, Employee.class);
System.out.println(destination.getId());
System.out.println(destination.getName());
System.out.println(destination.getDepartment().getDeptId());
The output of the above code is as given below.
1
John
D01
In the above case, a new instance of Employee
, a class of conversion destination is created.
The newly created Department
instance is set in the department
field of Employee
and
deptId
of the EmployeeForm
is copied.
When the Department
object is already set in the department
field of Employee
,
a new instance is not created but the deptId
of EmployeeForm
is copied
to the deptId
field of the existing Department
object.
EmployeeForm source = new EmployeeForm();
source.setId(1);
source.setName("John");
source.setDeptId("D01");
Employee destination = new Employee();
Department department = new Department();
destination.setDepartment(department);
beanMapper.map(source, destination);
System.out.println(department.getDeptId());
System.out.println(destination.getDepartment() == department);
The output of the above code is as given below.
D01
true
7.7.2.7. Collection mapping¶
Dozer supports two-way auto-mapping of the following Collection types. When field name is same, mapping definition XML file is not required.
java.util.List
<=>java.util.List
java.util.List
<=> Array- Array <=> Array
java.util.Set
<=>java.util.Set
java.util.Set
<=> Arrayjava.util.Set
<=>java.util.List
Bean mapping with a collection of the following classes is considered.
package com.example.dozer;
public class Email {
private String email;
public Email() {
}
public Email(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return email;
}
// generated by Eclipse
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((email == null) ? 0 : email.hashCode());
return result;
}
// generated by Eclipse
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Email other = (Email) obj;
if (email == null) {
if (other.email != null)
return false;
} else if (!email.equals(other.email))
return false;
return true;
}
}
Conversion source Bean
package com.example.dozer;
import java.util.List;
public class AccountForm {
private List<Email> emails;
public void setEmails(List<Email> emails) {
this.emails = emails;
}
public List<Email> getEmails() {
return emails;
}
}
Conversion destination Bean
package com.example.dozer;
import java.util.List;
public class Account {
private List<Email> emails;
public void setEmails(List<Email> emails) {
this.emails = emails;
}
public List<Email> getEmails() {
return emails;
}
}
Example of Mapping
AccountForm accountForm = new AccountForm();
List<Email> emailsSrc = new ArrayList<Email>();
emailsSrc.add(new Email("a@example.com"));
emailsSrc.add(new Email("b@example.com"));
emailsSrc.add(new Email("c@example.com"));
accountForm.setEmails(emailsSrc);
Account account = beanMapper.map(accountForm, Account.class);
System.out.println(account.getEmails());
The output of the above code is as given below.
[a@example.com, b@example.com, c@example.com]
There is no particular change in the explanation given so far.
As shown in the example below, it is necessary to exercise caution when the element is already added to the Collection field of copy destination Bean.
AccountForm accountForm = new AccountForm();
Account account = new Account();
List<Email> emailsSrc = new ArrayList<Email>();
List<Email> emailsDest = new ArrayList<Email>();
emailsSrc.add(new Email("a@example.com"));
emailsSrc.add(new Email("b@example.com"));
emailsSrc.add(new Email("c@example.com"));
emailsDest.add(new Email("a@example.com"));
emailsDest.add(new Email("d@example.com"));
emailsDest.add(new Email("e@example.com"));
accountForm.setEmails(emailsSrc);
account.setEmails(emailsDest);
beanMapper.map(accountForm, account);
System.out.println(account.getEmails());
The output of the above code is as given below.
[a@example.com, d@example.com, e@example.com, a@example.com, b@example.com, c@example.com]
All the elements of Collection of copy source Bean are added to the Collection of copy destination Bean.
Two Email
objects with a@exmample.com
are “Equal”, but are added easily.
(“Equal” here signifies true
when compared by Email.equals
and has the same value as Email.hashCode
.)
The above behavior is called as cumulative in Dozer terminology and is a default behavior at the time of Collection mapping.
This behavior can be changed in mapping definition XML 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">
<!-- omitted -->
<mapping>
<class-a>com.example.dozer.AccountForm</class-a>
<class-b>com.example.dozer.Account</class-b>
<field relationship-type="non-cumulative"><!-- (1) -->
<a>emails</a>
<b>emails</b>
</field>
</mapping>
<!-- omitted -->
</mappings>
Sr. No. | Description |
---|---|
(1)
|
Specify
non-cumulative in relationship-type attribute of <field> tag. Default value is cumulative .To specify
non-cumulative in all the fields of Bean to be mapped, specify non-cumulative in the relationship-type attribute of <mapping> tag. |
The output of the above code is as given below based on this setting.
[a@example.com, d@example.com, e@example.com, b@example.com, c@example.com]
Duplication of equivalent objects is eliminated.
Note
It is necessary to exercise caution for updating conversion source object with conversion destination object.
In the above example, a@exmample.com
of AccountForm
is stored in the copy destination.
It can be implemented using the mapping definition XML file settings even to remove the fields existing only in the copy destination collection.
<?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">
<!-- omitted -->
<mapping>
<class-a>com.example.dozer.AccountForm</class-a>
<class-b>com.example.dozer.Account</class-b>
<field relationship-type="non-cumulative" remove-orphans="true" ><!-- (1) -->
<a>emails</a>
<b>emails</b>
</field>
</mapping>
<!-- omitted -->
</mappings>
Sr. No. | Description |
---|---|
(1)
|
Set
true in the remove-orphans attribute of <field> tag. Default value is false . |
The output of the above code is as given below based on this setting.
[a@example.com, b@example.com, c@example.com]
Only the objects to be copied are reflected in the collection where they are to be copied.
The same result is obtained even with the settings given below.
<?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">
<!-- omitted -->
<mapping>
<class-a>com.example.dozer.AccountForm</class-a>
<class-b>com.example.dozer.Account</class-b>
<field copy-by-reference="true"><!-- (1) -->
<a>emails</a>
<b>emails</b>
</field>
</mapping>
<!-- omitted -->
</mappings>
Sr. No. | Description |
---|---|
(1)
|
Set
true in the copy-by-reference attribute of <field> tag. Default value is false . |
The behavior explained so far is given in the figure.
Default behavior (cumulative)
non-cumulative
non-cumulative and remove-orphans=true
Note
The difference in “non-cumulative and remove-orphans=true” pattern and “copy-by-reference” pattern is whether the container of Collection after Bean conversion is copy destination or copy source.
In case of “non-cumulative and remove-orphans=true” pattern, the container of Collection after Bean conversion is copy destination whereas in case of “copy-by-reference” , the container is copy source. It is explained in the figure given below.
non-cumulative and remove-orphans=true
copy-by-reference
It is necessary to exercise caution when the copy destination has one-to many and many-to-many relationship in the entity of JPA (Hibernate). An unexpected issue may occur when copy destination entity is under EntityManager control. For example, an SQL of DELETE ALL + INSERT ALL may be issued when the container of collection is changed and an SQL to UPDATE (DELETE or INSERT when the number of elements differ) the changes may be issued when copied with “non-cumulative and remove-orphans=true”. Any pattern can be used depending on the requirements.
Warning
When the Bean to be mapped has String
collection, a bug wherein the behavior is not as expected is generated.
StringListSrc src = new StringListSrc; StringListDest dest = new StringListDest(); List<String> stringsSrc = new ArrayList<String>(); List<String> stringsDest = new ArrayList<String>(); stringsSrc.add("a"); stringsSrc.add("b"); stringsSrc.add("c"); stringsDest.add("a"); stringsDest.add("d"); stringsDest.add("e"); src.setStrings(stringsSrc); dest.setStrings(stringsDest); beanMapper.map(src, dest); System.out.println(dest.getStrings());
If code given above is executed in the non-cumulative and remove-orphans=true settings, following is expected to be output.
[a, b, c]
However, following is output.
[b, c]
and duplicate String is removed.
When the code is executed in the copy-by-reference=”true” settings, following is output.
[a, b, c]
Tip
In Dozer, the mapping can be performed even between the lists which do not use Generics. At this time, data type of the object included in conversion source and conversion destination can be specified as HINT. Refer to Dozer Official Manual -Collection and Array Mapping(Using Hints for Collection Mapping)- for details.
Todo
It is checked that the mapping between Beans that uses Collection<T> fails.
Example :
public class ListNestedBean<T> { private List<T> nest; // omitted other declarations }Execution result :
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
7.7.3. How to extend¶
7.7.3.1. Creating custom convertor¶
- Example :
java.lang.String
<=>org.joda.time.DateTime
org.dozer.CustomConverter
provided by Dozer.- Global Configuration
- Class level
- Field level
Global Configuration is recommended to perform conversion using the same logic in the entire application.
It is convenient to inherit org.dozer.DozerConverter
to implement a custom converter.
package com.example.yourproject.common.bean.converter;
import org.dozer.DozerConverter;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.util.StringUtils;
public class StringToJodaDateTimeConverter extends
DozerConverter<String, DateTime> { // (1)
public StringToJodaDateTimeConverter() {
super(String.class, DateTime.class); // (2)
}
@Override
public DateTime convertTo(String source, DateTime destination) {// (3)
if (!StringUtils.hasLength(source)) {
return null;
}
DateTimeFormatter formatter = DateTimeFormat
.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime dt = formatter.parseDateTime(source);
return dt;
}
@Override
public String convertFrom(DateTime source, String destination) {// (4)
if (source == null) {
return null;
}
return source.toString("yyyy-MM-dd HH:mm:ss");
}
}
Sr. No. | Description |
---|---|
(1)
|
Inherit
org.dozer.DozerConverter . |
(2)
|
Set 2 target classes in the constructor.
|
(3)
|
Describe the logic to convert from
String to DateTime . In this example, Locale is used by default. |
(4)
|
Describe the logic to convert from
DateTime to String . In this example, Locale is used by default. |
The created custom converter should be defined for mapping.
dozer-configration-mapping.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">
<configuration>
<custom-converters><!-- (1) -->
<!-- these are always bi-directional -->
<converter
type="com.example.yourproject.common.bean.converter.StringToJodaDateTimeConverter"><!-- (2) -->
<class-a>java.lang.String</class-a><!-- (3) -->
<class-b>org.joda.time.DateTime</class-b><!-- (4) -->
</converter>
</custom-converters>
</configuration>
<!-- omitted -->
</mappings>
Sr. No. | Description |
---|---|
(1)
|
Define
custom-converters to which all the custom converters belong. |
(2)
|
Define converter for performing individual conversion. Specify fully qualified class name (FQCN) of implementation class in the converter type.
|
(3)
|
Fully qualified class name (FQCN) of conversion source Bean
|
(4)
|
Fully qualified class name (FQCN) of conversion destination Bean
|
When java.lang.String
<=> org.joda.time.DateTime
conversion needs to be performed in the entire application by using the mapping given above, the mapping is performed by calling custom converter instead of standard mapping.
Example :
Bean definition of conversion source
public class Source {
private int id;
private String date;
// omitted setter/getter
}
Bean definition of conversion destination
public class Destination {
private int id;
private DateTime date;
// omitted setter/getter
}
Mapping (two-way)
Source source = new Source();
source.setId(1);
source.setDate("2012-08-10 23:12:12");
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime dt = formatter.parseDateTime(source.getDate());
// Source to Destination Bean Mapping (String to org.joda.time.DateTime)
Destination destination = dozerBeanMapper.map(source, Destination.class);
assertThat(destination.getId(), is(1));
assertThat(destination.getDate(),is(dt));
// Destination to Source Bean Mapping (org.joda.time.DateTime to String)
dozerBeanMapper.map(destination, source);
assertThat(source.getId(), is(1));
assertThat(source.getDate(),is("2012-08-10 23:12:12"));
Refer to Dozer Official Manual -Custom Converters- for the details of custom converter.
Note
The conversion from String
to the standard date/time object such as java.utl.Date
is explained in “Mapping from string to date/time object”.
7.7.4. Appendix¶
The options that can be specified in the mapping definition XML file are explained.
All options can be confirmed in Dozer Official Manual -Custom Mappings Via Dozer XML Files-.
7.7.4.1. Field exclusion settings (field-exclude)¶
The fields that are not to be copied can be excluded at the time of Bean conversion.
Bean conversion is as given below.
Bean definition of conversion source
public class Source {
private int id;
private String name;
private String title;
// omitted setter/getter
}
Bean definition of copy destination
public class Destination {
private int id;
private String name;
private String title;
// omitted setter/getter
}
Define as follows to exclude any field of copy source Bean from mapping.
Carry out the settings of field exclusion in the mapping definition XML file as given below.
<?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">
<!-- omitted -->
<mapping>
<class-a>com.xx.xx.Source</class-a>
<class-b>com.xx.xx.Destination</class-b>
<field-exclude><!-- (1) -->
<a>title</a>
<b>title</b>
</field-exclude>
</mapping>
<!-- omitted -->
</mappings>
Sr. No. | Description |
---|---|
(1)
|
Set the field you want to exclude in the <field-exclude> element. In this example, if map method is executed after specification, the title value of destination is not overwritten while copying Destination object from Source object.
|
Source source = new Source();
source.setId(1);
source.setName("SourceName");
source.setTitle("SourceTitle");
Destination destination = new Destination();
destination.setId(2);
destination.setName("DestinationName");
destination.setTitle("DestinationTitle");
beanMapper.map(source, destination);
System.out.println(destination.getId());
System.out.println(destination.getName());
System.out.println(destination.getTitle());
The output of the above code is as given below.
1
SourceName
DestinationTitle
The title value of destination after mapping is same as in the previous state.
7.7.4.2. Specifying mapping (map-id)¶
The mapping indicated in Field exclusion settings (field-exclude) is applied while performing Bean conversion in the entire application. To specify the applicable range of mapping, define map-id as given below.
<?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">
<!-- omitted -->
<mapping map-id="mapidTitleFieldExclude">
<class-a>com.xx.xx.Source</class-a>
<class-b>com.xx.xx.Destination</class-b>
<field-exclude>
<a>title</a>
<b>title</b>
</field-exclude>
</mapping>
<!-- omitted -->
</mappings>
When the above settings are carried out, title can be excluded from copying by passing map-id (mapidTitleFieldExclude) to map
method.
When map-id is not specified, all the fields are copied without applying the settings.
The example of passing map-id to map
method is shown below.
Source source = new Source();
source.setId(1);
source.setName("SourceName");
source.setTitle("SourceTitle");
Destination destination1 = new Destination();
destination1.setId(2);
destination1.setName("DestinationName");
destination1.setTitle("DestinationTitle");
beanMapper.map(source, destination1); // (1)
System.out.println(destination1.getId());
System.out.println(destination1.getName());
System.out.println(destination1.getTitle());
Destination destination2 = new Destination();
destination2.setId(2);
destination2.setName("DestinationName");
destination2.setTitle("DestinationTitle");
beanMapper.map(source, destination2, "mapidTitleFieldExclude"); // (2)
System.out.println(destination2.getId());
System.out.println(destination2.getName());
System.out.println(destination2.getTitle());
Sr. No. | Description |
---|---|
(1)
|
Normal mapping.
|
(2)
|
Pass map-id as the third argument and apply the specific mapping rules.
|
The output of the above code is as given below.
1
SourceName
SourceTitle
1
SourceName
DestinationTitle
Tip
map-id can be specified not only by mapping items but also by defining the field. Refer to Dozer Official Manual -Context Based Mapping- for details.
Note
The same form object can be used for both creating new/updating the existing Web applications.
Form object is copied (mapped) to domain object, however a field may exist where the object is not to be copied depending on the operations.
In this case, use <field-exclude>
.
- Example: userId is included in the New form whereas it is not included in the Update form.
In this case, null is set in the userId at the time of update if the same form object is used. If form object is copied as it is by fetching copy destination object from DB, userId of copy destination becomes null. In order to avoid this, provide map-id for update and carry out the settings to exclude field of userId at the time of update.
7.7.4.3. Settings to exclude null/empty field of copy source (map-null, map-empty)¶
null
or empty field of copy source Bean can be excluded from mapping.
Set in the mapping definition XML file as given below.
<?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">
<!-- omitted -->
<mapping map-null="false" map-empty-string="false"><!-- (1) -->
<class-a>com.xx.xx.Source</class-a>
<class-b>com.xx.xx.Destination</class-b>
</mapping>
<!-- omitted -->
</mappings>
Sr. No. | Description |
---|---|
(1)
|
To exclude the
null field of copy source Bean from mapping, set false in the map-null attribute. Default value is true .To exclude the empty field of copy source Bean from mapping, set
false in the map-empty-string attribute. Default value is true . |
Bean definition of conversion source
public class Source {
private int id;
private String name;
private String title;
// omitted setter/getter
}
Bean definition of conversion destination
public class Destination {
private int id;
private String name;
private String title;
// omitted setter/getter
}
Mapping example
Source source = new Source();
source.setId(1);
source.setName(null);
source.setTitle("");
Destination destination = new Destination();
destination.setId(2);
destination.setName("DestinationName");
destination.setTitle("DestinationTitle");
beanMapper.map(source, destination);
System.out.println(destination.getId());
System.out.println(destination.getName());
System.out.println(destination.getTitle());
The output of the above code is as given below.
1
DestinationName
DestinationTitle
name
and title
field of copy source Bean are null
or empty, and are excluded from mapping.
7.7.4.4. Mapping from string to date/time object¶
The copy source string field can be mapped to the copy destination date/time field.
Following 6 types of conversions are supported.
Date/time
java.lang.String
<=>java.util.Date
java.lang.String
<=>java.util.Calendar
java.lang.String
<=>java.util.GregorianCalendar
java.lang.String
<=>java.sql.Timestamp
Date only
java.lang.String
<=>java.sql.Date
Time only
java.lang.String
<=>java.sql.Time
java.util.Date
is explained.java.util.Calendar
,java.util.GregorianCalendar
,java.sql.Timestamp
can also be performed by the same method.<?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">
<!-- omitted -->
<mapping>
<class-a>com.xx.xx.Source</class-a>
<class-b>com.xx.xx.Destination</class-b>
<field>
<a date-format="yyyy-MM-dd HH:mm:ss:SS">date</a><!-- (1) -->
<b>date</b>
</field>
</mapping>
<!-- omitted -->
</mappings>
Sr. No. | Description |
---|---|
(1)
|
Specify field name and date format of copy source.
|
Bean definition of conversion source
public class Source {
private String date;
// omitted setter/getter
}
Bean definition of conversion destination
public class Destination {
private Date date;
// omitted setter/getter
}
Mapping
Source source = new Source();
source.setDate("2013-10-10 11:11:11.111");
Destination destination = beanMapper.map(source, Destination.class);
assert(destination.getDate().equals(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse("2013-10-10 11:11:11.111")));
<?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">
<!-- omitted -->
<configuration>
<date-format>yyyy-MM-dd HH:mm:ss.SSS</date-format>
<!-- omitted other configuration -->
</configuration>
<!-- omitted -->
</mappings>
Refer to the Dozer Official Manual -Global Configuration- for the details of items that can be configured.
7.7.4.5. Mapping error¶
If mapping process fails during mapping, org.dozer.MappingException
(runtime exception) is thrown.
The typical examples of MappingException
being thrown are given below.
- map-id that does not exist in the
map
method is passed. - map-id existing in
map
method is passed however, the source/target type passed to map process differs from the definition specified in map-id. - When conversion is not supported by Dozer and custom converter for conversion does not exist as well.
Since these are normal program bugs, the sections called by map
method should be modified appropriately.