Usar @Transient com JPA pode parecer fácil, mas existem 3 coisas que se você fizer, podem deixar seu código confuso. Aprenda agora como utilizar da melhor forma, escrever um código de alta qualidade e entregar seu projeto no prazo!
Por que JPA?
Como tudo que vamos utilizar na programação, temos que entender o conceito, para não termos dor de cabeça mais tarde. Utilizamos JPA basicamente para fazer mapeamento objeto-relacional. Em geral, queremos estabelecer uma relação entre nossos objetos e uma base de dados relacional. O que fazemos é basicamente isso:
@Transient com JPA
A partir do momento que você anota sua classe com @Entity, o JPA espera que todos os atributos dela estejam relacionados a uma coluna do banco. Os atributos anotados com @Transient são uma exceção a essa regra. De certa forma, essa anotação funciona como uma ferramenta para burlar o conceito básico do JPA. A grande questão é que cada pequena diferença que você cria entre sua tabela e sua classe traz mais complexidade para seu mapeamento.
Por isso, usar @Transient com JPA pode ser um sinal de que tem algo cheirando mal no seu código e você não está percebendo. Ao mesmo tempo, nem sempre utilizar é necessariamente errado. Vamos ver alguns cenários e suas alternativas.
1. Usar @Transient com JPA para definir um tipo de operação
Um cenário comum. Você está na classe onde executa suas regras de negócio e recebe um objeto de Usuario. Você precisa saber se é para criar um novo usuário, ou atualizar um existente, e pra isso cria um atributo transient.
Se você escreveu um código assim, é uma boa hora para refatorar. O atributo atualizar não tem absolutamente nada a ver com a classe Usuario. Então a coerência do seu código foi para o espaço. Você criou um forte acoplamento entre o comportamento do método salvarUsuario
e uma flag que está dentro de uma entidade. Além disso, essa entidade só deveria ter atributos relacionados a ela.
Nesse caso, provavelmente seu fluxo de negócio para um novo cadastro e para uma atualização são diferentes. Até mesmo na camada de apresentação, raramente a página em que um usuário se cadastra é a mesma onde ele atualiza seus dados. Então não faz sentido vincular isso no seu negócio. Seria muito melhor ter dois métodos separados para cada operação e uma classe Usuario limpa.
Além disso, se você está recebendo a entidade de uma chamada externa ou da sua camada de apresentação, considere utilizar o padrão DTO.
2. Usar @Transient com JPA para passar um dado para validação
Outro cenário em que você pode considerar usar @Transient com JPA é quando percebe que precisa passar um dado apenas para validação, mas não para persistência. Vejamos.
Nesse caso temos dois exemplos. O atributos acima foram criados apenas para que sua camada de negócio faça alguma validação. Provavelmente seria algo mais ou menos assim:
Esse código tem alguns problemas:
- Primeiro, esses dois atributos não tem de fato a ver com a entidade que está sendo persistida.
- Depois, essas validações não precisam estar tão a fundo no backend. A confirmação do email serve para impedir um possível erro de digitação, algo que está muito mais próximo da camada de apresentação. A aceitação de termos de uso serve apenas para garantir que o usuário acessou os termos e/ou clicou em um checkbox, algo que não muda o comportamento dessa parte da aplicação.
- E por último, quem quer que esteja chamando esse método pode burlar as validações. A camada de apresentação poderia simplesmente copiar o conteúdo de
email
paraconfirmacaoEmail
, e marcar um true em aceitouTermosDeUso. Depois disso, faria a chamada. Sua validação só serviria para dar uma falsa sensação de segurança.
Qual seria então a alternativa? Depende muito do seu cenário:
- Se você usa serviços rest, deixe que apresentação faça esse trabalho. Se quiser muito manter os atributos para lembrar ao usuário do seu serviço de fazer essas validações, considere receber uma DTO ao invés da sua entidade.
- Se você usa algum framework como JSF, simplesmente faça essa validação no seu controller, sem criar atributos na classe.
- No pior dos casos, se você ainda acredita ter alguma razão para fazer isso, utilize parâmetros separados, deixando sua entidade fora disso:
3. Usar @Transient com JPA para armazenar outras entidades temporariamente
Outro caso em que usar @Transient com JPA costuma ser utilizado, é para armazenar uma lista de outras entidades. Por exemplo:
Por vários motivos que não são o foco deste artigo, muitas vezes mapeamentos bidirecionais não são recomendados. Isso faz com que alguns programadores criem esse atributo transient para preencher quando necessário. Eu considero esse caso um problema “menor”, pois conceitualmente o usuário realmente tem uma lista de endereços.
O grande problema aqui é quando essa lista é preenchida. Ao fazer isso, geralmente você está criando um acoplamento entre o código que preencheu a lista, e o código que utilizou. Minhas sugestões:
- Se o consumidor da entidade
Usuario
for uma fronteira externa ao sistema, ou uma camada de apresentação, você pode utilizar uma DTO. - Se o consumidor for outra parte interna do sistema que tem acesso ao banco de dados, deixe que ela mesma recupere essa informação.
- Considere utilizar um mapeamento bidirecional com
FetchType.LAZY
, e avalie quais seriam os impactos no seu código:
- Raramente não será possível utilizar uma das alternativas citadas acima. Caso você ainda acredite que não tem outra solução, preencha essa lista apenas em métodos que explicitamente fazem isso, e documente bem na classe Usuario:
Uso de DTO
Algumas vezes nesse artigo citei o uso do padrão DTO. Ele foi criado especificamente para isso: transferir informações entre subsistemas, ou fronteiras claras da aplicação. A única razão válida que vejo para não utilizá-lo é o aumento na quantidade de código. O chamado boilerplate code. Porém, ao utilizá-lo, você evita usar @Transient com JPA.
Além disso, você não precisa escrever todo o código para converter uma entidade para DTO. Existem muitas ferramentas para ajudar nesse mapeamento, que facilitam esse trabalho e suavizam o impacto no tamanho do código. Você pode encontrar uma lista dessas ferramentas aqui, e muitas outras na internet.
Quando usar @Transient com JPA
Bom, então não tem nenhum caso bom para se utilizar @Transient? Na verdade, tem. Um cenário onde essa anotação é muito útil, é para fazer cache de atributos derivados de outros atributos. Utilizando a mesma classe Usuario, é possível fazer algo do tipo:
É claro que esse cenário é extremamente simples, e talvez o aumento de complexidade não compense. Afinal, lembre-se que nesse caso você teria que atribuir null ao atributo nomeCompleto se sua classe for mutável e alguém alterar o primeiroNome ou sobrenome. Mas, em um cenário onde o ganho de performance pode ser considerável, é uma opção válida.
Parabéns! Agora você sabe usar @Transient com JPA, pode entregar seu projeto mais rápido e crescer na sua carreira.
Quer receber minhas melhores dicas para escrever código de alta qualidade e entregar projetos no prazo? Então acesse aqui.
Quer aprender a melhorar seu código diariamente? Então me segue no twitter : https://twitter.com/rinaldodev
E você? Conhece outras situações em que devemos ter cuidado com @Transient com JPA? Tem dicas de onde podemos usar sem problemas? Compartilhe também! Deixe um comentário!
Gostou do que aprendeu? Compartilhe com outros Devs!
Top comments (0)