Serializando e Deserializando Classes de Entidade do Hibernate/JPA para XML com XStream

Em um projeto que trabalhei tivemos a necessidade de representar em XML a nossa principal entidade de banco de dados, com [quase] todas as suas relações diretas e indiretas (uma grande árvore). A opção que se mostrou mais fácil foi o uso da biblioteca XStream. No entanto, esbarramos em alguns problemas uma vez que o Hibernate trabalha com Proxies e implementações próprias das Collections do Java, afim de promover a abordagem Lazy para a obtenção de entidades relacionadas. Nesse post demonstro como resolvemos esse problema.

Se estiver usando maven, adicione ao pom.xml da sua aplicação a dependência xstream-hibernate:

<dependency>
	<groupId>com.thoughtworks.xstream</groupId>
	<artifactId>xstream-hibernate</artifactId>
	<version>1.4.7</version>
</dependency>	

É muito importante que você crie e configure uma única instância de XStream que deverá ser usada tanto na serializaçaõ quanto na deserialização, para que tudo funcione. Configure-a da seguinte forma:

	
import org.hibernate.collection.internal.PersistentBag;
import org.hibernate.collection.internal.PersistentList;
import org.hibernate.collection.internal.PersistentMap;
import org.hibernate.collection.internal.PersistentSet;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.hibernate.converter.HibernatePersistentCollectionConverter;
import com.thoughtworks.xstream.hibernate.converter.HibernatePersistentMapConverter;
import com.thoughtworks.xstream.hibernate.converter.HibernatePersistentSortedMapConverter;
import com.thoughtworks.xstream.hibernate.converter.HibernatePersistentSortedSetConverter;
import com.thoughtworks.xstream.hibernate.converter.HibernateProxyConverter;
import com.thoughtworks.xstream.hibernate.mapper.HibernateMapper;
import com.thoughtworks.xstream.mapper.MapperWrapper;

public class MeuXStream {

    public static XStream xs;

    static {

        xs = new XStream() {
	    @Override
	    protected MapperWrapper wrapMapper(final MapperWrapper next) {
	        return new HibernateMapper(next);
	    }
        };

        xs.registerConverter(new HibernateProxyConverter());
        xs.registerConverter(new HibernatePersistentCollectionConverter(xs.getMapper()));
        xs.registerConverter(new HibernatePersistentMapConverter(xs.getMapper()));
        xs.registerConverter(new HibernatePersistentSortedMapConverter(xs.getMapper()));
        xs.registerConverter(new HibernatePersistentSortedSetConverter(xs.getMapper()));

        xs.alias("Bag", PersistentBag.class);
        xs.alias("Map", PersistentMap.class);
        xs.alias("List", PersistentList.class);
        xs.alias("Set", PersistentSet.class);

        xs.autodetectAnnotations(true);
    }
    ...
}

A maior parte das linhas acimas é para tratar a conversão de coleções para XML. Suas Classes de Entidades podem conter coleções, e estas vão estar com os tipos abstratos das Collections (Ex: Set, List, Map). Em tempo de execução, o Hibernate instanciará essas propriedades com suas próprias implementações dessas Collections (ex: org.hibernate.collection.PersistentList para o caso de List). No entanto você não vai querer que no seu XML final conste detalhes dessas coleções específicas do hibernate. Para isso, defina o HibernateMapper como o MapperWrapper do seu XStream. Além disso, registre os conversores do Hibernate fornecidos pela biblioteca xstream-hibernate. Por fim, é interessante definir alias para as 4 coleções do hibernate, pois, mesmo depois de convertidas, o XML final utiliza no atributo class o nome da classe do Hibernate, o que é irrelevante, mas fica poluído e verboso.

Caso você tenha mapeado alguma herança, certifique-se também de colocar a anotação @Proxy com lazy=false na super-classe. Isso evita que o Hibernate utilize Proxies nas instâncias dessa classe, o que leva ao XStream a serializar essas Proxies como se fossem instancias apenas da super-classe, gerando conflitos quando ele esbarra com uma propriedade da sub-classe e causando erros de de conversão (ConversionException). Lembrando que mesmo que você coloque essa anotação, isso não impedirá que carregamento das propriedades do tipo anotado seja Lazy, pois ainda há a configuração de cada propriedade nas próprias entidades (FetchType), apenas evitará a confusão com Proxies da super-classe.

	
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Proxy(lazy = false)
public class MinhaSuperClasse implements Serializable {
    ...
}

Provavelmente você não desejará representar toda sua cadeia de classes no XML, podando alguns pontos. Para isso, anote as propriedades que não desejar serializar com a anotação @XStreamOmitField. Para que essas anotações sejam reconhecidas, foi preciso aquele ultimo comando do bloco estático de inicialização do XStream.

Por fim, para serializar, o mais importante é carregar a sua entidade logo antes de transformar em XML, pois dessa forma, com a sessão ainda aberta, o próprio XStream irá percorrer todos os getters das propriedades, forçando o carregamento de tudo que foi configurado com a abordagem Lazy.

	
...
MinhaEntidade entidade = minhaEntidadeDAO.carregarPeloId(id);
String xml = xs.toXML(entidade)
...

Para deserializar, obtenha de volta o mesmo XStream usado na serialização e faça:

	
...
String xml = lerXmlDeAlgumLugar();
MinhaEntidade entidade = (MinhaEntidade) xs.fromXML(xml);
...

