22 de mai. de 2009

RMI e HIbernate... lições aprendidas


Estou participando de um projeto onde decidimos utilizar RMI para conversação de dois módulos do sistema. A decisão não vem ao caso agora, mas achei interessante gurdas alguns "causos"...


RMI e Hibernate, onde tudo começou


Como falei anteriormente, decidimos utilizar RMI para integrar dois módulos do sistema. Um módulo responsável pela parte de negócio( persistência também) e outro por uma parte específica bem baixo nível (baixo nível = bytes.. 01010101...etc...).

Então, o gênio projetista teve a seguinte idéia: "Vamos utilizar as classes persistentes para trafegar via RMI".

Resultado: LazyInitializationException -LIE.


Em um determinado método de uma classe acessada via RMI o retorno era um objeto que foi buscado do Hibernate. Este objeto tinha outros objetos, que tinham outros objetos, que tinham outros objetos... blá blá blá...

Soluções:


1º) Colocar "transient"(do java não do JPA) nos atributos que não deveriam trafegar via RMI. Puramor de Deus, Nãããão!!!

Imagina ficar colocando transiente em tudo que é atributo espalhado pelo código inteiro. Loucura!!!

2º) Modificar o design
Interessante... muito interessante...
O Design... (não é de moda porr!!)


Tentei fazer algo que representasse o problema. Então abaixo estão algumas classes de domínio, mapeadas no hibernate.


OBS: Para quem não sabe, em RMI devem existir interfaces comuns nos dois lados da comunicação. Então a interface PessoaData é conhecida pelos dois módulos.


Agora, viram o que o projetista gênio fez ?!?!?!

A classe Pessoa implementa a interface PessoaData. Na imagem acima temos somente um exemplo, mas no contexto real são umas 20 classes que irão trafegar via RMI. Todas essas classes são entidades persistentes.

Observe que a interface PessoaData retorna byte[]. Isso mesmo.. o "carinha" que utiliza estas classes vai precisar dos dados convertidos em bytes. Guarde esta informação.


Confesso que o projetista gênio(agora já deve-se saber quem é o projetista... :D ) percebeu a burrada somente quando ocorreu o erro com hibernate.


O que vocês conseguem enchergar ai?

Bom, vou dizer o que eu consegui enxergar:

  • Uma classe persistente implementa uma interface utilizada para comunicação entre sistemas. Acomplamento! Entidade persistente conhece uma interface onde o objetivo é comunicação de dados entre sistemas... "nada a vê"
  • Pelo motivo acima, a entidade persistente é responsável por converter String, Integer, Date em bytes. Coesão! Tava tudo misturado, conversões com bytes em uma classe onde o objeto é enviar e obter informações do banco de dados.

Solução tomada

Utilizamos os famosos Transfer Objects. Segue o exemplo:




Matamos o problema de coesão, onde a classe PessoaTo é responsável por trabalhar os dados para comunicação.
Matamos o problema de acomplamento onde a classe Pessoa não sabe que a classe PessoaTo existe.
Matamos o LazyInitializationException. A classe TO contém apenas o que será necessário para transferência.

Lições aprendidas
  • Colocar tudo em uma classe é diferente de economizar tempo
  • Preguisoço trabalha em dobro... :D
  • Projetos iterativos minimizam os erros. Descobrimos o problema no início e retrabalho foi de aproximadamente 5hs.
  • Enviar objetos utilizados para persistência via RMI-> ... LazyInitializationException
  • Spring RMI exporter facilita muito!
  • Cuidar as dependências das classes

Era isso..

Abraço!

3 comentários:

OL disse...

Ótimo registro!
Quem sabe um tutorial básico sobre essa comunicação RMI hein? hehehe

abraço!

Eduardo Frazão disse...

Boa tarde. Obrigado por compartilhar sua experiência.

Estou no meio de um projeto, onde certamente vamos trabalhar com Serviços remotos, mas não quero lidar com RMI diramente. É verboso e intrusivo. Quero poder reusar minha API integralmente, e deixar os clientes totalmente ignorantes da implementação da API. Será um projeto 100% OSGi. A questão é: Não terei versões diferentes de classes de domínio + classes DTO. Como você lida com os relacionamentos LAZY? Você acha devo hidratar os objetos antes da remessa? Não devo usar Lazys? Devo usar objetos 100% desatachados? Obrigado pela força!

Alexandre Simundi disse...

Eduardo,

Não tenho acompanhado meu blog faz tempo, vi sua resposta só agora.

Hoje acredito que lazy deve ser usado QUASE NUNCA.

Usar lazy loading significa deixar a transação do banco de dados aberta mais que devia, por isso recomendo fortemente não usar lazyloading.

Faz uma consulta que busque todos os dados necessários de uma vez só, depois envia o objeto pra onde tiver que ser enviado.

Lazy loading is evil!!! hehehe