Início > Arquitetura > XMLType: Direto das Trincheiras

XMLType: Direto das Trincheiras

Esse artigo trata de problemas que ocorreram ao tentar mapear o XMLType do Oracle em uma aplicação Java cujo pool de conexões era gerenciado pelo Tomcat. Não falaremos aqui sobre boas práticas de programação, mas sim faremos um relato direto das trincheiras.

Contexto

Restrições Ambientais

Em empresas onde a integração dos sistemas aplicativos se dá via banco de dados, há estruturas de dados compartilhados por diversas aplicações clientes que podem ter sido construídas em diferentes plataformas. O ideal seria que um banco de dados apenas servisse como repositório de dados, ou seja, armazenasse o estado persistente de instâncias de classes de negócio, mas esse não era o entendimento da equipe que concebeu a base de dados.

Para ser genérica o suficiente e servir dados aos diferentes tipos de aplicações, uma estrutura de dados (função, procedimento, tipo de dado e etc) em um banco de dados deveria ser simples o bastante. No caso de tabelas, as colunas devem ter tipos de dados básicos, como Number e Varchar no Oracle. Em funções, os parâmetros de entrada e o tipo de retorno deveriam também ser tipos básicos e, além disso, a quantidade de parâmetros de entrada deveria ser pequena.

Entretanto, quando um banco de dados se propõe a ser a fonte única em torno da qual todas as aplicações gravitam, ou seja, quando tem fim em si próprio e quase ganha vida, as aplicações devem se adaptar e fazer o possível para utilizar as estruturas de dados compartilhadas oferecidas.

Nesses ambientes, práticas como DDD (Domain Driven Design) não são aconselháveis, pois o modelo de negócio é fortemente dependente do banco de dados, o que torna inclusive o uso de ferramentas de mapeamento objeto-relacional – as quais deveriam abstrair a complexidade de um banco de dados – mais uma fonte de problemas.

Utilizar tipos ou dialetos SQL específicos de um SGBD (Sistema Gerenciador de Banco de Dados) cria uma forte dependência em relação ao banco de dados, o que nos leva até a questionar a criação de soluções mais genéricas. A decisão de implementar uma solução não portável deve levar em conta:

  • A necessidade que a gerou;
  • O tempo disponível;
  • As prioridades das tarefas e
  • As restrições ambientais apresentadas.

No nosso caso, não havia perspectiva para mudança da visão da equipe que cuidava do banco de dados, embora, após muitos embates, eles tenham adotado uma postura um pouco mais flexível. Sequer havia possibilidade de alterar o tipo do SGBD sendo que no máximo será feita atualização de versão de tempos em tempos.

Sendo assim, nossa solução ficou atrelada ao Oracle.

Requisitos

O problema que precisávamos resolver era o seguinte:

  • Criar um framework para armazenamento e recuperação das configurações da dimensão, ordenação, posição e visibilidade de todas as colunas de todas as tabelas manipuladas pelo usuário autenticado de acordo com as seguintes restrições:
    • Cada usuário tem suas próprias configurações da apresentação;
    • As alterações devem ser gravadas em um arquivo XML que deve ser armazenado em uma coluna do tipo XMLType, que é específica do banco de dados Oracle.

Modelando a Solução

Modelamos o problema e a partir daí passamos a nos preocupar com as restrições de banco, pois elas limitariam nossas alternativas de solução. Atributos não relevantes foram omitidos dos modelos apresentados.

Partimos de uma entidade chamada Sessao que possuía, dentre outros atributos, o XMLType:

Fig. 1 – Modelagem inicial da sessão do usuário

Fig. 1 – Modelagem inicial da sessão do usuário

Como seria muito difícil trabalhar com esse tipo do Oracle, pensamos em um modelo que apresentasse de forma hierárquica as tabelas e colunas:

Fig. 2 – Modelagem final da sessão do usuário

Fig. 2 – Modelagem final da sessão do usuário

Tecnologias Utilizadas

As duas figuras anteriores mostram respectivamente o que tínhamos e onde queríamos chegar. Para obter o resultado modelado na Fig. 2, combinamos o UserType do Hibernate, o parser de XML XStream, o driver JDBC e o XDK (Oracle XML Developer Kit) da Oracle. Não entraremos em detalhes sobre cada ferramenta utilizada. Faremos apenas uma breve apresentação de cada uma.

XML com XStream

Sabendo que as configurações deveriam ser armazenadas em um arquivo XML, primeiro foi pesquisada uma forma de converter esse arquivo para uma coleção de objetos que representassem tabelas e os dados de suas colunas. Decidimos utilizar o XStream, pois com ele poderíamos ler e escrever XMLs de forma fácil mantendo a hierarquia dos objetos.

