Enterprise Java

Orika: Mapping JAXB Objects to Business/Domain Objects

This post looks at mapping JAXB objects to business domain objects with Orika. Earlier this month, I covered the same mapping use case using reflection-based Dozer. In this post, I’ll assume the same example classes need to be mapped, but they will be mapped using Orika instead of Dozer.

Dozer and Orika are intended to solve the same type of problem: the automatic mapping of two “data” objects that do not share a common inheritance but represent the same same of data fields. Dozer uses reflection to accomplish this while Orika uses reflection and bytecode manipulation to accomplish it. Orika’s slogan is, “simpler, lighter and faster Java bean mapping.”

Orika has an Apache License, Version 2, and can be downloaded at https://github.com/orika-mapper/orika/archive/master.zip (sources) or at http://search.maven.org/#search|ga|1|orika (binaries). Orika has dependencies on Javassist (for bytecode manipulation), SLF4J, and paranamer (to access method/constructor parameter names at runtime). Two of these three dependencies (JavaAssist and paranamer but not SLF4J) are bundled in orika-core-1.4.4-deps-included.jar. If the dependencies are already available, the slimmer orika-core-1.4.4.jar can be used instead. As the names of these JARs suggest, I’m using Orika 1.4.4 for my examples in this post.

In my post Dozer: Mapping JAXB Objects to Business/Domain Objects, I discussed reasons that using instances of JAXB-generatated classes as business or domain objects is often not desirable. I then showed “traditional” ways of mapping between JAXB-generated classes and custom data classes so that data could be passed throughout an application in the business domain data objects. For this post, I will be using the same approach, but with Orika doing the mapping rather than doing custom mapping or using Dozer for the mapping. For convenience, I include the cost listings here for the JAXB-generated classes com.blogspot.marxsoftware.AddressType and com.blogspot.marxsoftware.PersonType as well as the renamed custom data classes dustin.examples.orikademo.Address and dustin.examples.orikademo.Person.

JAXB-generated AddressType.java

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2013.12.03 at 11:44:32 PM MST
//
 
package com.blogspot.marxsoftware;
 
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;
 
/**
 * <p>Java class for AddressType complex type.
 *
 * <p>The following schema fragment specifies the expected content contained within this class.
 *
 * <pre>
 * <complexType name="AddressType">
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <attribute name="streetAddress1" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="streetAddress2" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="city" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="state" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="zipcode" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * </pre>
 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "AddressType")
public class AddressType {
 
    @XmlAttribute(name = "streetAddress1", required = true)
    protected String streetAddress1;
    @XmlAttribute(name = "streetAddress2")
    protected String streetAddress2;
    @XmlAttribute(name = "city", required = true)
    protected String city;
    @XmlAttribute(name = "state", required = true)
    protected String state;
    @XmlAttribute(name = "zipcode", required = true)
    protected String zipcode;
 
    /**
     * Gets the value of the streetAddress1 property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getStreetAddress1() {
        return streetAddress1;
    }
 
    /**
     * Sets the value of the streetAddress1 property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setStreetAddress1(String value) {
        this.streetAddress1 = value;
    }
 
    /**
     * Gets the value of the streetAddress2 property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getStreetAddress2() {
        return streetAddress2;
    }
 
    /**
     * Sets the value of the streetAddress2 property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setStreetAddress2(String value) {
        this.streetAddress2 = value;
    }
 
    /**
     * Gets the value of the city property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getCity() {
        return city;
    }
 
    /**
     * Sets the value of the city property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setCity(String value) {
        this.city = value;
    }
 
    /**
     * Gets the value of the state property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getState() {
        return state;
    }
 
    /**
     * Sets the value of the state property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setState(String value) {
        this.state = value;
    }
 
    /**
     * Gets the value of the zipcode property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getZipcode() {
        return zipcode;
    }
 
    /**
     * Sets the value of the zipcode property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setZipcode(String value) {
        this.zipcode = value;
    }
 
}

JAXB-generated PersonType.java

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2013.12.03 at 11:44:32 PM MST
//
 
package com.blogspot.marxsoftware;
 
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
 
/**
 * <p>Java class for PersonType complex type.
 *
 * <p>The following schema fragment specifies the expected content contained within this class.
 *
 * <pre>
 * <complexType name="PersonType">
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <sequence>
 *         <element name="MailingAddress" type="{http://marxsoftware.blogspot.com/}AddressType"/>
 *         <element name="ResidentialAddress" type="{http://marxsoftware.blogspot.com/}AddressType" minOccurs="0"/>
 *       </sequence>
 *       <attribute name="firstName" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="lastName" type="{http://www.w3.org/2001/XMLSchema}string" />
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * </pre>
 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "PersonType", propOrder = {
    "mailingAddress",
    "residentialAddress"
})
public class PersonType {
 
    @XmlElement(name = "MailingAddress", required = true)
    protected AddressType mailingAddress;
    @XmlElement(name = "ResidentialAddress")
    protected AddressType residentialAddress;
    @XmlAttribute(name = "firstName")
    protected String firstName;
    @XmlAttribute(name = "lastName")
    protected String lastName;
 
    /**
     * Gets the value of the mailingAddress property.
     *
     * @return
     *     possible object is
     *     {@link AddressType }
     *    
     */
    public AddressType getMailingAddress() {
        return mailingAddress;
    }
 
    /**
     * Sets the value of the mailingAddress property.
     *
     * @param value
     *     allowed object is
     *     {@link AddressType }
     *    
     */
    public void setMailingAddress(AddressType value) {
        this.mailingAddress = value;
    }
 
    /**
     * Gets the value of the residentialAddress property.
     *
     * @return
     *     possible object is
     *     {@link AddressType }
     *    
     */
    public AddressType getResidentialAddress() {
        return residentialAddress;
    }
 
    /**
     * Sets the value of the residentialAddress property.
     *
     * @param value
     *     allowed object is
     *     {@link AddressType }
     *    
     */
    public void setResidentialAddress(AddressType value) {
        this.residentialAddress = value;
    }
 
    /**
     * Gets the value of the firstName property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getFirstName() {
        return firstName;
    }
 
    /**
     * Sets the value of the firstName property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setFirstName(String value) {
        this.firstName = value;
    }
 
    /**
     * Gets the value of the lastName property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getLastName() {
        return lastName;
    }
 
    /**
     * Sets the value of the lastName property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setLastName(String value) {
        this.lastName = value;
    }
 
}

Domain/Business Class Address.java

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package dustin.examples.orikademo;
 
import java.util.Objects;
 
/**
 * Address class.
 *
 * @author Dustin
 */
