JPA – Java Persistence API

A Java Persistence API é sensacional. No começo eu não ia com a cara dela, mas agora ela está muito madura e funciona redondinha que nem um coco. Eu tenho uma grande dificuldade com ela, as anotações para Join e para objetos que não são uma tabela. Toda vez que preciso utilizar, busco algo que já fiz, ou tenho que sair vasculhando a WEB. Então vou colocar aqui meu personal guia de referencia JPA! Se eu conseguir superar a preguiça, coloco também um pouquinho das anotações mais básicas, para os newbies.

Uma conceito muito simples, porém muito importante na hora de fazer os relacionamentos é saber se o mesmo será Unidirecional ou Bidirecional.

Unidirecional

Apenas uma dos lados(classe) conhecerá a outra.

Bidirecional

Se o unidirecional apenas um lado conhece o outro no Bi é …..Joãozinho: “Bisexual professor!!!”, não Joãozinho. No Bidirecional, ambos os lados se conhecem!
Perigo, Perigo, Perigo!!!!467025.jpg-c_620_260_x-f_jpg-q_x-xxyxx
Em um relacionamento Bidirecional, um dos lados deve ser o mandante obrigatoriamente. Não podem ambos serem mandantes, e também não pode ficar sem 1 e apenas 1 mandante. Estou frisando isso pois em todo o lugar que eu li sobre esse assunto sempre frisavam isso, então, algo de muito ruim pode acontecer caso essa lei não seja seguida, e como colocar um pão com manteiga nas costas de um gato e jogá-lo no chão, ninguém sabe o que poderá ocorrer, mas que não será bom, isso te garanto que não será!

@OneToOne – Unidirecional

Veremos abaixo o relacionamento 1 para 1(@OneToOne) unidirecional:

@Entity
public class Pessoa{
@Id
@GeneratedValue
private int id;
private String nome;

@OneToOne
@JoinColumn(name="id_celular")
private Celular celularX;
}

@Entity
public class Celular {
@Id
@GeneratedValue
private int id;
private int numero;

}
Ou seja, a tabela da Pessoa possui um campo ID_CELULAR, que é a chave estrangeira para a tabela Celular. A tabela Celular, NÃO possui um campo ID_PESSOA(por exemplo) para que ela possa utilizar para encontrar uma Pessoa.
A anotação @JoinColumn, indica que a chave estrangeira ficará dentro da própria tabela (Pessoa), fazendo com que a entidade Pessoa seja a dona do relacionamento.

@OneToOne – Bidirecional

Chamei a propriedade celular da classe Pessoa de celularX, para ficar bem claro agora que na propriedade mappedBy da anotação OneToOne o valor do campo é o nome da Propriedade, e não o nome do campo na Tabela.

Óbvio que faltou e faltará adicionar os getters and setters, mas não vou fazer-los para não ficar enchendo linguiça, pois se alguém ainda tem dúvida nisso, proibi-los-ei de continuar a ler este post! Não gostou cai dentro!!! Kkkkkkkk.
@Entity
public class Pessoa{
    @Id
    @GeneratedValue
    private int id;
    private String nome;

    @OneToOne
    @JoinColumn(name="id_celular")
    private Celular celularX;
}

@Entity
public class Celular {
    @Id
    @GeneratedValue
    privateint id;
    privateint numero;

    @OneToOne(mappedBy="celularX")
    private Pessoa pessoa;

}

Para lembrar

@JoinColumn – Coluna a ser utilizado no Join(Ligação) entre as tabelas, refere-se a entidade, classe, e consequentemente à tabela onde a anotação foi feita.

(mappedBy=””) – Tradução : Mapeado Por, ou seja mapeado por alguma outra coisa. Logo refere-se a entidade, classe, e consequentemente tabela que está abaixo da anotação que contém a propriedade de anotação mappedBy. O valor do mappedBy é o nome da propriedade na outra entidade(classe), que se relaciona com a entidade(classe) atual, onde o mappedBy está.

OneToMany e ManyToOne

ManyToOne

  • Utilizaremos novamente a anotação @JoinColumn para definir quem será o dono do relacionamento.
  • O lado do relacionamento que tiver a anotação @ManyToOne será sempre dominante.
  • Não existe a opção de utilizar mappedBy dentro da anotação @ManyToOne.
@Entity
public class Ligacao{
 @Id
 @GeneratedValue
 private int id;

 @ManyToOne
 @JoinColumn(name = "id_celular")
 private Celular celular;

 private long duracao;
}

