Back-End

8 mar, 2017

Como usar o Optional do Java 8 com a JPA (Hibernate)

Publicidade

Finalmente, temos em mãos um facilitador para evitar o velho NullPointerException. O Optional é um novo recurso no mundo Java; tenha em mente como uma espécie de caixa que pode ser preenchida ou não, caso possua algo. Então, é possível utilizar vários recursos que nos auxiliam no manuseio desse conteúdo; caso a caixa não esteja preenchida, ao menos podemos evitar, sem nenhum esforço, o bendito/maldito erro do NullPointerException.

Você já tentou utilizar o Optional como um atributo de uma entidade? Se sim, provavelmente aconteceu o erro (será explicado durante o texto) e sua aplicação mal subiu. Então, “Como é que utilizo o Optional com a JPA?”, “Por que não posso utilizá-lo como atributo na minha entidade?”, “Para que ele serve?”. Pretendo responder essas perguntas e te prometo que não serei chato.

Falar é fácil, me mostre o código!

Para aqueles que preferem ir direto para o código, aqui esta o link do repositório no GitHub.

Domínio da nossa mini aplicação

Como fazíamos o mapeamento antes do Optional?

@Entity
 public class WeightHistory implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(updatable = false, nullable = false)
    private Long id;
    @Version
    @Column
    private int version;
    @Column(nullable = false)
    private BigDecimal weight;
    @ManyToOne
    private Person person;

    public Long getId() { return this.id; }

    public int getVersion() { return this.version; }

    public BigDecimal getWeight() { return weight; }

    public Person getPerson() { return person; }

Neste texto, vamos analisar o uso do Optional com o atributo Person da entidade acima, esse atributo foi escolhido por ser o único na classe que pode ser nulo.

No que o Optional vai te ajudar?

Esse recurso vai te ajudar a diminuir a quantidade de NullPointerException na sua aplicação/API por causa da possibilidade de trabalhar com objetos sem valor.

Para isso, você só precisa trabalhar com funções que retornem o tipo Optional e não recomendo usá-lo diretamente nos atributos de suas entidades/DTOs. O Optional foi planejado para ser um recurso utilizado como tipo de retorno para funções, serviços, DAOs, repositórios etc. A JSR-335 definiu que o Optional não seria serializável, logo ele é um novo tipo que serve como suporte na linguagem – essa foi uma escolha planejada pelos arquitetos da linguagem.

Caso queria ler mais sobre essa discussão de ser ou não serializável, você pode acessar a mail list da openjdk. Também recomendo a leitura desse texto do Ivan Queiroz, que explica com vários exemplos o uso mais detalhado do Optional.

Onde você deve evitar usar o Optional?

O Optional não deve ser sua bala de prata para evitar todos os NullPointerException da sua aplicação/API, mas deve ser encarado como um facilitador. A JSR-335 não recomenda que você utilize o recurso nos contextos abaixo:

  • Como atributo da sua entidade (lembre-se ele não é serializável);
  • Como atributo do seu DTO (lembre-se ele não é serializável);
  • Como parâmetro das suas funções;
  • Como parâmetro nos construtores.

Qual o melhor formar até agora de usar o recurso?

@Entity
 public class WeightHistory implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(updatable = false, nullable = false)
    private Long id;
    @Version
    @Column
    private int version;
    @Column(nullable = false)
    private BigDecimal weight;
    @ManyToOne
    private Person person;

    public Long getId() { return this.id; }

    public int getVersion() { return this.version; }

    public BigDecimal getWeight() { return weight; }

    public Optional<Person> getPerson() { return Optional.ofNullable(person); }

Sim, a solução é simples mesmo. Não podemos utilizá-lo como atributo na nossa classe Person devido a tudo que foi explicado acima, mas podemos utilizá-lo como tipo de retorno do método get().

Caso queria praticar o uso do Optional, você pode ir lá no repositório no GitHub e hackear o código. Tente usar o Optional como atributo da classe e tente subir a aplicação.