public class Address
{
   private String streetAddress1;
   private String streetAddress2;
   private String municipality;
   private String state;
   private String zipCode;
 
   public Address() {}
 
   public Address(
      final String newStreetAddress1,
      final String newStreetAddress2,
      final String newMunicipality,
      final String newState,
      final String newZipCode)
   {
      this.streetAddress1 = newStreetAddress1;
      this.streetAddress2 = newStreetAddress2;
      this.municipality = newMunicipality;
      this.state = newState;
      this.zipCode = newZipCode;
   }
 
   public String getStreetAddress1()
   {
      return this.streetAddress1;
   }
 
   public void setStreetAddress1(String streetAddress1)
   {
      this.streetAddress1 = streetAddress1;
   }
 
   public String getStreetAddress2()
   {
      return this.streetAddress2;
   }
 
   public void setStreetAddress2(String streetAddress2)
   {
      this.streetAddress2 = streetAddress2;
   }
 
   public String getMunicipality()
   {
      return this.municipality;
   }
 
   public void setMunicipality(String municipality)
   {
      this.municipality = municipality;
   }
 
   public String getState() {
      return this.state;
   }
 
   public void setState(String state)
   {
      this.state = state;
   }
 
   public String getZipCode()
   {
      return this.zipCode;
   }
 
   public void setZipCode(String zipCode)
   {
      this.zipCode = zipCode;
   }
 
   @Override
   public int hashCode()
   {
      return Objects.hash(
         this.streetAddress1, this.streetAddress2, this.municipality,
         this.state, this.zipCode);
   }
 
   @Override
   public boolean equals(Object obj)
   {
      if (obj == null) {
         return false;
      }
      if (getClass() != obj.getClass()) {
         return false;
      }
      final Address other = (Address) obj;
      if (!Objects.equals(this.streetAddress1, other.streetAddress1))
      {
         return false;
      }
      if (!Objects.equals(this.streetAddress2, other.streetAddress2))
      {
         return false;
      }
      if (!Objects.equals(this.municipality, other.municipality))
      {
         return false;
      }
      if (!Objects.equals(this.state, other.state))
      {
         return false;
      }
      if (!Objects.equals(this.zipCode, other.zipCode))
      {
         return false;
      }
      return true;
   }
 
   @Override
   public String toString()
   {
      return "Address{" + "streetAddress1=" + streetAddress1 + ", streetAddress2="
         + streetAddress2 + ", municipality=" + municipality + ", state=" + state
         + ", zipCode=" + zipCode + '}';
   }
 
}

Domain/Business Class Person.java

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package dustin.examples.orikademo;
 
import java.util.Objects;
 
/**
 * Person class.
 *
 * @author Dustin
 */
public class Person
{
   private String lastName;
   private String firstName;
   private Address mailingAddress;
   private Address residentialAddress;
 
   public Person() {}
 
