quarta-feira, 11 de março de 2015

Gestão de atribuição de chaves primárias - ORACLE / JPA

No artigo anterior, usámos o JPA para criar uma camada de acesso a uma BD ORACLE.
A BD ORACLE, cuja criação foi tratada em "Exemplo de Aplicação Java Swing com BD Oracle - 1", usa sequências e triggers para atribuição automática da chave em cada tabela.

O JPA permite-nos fazer operações sobre a BD (criar, alterar, apagar ou selecionar registos das tabelas). No entanto, quando queremos fazer várias operações relacionadas, e precisamos de saber que chave foi atribuída pela BD a um dado registo criado anteriormente, cujo objeto ainda temos em memória, não temos maneira segura de conhecer o valor dessa chave. E, o objeto que ainda temos em memória permanece sem o valor da chave nos seus atributos, fazendo com que o JPA considere que essa instância não é gerida por si. Isto, impede-nos de usar essa instância para operações subsequentes com a BD.

O ORACLE, tipicamente usa sequências para atribuição de valores de chave primária nas suas tabelas.
A utilização dessas sequências na atribuição da chave primária pode fazer-se definindo um default value para a coluna respetiva, ou criando um trigger before insert o qual obtém o valor seguinte da sequência e o atribui à coluna da chave primária da tabela.



Qualquer Entidade JPA (classe associada a uma tabela) deve ter uma chave primária. Para fazer o JPA atualizar o valor da chave na instância da Entidade, quando essa instância é persistida (armazenada) na BD, precisamos de deixar que seja o JPA a gerir a atribuição dos valores a essas chaves.

Assim, temos várias hipóteses de configuração da Entidade JPA:
  • Campo de chave primária simples: Utiliza @Id
  • Campo de chave primária gerado automaticamente: Utiliza @Id e @GeneratedValue. Aqui temos, pelo menos, duas alternativas:
    • Gerar ID a partir de uma tabela
    • Gerar ID a partir de uma sequência
Para informação sobre estas e outras alternativas, pode-se consultar "Configuring a JPA Entity Primary Key".


Vamos aqui usar a alternativa de geração do ID a partir de uma sequência.
Ao gerar as entidades JPA a partir da BD ORACLE, no artigo anterior, para cada chave primária foi gerada a seguinte anotação de chave primária simples:

    @Id
    @Basic(optional = false)
    @Column(nullable = false)
    private Integer bookid;


(exemplo na Entidade Book, associada à tabela BOOK)

Neste caso, o JPA não gera nem faz a gestão das chaves primárias, não atualizando o atributo chave nas instâncias de entidades JPA que persista na BD.
Para deixar ser o JPA a gerir e gerar as chaves primárias a partir de uma sequência criada na BD ORACLE, precisamos de eliminar o trigger que atribui a chave, e colocar as seguintes anotações JPA em substituição das anteriores:

    @SequenceGenerator(
        name="BOOK_SEQUENCE_GENERATOR",
        sequenceName="BOOK_SEQ",
        allocationSize=25
    )
    @Id
    @GeneratedValue(strategy=SEQUENCE, generator="BOOK_SEQUENCE_GENERATOR")
    @Column(name="BOOKID", nullable = false)
    private Integer bookid;



O que fizemos foi:
   1º - Usando @SequenceGenerator definir um Generator de chaves a partir da sequência identificada (neste caso BOOK_SEQ);
   2º - Usando @Id @GeneratedValue dizer ao JPA que a chave deve ser gerada usando o SequenceGenerator definido acima.

Após fazer esta alteração em BOOK e uma alteração similar em AUTHOR, passa a ser possível fazer algo como o seguinte, no exemplo do artigo anterior:

  em.getTransaction().begin();
  Author au = new Author();
  au.setName("Sousa");
  au.setSurname("Tavares");
 
em.persist(au);

  //imprimir o valor da PK criada na BD
  System.out.println(au.getIdauthor());

  Book nbook = new Book();
  nbook.setTitle("Equador");
  nbook.setIsbn("12345");
  nbook.setEdition(3);
  nbook.setAuthorSet( new HashSet<>());
  nbook.getAuthorSet().add(au);
  em.persist(nbook);

  em.getTransaction().commit();

Este excerto de código irá criar um autor na tabela AUTHOR, um livro na tabela BOOK, e um registo associando o autor ao livro em BOOKAUTHOR.

No próximo artigo iremos criar um projeto Java Class Library usando as entidades JPA que criámos anteriormente, a qual pretenderá fornecer funções/serviços de lógica do negócio (BLL, Business Logic Layer) para uma camada de User Interface.


Sem comentários:

Enviar um comentário