OneToMany

 Para tornar o relacionamento Bidirecional vamos alterar a classe celular.
  • Utilizamos a anotação @OneToMany sempre sobre uma coleção.
  • Utilizamos o mappedBy para indicar que esse relacionamento NÃO é o lado dominante.

@Entity
public class Celular {
@Id
    @GeneratedValue
    private int id;
    private int numero;

    @OneToOne(mappedBy="celularX")
    private Pessoa pessoa;

    @OneToMany(mappedBy = "celular")
    private List listaLigacoes;

}
OBSERVAÇÃO: Todo o relacionamento necessita que umas das entidades seja a dona do relacionamento, ou seja, que tenha a chave estrangeira na tabela do banco de dados. No exemplo acima é possível ver na classe Ligação a utilização da anotação @JoinColumn que indica que a chave estrangeira ficará dentro da tabela Ligacao, e o atributo mappedBy deve apontar para o nome do atributo e não para o nome da classe.

Não existe relacionamento automático

Para que um relacionamento bidirecional funcione corretamente é necessário fazer o set na propriedade dos dois objetos, e não apenas um e assumir que alguém fará o reverso para você automaticamente. Exemplo:


Pessoa pessoa = new Pessoa();
Celular celular = new Celular();

pessoa.setCelularX(celular);
celular.setPessoa(pessoa);
entityManager.persist();

Chave Primária

@Id

Para definição de Ids de tabela no banco temos duas principais formas. Atualmente o Oracle e o Postgres trabalham com Sequence, o Sql Server e o MySQL trabalham com Identity.

Identity

Esse é o tipo de geração automática mais simples que existe, basta anotar o atributo id, com @GeneratedValue(strategy = GenerationType.IDENTITY). Utilizado pelo SQL Server e o MySQL. Exemplo:

@Entity
public class Celular {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private int numero;
}

Sequence

O tipo de geração de id por Sequence, é o utilizado pelo Oracle e pelo PostgreSQL, utilizando a anotação@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "NOME_DA_SEQUENCE"). Exemplo:

@Entity
@SequenceGenerator(name ="QUALQUER NOME", sequenceName = "NOME_DA_SEQUENCE_NO_BANCO", allocationSize = 1)
public class Celular {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "QUALQUER NOME")
private int id;
private int numero;

Primeiramente defina o gerador de sequencia nas anotações da classe, configure o nome da Sequence no banco e mais algumas poucas propriedade se quiser e dê um apelido. Em seguida na propriedade com a anotação @Id, adicione outra anotação, a @GeneratedValue, configurando a strategy(do grego “estrategí”) para Sequence e na propriedade generator, utilize o apelido utilizado na anotação @SequenceGenerator no início da entidade.

@ConstructResult – Mapeando SQL complexos para um POJO

Esse aqui foi um inferno descobrir como fazer. Eu sempre fiz da maneira errada utilizando  a anotação @Entity em um POJO e deixando a opção de validação do hibernate desligada, senão dava erro, e demorou para descobrir, só aprendi quando troquei de emprego. Então, saí em uma caçada pela forma correta de fazer isso e enfim…

Primeiro você cria um pojo simplão e adiciona um construtor com todos os campos do pojo e que serão retornados na query.

public class ResultTesteDTO implements Serializable{

private static final long serialVersionUID = 6873051541292059290L;

public ResultTesteDTO(Integer idProduto, Integer valorTotal) {
  super();
  this.idProduto = idProduto;
  this.valorTotal = valorTotal;
}

public Integer idProduto;

public Integer valorTotal;

public Integer getIdProduto() {
  return idProduto;
}

public void setIdProduto(Integer idProduto) {
  this.idProduto = idProduto;
}

public Integer getValorTotal() {
  return valorTotal;
}

public void setValorTotal(Integer valorTotal) {
  this.valorTotal = valorTotal;
}
}
 Depois você precisa adicionar a anotação @SqlResultSetMapping, com o @ConstructorResult e de preferência passando os tipos das colunas com @ColumnResult para garantir. MAS, onde adicionar esta anotação???? No caso do Hibernate, você deve adicionar em alguma classe com @Entity(uma de verdade, não vá anotar o próprio POJO) do seu projeto. No caso do EclipseLink, pode ser em qualquer classe do classpath. Segue exemplo abaixo:
@SqlResultSetMapping(name = "QueryComplexa", classes = {
    @ConstructorResult(targetClass = ResultTesteDTO.class,
          columns = {
               @ColumnResult(name = "id_produto", type=Integer.class),
               @ColumnResult(name = "TOTAL", type=Integer.class)
           })
})
Agora é só criar a native query, LEMBRANDO de passar no segundo parâmetro o nome do @SqlResultSetMapping, que no caso aqui é  “QueryComplexa“.
 public List  listaTeste(final Long idProduto) {

 final Query query = em.createNativeQuery("select p.id_produto, count(*) TOTAL from produto p\n"+ "group by p.id_produto", "QueryComplexa");

 final List<ResultTesteDTO>  lista = query.getResultList();

return lista;
}

Aaaaaaaaaaaaaaaaaaaaaaaeeeeeeeeeeeeeeeeeeeeeeeeeeeeee!!!!!!!!!!!!!!!!!!

Ah, meus sinceros agradecimentos: http://stackoverflow.com/questions/29636004/where-to-place-sqlresultsetmapping-in-case-of-constructorresult.

Agora que conheço a forma teoricamente correta de se fazer isso, percebo a outra forma era mais elegante e simples de implementar, bem mais elegante e simples por sinal. Segue abaixo a forma como fazia anteriormente:

@Entity
@SqlResultSetMapping(name = "ResultTesteDTO", entities = @EntityResult(entityClass = ResultTesteDTO.class))
public class ResultTesteDTO implements Serializable{

private static final long serialVersionUID = 6873051541292059290L;

public static final String sql = "select p.id_produto, count(*) total from produto p group by p.id_produto";&amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;@Id

@Column(name = "id_produto")
public Integer idProduto; 

@Column(name = "total")
public Integer valorTotal; 

} 
 public List  listaTeste(final Long idProduto) {

 final Query query = em.createNativeQuery("select p.id_produto, count(*) TOTAL from produto p group by p.id_produto", ResultTesteDTO.class);

final List<ResultTesteDTO> lista = query.getResultList();

return lista;
} 
  • Anotava o proprio DTO com @Entity;
  • Anotava com @SqlResultSetMapping, configurando a própria classe e dando um nome quaquer;
  • Anotava as propriedades com @Column, utilizando os campos do SQL, que já mantinha na própria classe;
  • Usaria nas consultas como se fosse uma entidade normalmente;

Guia de Referência

@Table(name=”nome da tabela”) – Uma tabela e seu nome

@Id – Chave primária

@SequenceGenerator(name =”QUALQUER NOME”, sequenceName = “NOME_DA_SEQUENCE_NO_BANCO”, allocationSize = 1) – Define o gerador de sequência e seu apelido

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = “QUALQUER NOME”) – Informa qual o gerador de sequencia será utilizado

@OneToOne(mappedBy=”nome da propriedade”) – Relacionamente UmParaUm

mappedBy=”nome da propriedade” – Utilizado quando o mapeamento não é o lado dominante.

@OneToMany – Relacionamento UmParaMuitos

@ManyToOne – Relacionamento MuitosParaUm. Não pode utilizar mappedBy.

@JoinColumn – indica que a chave estrangeira ficará dentro da própria tabela (Pessoa), fazendo com que a entidade Pessoa seja a dona do relacionamento. Utilizado no @OneToOne ou no @OneToMany

@SqlResultSetMapping(name = “Nome da Query”, classes = { @ConstructorResult(targetClass = AlgumPOJO.class, columns = { @ColumnResult(name = “nome da coluna”, type=Integer.class) }) }) – Mapeando um POJO qualquer para ser utilizado para mapear uma query complexa.

Bibliografia

http://uaihebert.com/jpa-mini-livro-primeiros-passos-e-conceitos-detalhados/19/

http://stackoverflow.com/questions/29636004/where-to-place-sqlresultsetmapping-in-case-of-constructorresult

http://stackoverflow.com/questions/25179180/jpa-joining-two-tables-in-non-entity-class/25184489#25184489

2 comentários em “JPA – Java Persistence API

  1. hauahauhauahua… seu post além de estar super informativo, está muito engraçado! ahahahaha

    Pastei tanto com esse trem lá na Certisign… mas até que era divertido…

    Parabéns, Marcelo! Certeza que isso vai ajudar muitas pessoas!

    beijos

    Curtir

    1. Lendo te dá saudades ou depressão? Kkkk
      Acho que já sei a resposta. Agora você deve se preocupar com fisiológica, nutrição, …..
      Obrigado pelo apoio, preciso escrever senão esqueço tudo.
      Ah, peguei meu diploma essa semana lá na USJT! Deu até saudade, mas passou bem rápido kkkkk.

      Curtir

Deixe um comentário