O framework Dozer é um parser de um POJO para outro POJO, o que significa dizer que ele copia automaticamente, de forma recursiva, dados de um objeto para outro.
O Dozer supporta mapear propriedades simples, tipos complexos, mapeamento bidirecional, mapeamento implicito-explicito, suporta também o mapeamento de coleções de atributos.
O Dozer suporta não somente o mapeamento entre nomes de atributos mas também faz conversões entre tipos. Para cenários mais complexos o Dozer permite realizar conversões customizadas através de arquivos XML.
O mapper é indicado sempre que você precisa copiar as informações de um POJO para outro. A maioria dos atributos podem ser mapeados automaticamente pelo Dozer através de reflexão, mas, como já foi comentado, qualquer mapeamento customizado pode ser feito através de arquivos XML. O mapeamento é bi-direcional somente o relacionamento entre as classes precisa ser definido. Se qualquer nome de alguma propriedade em ambos os objetos forem iguais você não precisará fazer nenhum mapeamento adicional para mapear essas propriedades.
A figura abaixo representa um exemplo em que é recomendável utilizar o Dozer em uma arquitetura. Note que ele é tipicamente usado nas entradas e saídas da aplicação. O Dozer garante que seus objetos de domínio interno não sejam vistos pelas camadas de apresentação ou por consumidores externos. Ele também pode ajudar a mapear seus objetos de domínio para chamadas externas de API’s e vice versa.
Em outras palavras, ao invés de você expor suas entidades JPA, por exemplo, o que indiretamente tornaria públicas informações de sua base de dados, você cria um POJO similar às suas entidades JPA, só que sem as anotações, e expõe esse POJO para os clientes de sua aplicação. Entre o POJO exposto e o a entidade JPA você poderia utilizar um framework como o Dozer e parsear dados de suas entidades JPA para o POJO exposto e vice e versa, encapsulando informações importantes de sua aplicação.
O Parser – “DozerParser”
No nosso exemplo temos um Parser genérico fazendo conversão de dois POJOS simples. Inicialmente criamos uma instância do Mapper, posteriormente temos dois métodos responsáveis por parsear um objeto e uma lista de objetos respectivamente.
Note que eles recebem objetos genéricos, o que significa que você pode usar da forma como está em um projeto seu que já vai funcionar. Logo abaixo temos um terceiro método que é quem efetivamente faz a invocação ao Dozer e este por sua vez realiza o parse.
package br.com.semeru.dozer.converter; import java.util.ArrayList; import java.util.List; import org.dozer.DozerBeanMapper; import org.dozer.Mapper; public class DozerParser { private static final Mapper dozerMapper = new DozerBeanMapper(); public static <O, D> D parseObjectInputToObjectOutput(O originalObject, Class<D> destinationObject) { return parser(destinationObject, originalObject); } public static <O, D> List<D> parserListObjectInputToObjectOutput(List<O> originalObjects, Class<D> destinationObject) { List<D> destinationObjects = new ArrayList<D>(); for (Object originalObject : originalObjects) { destinationObjects.add(parser(destinationObject, originalObject)); } return destinationObjects; } private static <D> D parser(Class<D> destinationObject, Object originalObject) { return dozerMapper.map(originalObject, destinationObject); } }
O objeto de origem – “InputObject”
Aqui temos um objeto de entrada, que é um POJO simples.
package br.com.semeru.dozer.objects; import java.io.Serializable; public class InputObject implements Serializable { private static final long serialVersionUID = 1L; private String name; private Integer age; // Getters, setters, equals e hashCode omitidos }
O objeto de destino – “OutputObject”
Aqui temos o segundo POJO para onde os dados do objeto de entrada serão copiados. Note que a extrutura dos POJO’s devem ser iguais. Caso contrário você deveria personalizar o parser de acordo com a sua necessidade usando arquivos XML.
package br.com.semeru.dozer.objects; import java.io.Serializable; public class OutputObject implements Serializable { private static final long serialVersionUID = 1L; private String name; private Integer age; // Getters, setters, equals e hashCode omitidos }
A classe de Mock – “MockInputObject”
Aqui temos uma classe de Mock que cria uma instância do POJO de entrada e popula-o com informações simulando um acesso à uma base de dados por exemplo.
package br.com.semeru.dozer.mocks; import br.com.semeru.dozer.objects.InputObject; import java.util.ArrayList; import java.util.List; public class MockInputObject { private InputObject inputObject = new InputObject(); public InputObject mockInput() { inputObject.setName("Name Test"); inputObject.setAge(21); return inputObject; } public List<InputObject> mockInputList() { List<InputObject> inputObjects = new ArrayList<InputObject>(); for (int i = 0; i < 3; i++) { inputObjects.add(mockInput(i)); } return inputObjects; } private InputObject mockInput(Integer number) { inputObject.setName("Name Test " + number); inputObject.setAge(20 + number); return inputObject; } }
Os testes – “DozerParserTest”
Por fim temos os testes unitários que são responsáveis por verificar o comportamento do parser pasando as instancias mockadas pela classe acima pelo nosso parser e realizando asserções nos resultados.
package br.com.semeru.dozer.converter; import br.com.semeru.dozer.mocks.MockInputObject; import java.util.List; import junit.framework.Assert; import org.junit.Before; import org.junit.Test; import br.com.semeru.dozer.objects.OutputObject; public class DozerParserTest { MockInputObject inputObject; @Before public void setUp() { inputObject = new MockInputObject(); } @Test public void parseObjectInputToreObjectOutputTest() { OutputObject output = DozerParser.parseObjectInputToObjectOutput(inputObject.mockInput(), OutputObject.class); Assert.assertEquals("Name Test", output.getName()); Assert.assertTrue(output.getAge() == 21); } @Test public void parserListObjectInputToObjectOutputTest() { List<OutputObject> output = DozerParser.parserListObjectInputToObjectOutput(inputObject.mockInputList(), OutputObject.class); Assert.assertEquals("Name Test 2", output.get(0).getName()); Assert.assertTrue(output.get(0).getAge() == 22); } }
Enfim o Dozer é uma ótima opção de parser de um POJO para outro sendo também bastante simples de se usar. Como alternativa ao Dozer temos o Orika que faz as mesmas tarefas que o Dozer. No meu Github você pode ver um exemplo básico de utilização do Dozer e do Orika. Bom proveito e faça bons estudos.