Conversor de Tipos do Hibernate

Utilizamos o Hibernate para mapear o banco de dados relacional. Sendo assim, primeiro verificamos o que ele poderia nos oferecer para mapear um tipo de dado complexo. Para fazer a conversão do XMLType na estrutura Tabela/Coluna modelada, analisamos duas possibilidades:

  1. Criar um UserType do Hibernate;
  2. Mapear o XMLType na entidade Sessao deixando a responsabilidade de fazer a conversão na entidade.

A primeira linha parecia mais promissora, pois entendemos que não era responsabilidade da entidade saber converter arquivos XML.

Oracle XDK

O XDK é um conjunto de componentes e ferramentas escritas em Java, C, e C++. O suporte ao XMLType é fornecido através do XML DB. Esse tipo é uma estrutura de dados composta de um CLOB (Character Large Object) utilizado para armazenar o arquivo XML original e funções agregadas para manipular o CLOB. É possível gravar e consultar diretamente o CLOB contido na coluna do tipo XMLType sem precisar recuperar o dado como XMLType. Sendo assim, tínhamos em mãos duas possibilidades para lidar com esse tipo de dados antes de convertê-lo para a estrutura que definimos, mas nenhuma das duas daria certo que tivéssemos em mãos uma OracleConnection.

Problemas Acessando o Pool de Conexões

Quando o Servlet Container – no nosso caso o Tomcat – gerencia o pool de conexões, ele fornece à aplicação um wrapper para o objeto Connection original quando se faz o lookup via JNDI. Era utilizada a classe JndiObjectFactoryBean do Spring Framework.  A configuração original do config.xml do Tomcat era:

server.xml

Dentro do método  nullSafeSet do nosso UserType precisávamos que fosse feito um cast para a implementação do PreparedStatement. No nosso caso, era um or.apache.tomcat.dbc.dbc.DelegatingPreparedStatement.

nullSafeSet

Sim, para fazer o cast para essa classe tivemos que importam uma biblioteca que vinha com o Tomcat. Em tempo de execução ocorreu uma ClassCastException na linha do cast cuja mensagem era:

cast

Notem que os namespaces são iguais. A versão do naming-resource-dbc.jar do Tomcat é idêntica à da aplicação. Não há dois jars contendo a mesma classe no classpath. Parecia ser um problema de proxy, mas não era. Lemos em algum lugar que exceções desse tipo são o terror dos programadores que trabalham com JDBC/Oracle.

Há muitas alternativas para se trabalhar com o tipo de dado que queríamos (XMLType), mas todas, direta ou indiretamente, necessitavam da implementação da OracleConnection, que só para deixar registrado era uma classe package protected chamada T4CConnection.

Solução

Depois de gastar alguns dias pesquisando e testando várias alternativas, encontramos uma solução que funcionou, mas para isso tivemos que alterar o tipo de datasource fabricado no container:

server.xml

Notem que o atributo “username” foi alterado para “user”, pois é em um atributo com esse nome que as classes do XML DB armazenam o usuário do banco.

Agora podemos garantir que temos um OraclePreparedStatement que por sua vez dá acesso a uma implementação da OracleConnection. Dessa forma, basta fazer o cast para OraclePreparedStatement, converter a coleção de Tabela para String utilizando o XStream e criar o XMLType que será enviado ao Oracle. O método nullSafeSet ficou mais ou menos assim:

nullSafeSetDe forma semelhante, convertemos o XML em uma coleção de Tabela no método nullSafeGet:

nullSafeGet

Referências:

https://www.hibernate.org/369.html
https://hibernate.bluemars.net/444.html?cmd=prntdoc
http://www.sc.ehu.es/siwebso/KZCC/Oracle_10g_Documentacion/appdev.101/b10790/xdb11jav.htm
http://forums.oracle.com/forums/thread.jspa?messageID=3122459
http://forums.oracle.com/forums/message.jspa?messageID=855366
http://www.oracle-base.com/articles/9i/XMLTypeDatatype.php
http://www.oracle.com/technology/sample_code/tech/java/codesnippet/jdbc/clob10g/handlingclobsinoraclejdbc10g.html
http://www.filibeto.org/sun/lib/nonsun/oracle/10.2.0.1.0/B19306_01/appdev.102/b14259/xdb03usg.htm#i1040530s
http://www.oracle.com/technology/tech/xml/xdkhome.html
http://xstream.codehaus.org/

Anúncios
Categorias:Arquitetura
  1. Nenhum comentário ainda.
  1. No trackbacks yet.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: