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.
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!