DEV Community

Cover image for (tradução) Lombok e JPA: O que pode dar errado?
EronAlves1996
EronAlves1996

Posted on

(tradução) Lombok e JPA: O que pode dar errado?

Esta é uma tradução do artigo Lombok and JPA: What go wrong? escrito por Andrey Oganesyan.

Lombok é uma ótima ferramenta que traz uma maior concisão e um menor ruído ao seu código. Porém, há algumas coisas a se considerar quando se usa junto com JPA. Neste artigo, iremos investigar como o mau uso do Lombok pode trazer perdas de perfomance para aplicações que usam JPA (ou até mesmo quebrá-las), e como evitar isso, mantendo os benefícios de usar Lombok.

Nós desenvolvemos JPA Buddy - um plugin para IntelliJ IDEA [notas do tradutor: um ótimo plugin, inclusive, que recomendo] pensando em ter um uso mais simplificado do JPA. Antes de escrever qualquer código desse plugin, buscamos entender, lendo vários projetos do Github, como as pessoas trabalham com JPA. Percebemos que um monte deles usam Lombok para definir suas entidades.

É absolutamente OK usar Lombok nos seus projetos que incluem JPA, porém, há alguns "caveats" a serem observados. Analisando os projetos, vemos pessoas caírem nas mesmas armadilhas sempre e sempre. Por este motivo, incluímos várias regras de inspeção de código Lombok no JPA Buddy. Este artigo mostra os principais problemas que você pode enfrentar usando Lombok com entidades JPA.

HashSets e HashMaps quebrados

Muitas vezes, classes que definem entidades são anotadas com @EqualsAndHashcode ou @Data. A documentação para @EqualsAndHashCode define que:

[Tradução direta] Por padrão, irá usar todas as propriedades que não sejam estáticas e nem transientes, mas você pode modificar quais propriedades são usadas (e mesmo especificar que o retorno de vários métodos devem ser usados), simplesmente marcando os membros com @EqualsAndHashCode.Include ou @EqualsAndHashCode.Exclude

A implementação de equals ou hashCode para entidades JPA é um assunto sensível. Naturalmente, entidades são mutáveis. Mesmo o id de uma entidade é frequentemente gerado por um banco de dados, fazendo com que a mesma seja mudada depois que a entidade é persistida pela primeira vez. Isto significa que não podemos contar com nenhuma propriedade para calcular um hashCode confiável.

Por exemplo, vamos criar uma entidade de teste:

@Entity
@EqualsAndHashCode
public class TestEntity {

  @Id 
  @Generatedvalue(strategy = GenerationType.IDENTITY) 
  @Column(nullable = false) 
  private Long id;

}
Enter fullscreen mode Exit fullscreen mode

E executar o código abaixo:

TestEntity testEntity = new TestEntity();
Set<TestEntity> set = new HashSet<>();

set.add(testEntity);
testEntityRepository.save(testEntity);

Assert.isTrue(set.contains(testEntity), "Entity not found in the set");
Enter fullscreen mode Exit fullscreen mode

A asserção na última linha falha, mesmo que a entidade foi adicionada ao Set nas linhas anteriores. "Delomboking" [nota do tradutor: é possível retirar o lombok sem trazer alterações no funcionamento do código, através da ferramenta Delombok] a anotação @EqualsAndHashCode nos dá o seguinte código:

public int hashCode() {
   final int PRIME = 59;
   int result = 1;
   final Object $id = this.getId();
   result = result * PRIME + ($id == null ? 43 : $id.hashCode());
   return result;
}
Enter fullscreen mode Exit fullscreen mode

Assim que o id é gerado (na primeira chamada no método save), o hashCode muda. Então o HashSet procura pela entidade em uma "pool" diferente, porém não a encontra. Isso não seria um problema se o id fosse configurado durante a criação do objeto da entidade (isto é, se o UUID ou o id autoincrementável fosse definido pela aplicação), mas ids autogerados pelo banco de dados são mais comuns.

Carregar acidentalmente atributos "Lazy"

Como mencionado acima, @EqualsAndHashCode inclui todos as propriedades do objeto por padrão. O mesmo pode ser dito para o @ToString:

[tradução direta] Toda e qualquer definição de classe pode ser anotada com @ToString para que o Lombok gere uma implementação do método toString(). Por padrão, ele irá retornar o nome da sua classe, junto com cada propriedade, em ordem, separado por vírgulas.

Estes métodos evocam equals, hashCode, toString em todas as propriedades de um objeto. Isso pode levar a efeitos colaterais indesejados para entidades JPA: acidentalmente carregar atributos que são "Lazy".

Por exemplo, invocar hashCode em uma propriedade @OneToMany que é lazy pode carregar todas as entidades que ele contém. Isto pode facilmente prejudicar a performance da aplicação. Isso pode levar também à exceção LazyInitializationException se isto acontece fora de uma transação.

Nós acreditamos que @EqualsAndHashCode e @Data não devem ser usadas para entidades por completo. Neste caso, JPA Buddy emite um aviso para os usuários:

Image description

@ToString pode continuar a ser usado, mas todas propriedades que são "lazy" precisam ser excluídas. Basta colocar @ToString.Exclude nas propriedades que precisam excluídas, ou então usar @ToString(onlyExplicitlyIncluded=true) na classe e anotar as propriedades que não são "lazy" com @ToString.Include. JPA Buddy tem um quick fix para isso:

Image description

Falta de um construtor sem argumentos

De acordo com a especificação do JPA, é obrigatório que todas as entidades tenham um construtor sem argumentos que seja public ou protected. Obviamente, quando @AllArgsConstructor é utilizado, o compilador não gera o construtor padrão. O mesmo vale também para @Builder.

[tradução direta] Anotar uma classe com @Builder temo mesmo efeito que anotar ela com @AllArgsConstructor(access=AccessLevel.PACKAGE), e, juntamente com isso, anotar o construtor gerado com @Builder

Então tenha certeza de sempre utilizar @NoArgsConstructor ou escreva um construtor explicitamente:

Image description

Conclusão

Lombok faz seu código parecer melhor, mas como qualquer ferramenta "mágica", é importante entender como exatamente ela funciona e quando usar ela. Você pode contar com ferramentas de desenvolvimento para antecipar potenciais problemas para você. Ou então, você pode acidentalmente prejudicar a performance da aplicação, ou mesmo quebrar tudo.

Ao trabalhar com JPA e Lombok, lembre-se sempre dessas regras:

  • Evite usar @EqualsAndHashCode e @Data com entidades JPA;
  • Sempre exclua atributos "lazy" quando usar @ToString;
  • Nunca se esqueça de adicionar @NoArgsConstructor a entidades com @Builder ou @AllArgsConstructor.

[Última frase do texto excluída deliberadamente].

Top comments (0)