- Advance
Sometimes we need to copy the contents of a bean to another. If the source and target are of the same class we can use a copy constructor or an existing util class like org.apache.commons.beanutils.BeanUtils from Apache Commons BeanUtils or org.springframework.beans.BeanUtils from Spring Framework. This last solution is only recommended if you're in a Java application built with this framework.
However in many situations we need to copy some or all the attributes from an object of class A in an object of class B. It's in this case when we need to build a manual mapper using the getter and setter methods or a library like Dozer. It allows you to customize easily the mapping of the attributes of two objects.
- Explanation
We have created a little project in the SVN repository where we have simulated a hierarchy of a value object with a collection of children and their matching business objects with a similar form. In the following lines we are going to explain how Dozer can help us to implement a mapper quickly and configuring it with only a XML file. The mapping classes are very similar in field naming but we have change the name of the identifiers intentionally. We have omitted the equals and hashCode methods to be clearer. Finally, the samples are shown as unit tests but the data bean initialization methods have been omitted.
Value objects
public class ChildVO implements Serializable { private static final long serialVersionUID = -3066509911454147138L; protected Integer persistenceId; protected Double price; protected ParentVO parentVO; public Integer getPersistenceId() { return persistenceId; } public void setPersistenceId(Integer persistenceId) { this.persistenceId = persistenceId; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public ParentVO getParentVO() { return parentVO; } public void setParentVO(ParentVO parentVO) { this.parentVO = parentVO; } /* ... */ }
public class ParentVO implements Serializable { private static final long serialVersionUID = 440303735656145958L; protected Integer persistenceId; protected String name; protected SetchildVOs; public Integer getPersistenceId() { return persistenceId; } public void setPersistenceId(Integer persistenceId) { this.persistenceId = persistenceId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set getChildVOs() { return childVOs; } public void setChildVOs(Set childVOs) { this.childVOs = childVOs; } /* ... */ }
Business objects
public class ChildBO implements Serializable { private static final long serialVersionUID = -3066509911454147138L; protected Integer businessId; protected Double price; protected ParentBO parentBO; public Integer getBusinessId() { return businessId; } public void setBusinessId(Integer businessId) { this.businessId = businessId; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public ParentBO getParentBO() { return parentBO; } public void setParentBO(ParentBO parentBO) { this.parentBO = parentBO; } /* ... */ }
public class ParentBO implements Serializable { private static final long serialVersionUID = 440303735656145958L; protected Integer businessId; protected String name; protected SetchildBOs; public Integer getBusinessId() { return businessId; } public void setBusinessId(Integer businessId) { this.businessId = businessId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set getChildBOs() { return childBOs; } public void setChildBOs(Set childBOs) { this.childBOs = childBOs; } /* ... */ }
- Mapper initialization
protected final DozerBeanMapper mapper; public DozerMappingTest() { ListmyMappingFiles = new ArrayList (); myMappingFiles.add("dozerBeanMapping.xml"); this.mapper = new DozerBeanMapper(); this.mapper.setMappingFiles(myMappingFiles); }
- Map an object to another of the same class (simple copy)
/** * This test shows the copy capability of the library. It doesn't need a XML configuration. */ @Test public void testCopySimpleClass() { ChildVO vo = fillChildVO(); ChildVO voPrime = mapper.map(vo, ChildVO.class); assertThat(vo, equalTo(voPrime)); }
- Map an object to another of the same class with a collection of children objects (deep copy)
/** * This test shows the copy with a child element in a collection. It doesn't need a XML configuration. */ @Test public void testCopyParentClass() { ParentVO parentVo = fillParentVO(); ChildVO childVo = fillChildVO(); linkParentAndChildVO(parentVo, childVo); ParentVO parentVoPrime = mapper.map(parentVo, ParentVO.class); assertThat(parentVoPrime.getChildVOs(), notNullValue()); assertThat(parentVoPrime.getChildVOs().size(), equalTo(1)); assertThat(parentVo, equalTo(parentVoPrime)); }
- Map an object to another of different class (simple copy)
/** * This test shows the copy of the simple properties of a class to another. We need to add the following XML to run properly: * <mapping> * <class-a>gazpachito.examples.dozer.persistence.ChildVO</class-a> * <class-b>gazpachito.examples.dozer.facade.ChildBO</class-b> * <field> * <a>persistenceId</a> * <b>businessId</b> * </field> * </mapping> */ @Test public void testMapSimpleClass() { ChildVO childVo = fillChildVO(); ChildBO childBo = mapper.map(childVo, ChildBO.class); assertThat(childBo.getBusinessId(), notNullValue()); assertThat(childBo.getPrice(), notNullValue()); assertThat(childBo.getParentBO(), nullValue()); }
- Map an object to another of different class with a collection of children objects (deep copy)
/** * This test shows the copy of the properties of the parent class and a child. We need the following XML: * <mapping> * <class-a>gazpachito.examples.dozer.persistence.ChildVO</class-a> * <class-b>gazpachito.examples.dozer.facade.ChildBO</class-b> * <field> * <a>persistenceId</a> * <b>businessId</b> * </field> * <field> * <a>parentVO</a> * <b>parentBO</b> * </field> * </mapping> * <mapping> * <class-a>gazpachito.examples.dozer.persistence.ParentVO</class-a> * <class-b>gazpachito.examples.dozer.facade.ParentBO</class-b> * <field> * <a>persistenceId</a> * <b>businessId</b> * </field> * <field> * <a>childVOs</a> * <b>childBOs</b> * </field> * </mapping> */ @Test public void testMapParentClass() { ParentVO parentVO = fillParentVO(); ChildVO childVO = fillChildVO(); linkParentAndChildVO(parentVO, childVO); ParentBO parentBo = mapper.map(parentVO, ParentBO.class); assertThat(parentBo, notNullValue()); assertThat(parentBo.getBusinessId(), notNullValue()); assertThat(parentBo.getName(), notNullValue()); SetchildBos = parentBo.getChildBOs(); assertThat(childBos, notNullValue()); assertThat(childBos.size(), equalTo(1)); ChildBO childBo = parentBo.getChildBOs().iterator().next(); assertThat(childBo.getBusinessId(), notNullValue()); assertThat(childBo.getPrice(), notNullValue()); assertThat(childBo.getParentBO(), notNullValue()); assertThat(childBo.getParentBO(), equalTo(parentBo)); }
- Performance
- Dozer mapper
| Measurement Point | # | Average | Min | Max | Total |
|-----------------------|------|---------|-------|-----------|------------|
| DozerParentMapper:map | 1000 | 14,627 | 4,479 | 2.107,324 | 14.626,801 |
|-----------------------|------|---------|-------|-----------|------------|
- Manual mapper
| Measurement Point | # | Average | Min | Max | Total |
|------------------------|------|---------|-------|-----------|-----------|
| ManualParentMapper:map | 1000 | 2,752 | 0,151 | 2.368,851 | 2.751,547 |
|------------------------|------|---------|-------|-----------|-----------|
As you can see, the manual mapper is approximately six times faster but this test is a bit vague.
- Conclusions
The usage of a mapping library can save us many time in development process but it adds a little overhead. You have to consider it usage but the flexibility of this library eases the mapping of complex classes. We recommend to read the development guide to take advantage of Dozer because this guide only covers a bit of the features of this great Java library.Thanks to my partner Fernando for the idea to do a performance test.
No comments:
Post a Comment