Atente para a limitação de que o XML gerado em um momento só poderá ser deserializado em um outro momento se a mesma estrutura de classes e propriedades se mantiver. Qualquer mudança de nome de classe ou de propriedade, ou mesmo alteração de pacote, resultará em um erro de conversão. Para contornar esse problema pode-se definir alias para todas as propriedades e classes com as anotações @XStreamAlias e @XStreamAliasType, ou fazendo chamadas à xs.alias(…) ou xs.aliasType(…). Pode-se ainda criar Converters customizados para definir como serializar/deserializar uma classe específica, ou no caso de um comportamento geral para todas a classes, o ideal é escrever um Mapper customizado. Detalhes sobre esse processo de customização você encontra nessa ajuda do XStream.

Forçando o uso de Hibernate 3.x com JPA no Jboss AS 7 / EAP 6

O Jboss AS 7 já provê a biblioteca do Hibernate 4 para os projetos, não sendo necessário incluí-la na lib de seu WAR. No entanto, pode acontecer de você querer usar uma versão mais antiga do Hibernate, como por exemplo a versão 3. Além de incluí-la como lib, você precisa avisar para o Jboss para deixar de prover o Hibernate dele, no caso a versão 4. Para tanto, alguns passos sesão necessários.

Você precisa adicionar ao WAR final o arquivo META-INF/jboss-deploy-structure.xml com o segunte conteúdo:

<jboss-deployment-structure>
	<deployment>
		<exclusions>
			<module name="org.hibernate" />
		</exclusions>		
	</deployment>
</jboss-deployment-structure>

Você também vai precisar adicionar uma propriedade a mais na sua Persistence Unit declarada no persistence.xml:

...
	<property name="jboss.as.jpa.providerModule" value="hibernate3-bundled" />
...

E por fim, certifique-se de fornecer o JAR do hibernate ao compilar o pacote final. Para quem usa Maven, basta adicionar o seguinte:

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-entitymanager</artifactId>
	<version>3.6.10-Final</version>
</dependency>

Não se esqueça de verificar a árvore de dependências do Maven para saber se alguma dependência indireta está requerendo alguma biblioteca específica do Hiberante também na versão 4. Se houver, o downgrade pode não ser possível ou pelo menos será não-trivial. Boa sorte!

Review do Livro “Aplicações Java para Web com JSF e JPA” de Gilliard Cordeiro

Em 2012 assumí a disciplina de Tópicos Especiais em Análise e Desenvolvimento de Sistemas na faculdade em que leciono. Por se tratar de uma disciplina do último período do curso, encarei ela como uma “última chance” de resgatar os alunos, ao invés de simplesmente abordar assuntos complexos diversos como é de costume em disciplinas de Tópicos Especiais. Queria deixar algo de legal para os alunos levarem para o mercado de trabalho logo em seguida, já agregando ao currículo. Como o curso escolheu Java e eles já tinham visto algo de Desenvolvimento Web em JAVA em semestres anteriores, decidi abordar os frameworks JSF 2 e JPA 2, muito requisitados pelo mercado. No entanto, para tocar o curso precisaria de literatura em português para me apoiar, tendo em vista que não poderia exigir o inglês dos alunos (faz parte…).

Qual não foi minha Surpresa de descobrir duas grandes coisas: a editora Casa do Código, que como ela mesmo se entitula publica “livros de programadores para programadores”; e dentre os seus títulos descobri também uma grande preciosidade, o livro “Aplicações Java para Web com JSF e JPA” de Gilliard Cordeiro. O que me chamou a atenção foi: o livro aborda duas tecnologias que geralmente andas juntas; é em português, melhor, foi escrito por um brasileiro (já traduções nem sempre carregam o estilo do escritor original); o preço é convidativo, inclusive dando opção de comprar apenas a versão digital por uma pechincha; o tamanho do livro é razoável, considero pequeno, 275 páginas, exigindo apenas alguns minutos por dia para terminar de ler o livro em poucas semanas (essencial para indicar para alunos!)

Adquirí um exemplar e lí. Gostei bastante da escolha didática do livro que faz questão de elucidar como as coisas eram antes do JSF e do JPA e o motivo de cada dessas tecnologias terem entrado no jogo. Essa visão ajuda o leitor a compreender os frameworks não apenas como uma caixa preta a ser dominado mas sim como algo fruto de um design que evoluiu junto com a comunidade de desenvolvimento em torno do Java e de suas tecnologias. Destaco a introdução onde diagramas demonstram a evolução do código de um Servlet puro sem separação nenhuma de lógica de apresentação, negócio e persistência até atingir padrões mais atuais de design, justificando muita coisa repetida sem explicação por muitas equipes.

O livro não se propõe a ser um guia definitivo de JSF e JPA, e assim sendo, não entra em muitos detalhes tal como uma bíblia. Mas sua leitura provê o necessário para desenvolver uma aplicação web completa envolvendo aspectos diversos além do básico tais como segurança e performance. Considero uma excelente leitura para quem já programa em Java e desconhece os dois frameworks ou mesmo quem já programa em JSF mas ainda se sente perdido com conceitos fundamentais tais como o ciclo de uma requisição no JSF, o uso dos componentes básicos JSF, o uso de Ajax com o JSF. Já na parte de JPA, destaco as discussões diversas sobre performance seja ao abordar carregamento preguiçoso (lazy), mapeamento ou o problema das N+1 queries, o que pode embasar até mesmo desenvolvedores experientes na tomada de decisões em seus projetos.

Em resumo, gostei bastante da abordagem livro. Mais tarde também adquirí exemplares de outros títulos relacionados com Java da Casa do Código, dentre eles: Design Patterns, JSF Eficaz, Test-Driven Development, e o recém lançado CDI. Todos são livros relativamente pequenos mas com conteúdo denso, destinados realmente para programadores. Fica a dica!