   public Person(
      final String newLastName,
      final String newFirstName,
      final Address newResidentialAddress,
      final Address newMailingAddress)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.residentialAddress = newResidentialAddress;
      this.mailingAddress = newMailingAddress;
   }
 
   public String getLastName()
   {
      return this.lastName;
   }
 
   public void setLastName(String lastName) {
      this.lastName = lastName;
   }
 
   public String getFirstName()
   {
      return this.firstName;
   }
 
   public void setFirstName(String firstName)
   {
      this.firstName = firstName;
   }
 
   public Address getMailingAddress()
   {
      return this.mailingAddress;
   }
 
   public void setMailingAddress(Address mailingAddress)
   {
      this.mailingAddress = mailingAddress;
   }
 
   public Address getResidentialAddress()
   {
      return this.residentialAddress;
   }
 
   public void setResidentialAddress(Address residentialAddress)
   {
      this.residentialAddress = residentialAddress;
   }
 
   @Override
   public int hashCode()
   {
      int hash = 3;
      hash = 19 * hash + Objects.hashCode(this.lastName);
      hash = 19 * hash + Objects.hashCode(this.firstName);
      hash = 19 * hash + Objects.hashCode(this.mailingAddress);
      hash = 19 * hash + Objects.hashCode(this.residentialAddress);
      return hash;
   }
 
   @Override
   public boolean equals(Object obj)
   {
      if (obj == null)
      {
         return false;
      }
      if (getClass() != obj.getClass())
      {
         return false;
      }
      final Person other = (Person) obj;
      if (!Objects.equals(this.lastName, other.lastName))
      {
         return false;
      }
      if (!Objects.equals(this.firstName, other.firstName))
      {
         return false;
      }
      if (!Objects.equals(this.mailingAddress, other.mailingAddress))
      {
         return false;
      }
      if (!Objects.equals(this.residentialAddress, other.residentialAddress))
      {
         return false;
      }
      return true;
   }
 
   @Override
   public String toString() {
      return  "Person{" + "lastName=" + lastName + ", firstName=" + firstName
            + ", mailingAddress=" + mailingAddress + ", residentialAddress="
            + residentialAddress + '}';
   }
 
}

As was the case with Dozer, the classes being mapped need to have no-arguments constructors and “set” and “get” methods to support conversion in both directions without any special additional configuration. Also, as was the case with Dozer, Orika maps same-named fields automatically and makes it easy to configure the mapping of the exceptions (fields whose names don’t match). The next code listing, for a class I call OrikaPersonConverter, demonstrates the instantiation and configuration of an Orika MapperFactory to map most fields by default and to map the fields with different names than each other (“municipality” and “city”) through explicit mapping configuration. Once the MapperFactory is configured, copying from one object to another is easy and both directions are depicted in the methods copyPersonTypeFromPerson and copyPersonFromPersonType.

OrikaPersonConverter

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package dustin.examples.orikademo;
 
import com.blogspot.marxsoftware.AddressType;
import com.blogspot.marxsoftware.PersonType;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
 
/**
 * Convert between instances of {@link com.blogspot.marxsoftware.PersonType}
 * and {@link dustin.examples.orikademo.Person}.
 *
 * @author Dustin
 */
public class OrikaPersonConverter
{
   /** Orika Mapper Facade. */
   private final static MapperFacade mapper;
 
   static
   {
      final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
      mapperFactory.classMap(Address.class, AddressType.class)
                   .field("municipality", "city")
                   .byDefault()
                   .register();
      mapper = mapperFactory.getMapperFacade();
   }
 
   /** No-arguments constructor. */
   public OrikaPersonConverter() {}
 
   /**
    * Provide an instance of {@link com.blogspot.marxsoftware.PersonType}
    * that corresponds with provided {@link dustin.examples.orikademo.Person} as
    * mapped by Dozer Mapper.
    *
    * @param person Instance of {@link dustin.examples.orikademo.Person} from which
    *    {@link com.blogspot.marxsoftware.PersonType} will be extracted.
    * @return Instance of {@link com.blogspot.marxsoftware.PersonType} that
    *    is based on provided {@link dustin.examples.orikademo.Person} instance.
    */
   public PersonType copyPersonTypeFromPerson(final Person person)
   {
      PersonType personType = mapper.map(person, PersonType.class);
      return personType;
   }
 
   /**
    * Provide an instance of {@link dustin.examples.orikademo.Person} that corresponds
    * with the provided {@link com.blogspot.marxsoftware.PersonType} as
    * mapped by Dozer Mapper.
    *
    * @param personType Instance of {@link com.blogspot.marxsoftware.PersonType}
    *    from which {@link dustin.examples.orikademo.Person} will be extracted.
    * @return Instance of {@link dustin.examples.orikademo.Person} that is based on the
    *    provided {@link com.blogspot.marxsoftware.PersonType}.
    */
   public Person copyPersonFromPersonType(final PersonType personType)
   {
      Person person = mapper.map(personType, Person.class);
      return person;
   }
}

As is the case with Dozer, the mapping between two classes is bidirectional and so only needs to be made once and will apply in copying from either object to the other.

Conclusion

Like Dozer, Orika offers much more customizability and flexibility than demonstrated in this post. However, for relatively simple mappings (which are very common with applications using JAXB-generated objects), Orika is very easy to use out of the box. A good resource for learning more about Orika is the Orika User Guide.
 

Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button