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.