Solucionado (ver solução)
Solucionado
(ver solução)
3
respostas

Como invocar uma stored procedure no hibernate?

Olá pessoal,

Eu estou usando hibernate para mapear umas tabelas de uma aplicação que rosa em banco Oracle. Existe uma tabela no schema da aplicação que a PK é obtida a partir de uma stored procedure (na verdade uma função de banco). Essa função só retorna um NUMBER. A minha dúvida é como mapear essa função no Hibernate para que seja possível atualizar a PK no momento em que vou persistir os dados no banco? Pelo o que eu já andei estudando, não posso utilizar as anotações GeneratedValue, SequenceGenerator e TableGenerator, já que essas são as anotações do hibernate para trabalhar com sequências de banco ou com o suporte de uma tabela para geração de valores de forma automática para PKs.

Desde já, agradeço pela ajuda e atenção. Anderson

3 respostas

Fala ai Anderson, de boa ?

Cara você pode usar um recurso que é escrever a query nativa ai pode usar a procedure na mão.

Acho que pode ser interessante.

Olá Matheus,

Eu fiz o seguinte dentro da classe de mapeamento da tabela. No método "setId(Long seqReg)", eu forcei a execução de uma consulta para executar a função, conforme código abaixo:

    public void setId(final Long seqReg) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("ApplicationDS");
        EntityManager em = emf.createEntityManager();

        this.seqReg =
                (Long)em.createNativeQuery(
                        "SELECT pckp8100.gera_seq_chave FROM dual"
                        ).getSingleResult();

        em.close();
        emf.close();
    }

Eu não gostei da implementação, pois acredito que exista uma forma mais elegante/certa de resolver isso.

Alguma sugestão?

Obrigado, Anderson

solução!

Encontrei a forma correta de implementar a chamada de uma função, ou procedure ou package do Oracle, usando JPA.

Como a aplicação que desenvolvo é implantada em um servidor de aplicação, especificamente o WildFly 10.1.0, todo o mapeamento de tabelas estão divididas em dois packages: (i) entity, e (ii) DAO. Com essa estrutura, o JPA fica encarregado de fazer a manipulação das tabelas do modelo da aplicação. Ocorre que hierarquia de classes, relacionadas com o mapeamento das tabelas, parte de uma classe abstrata chamada BaseDAO que possui um objeto privado do tipo EntityManager e, que por sua vez, é anotado com @PersistenceContext(name = "ApplicationDS"). As classes DAO extendem a classe abstrata BaseDAO , permitindo ao JPA maninupa-las de forma correta, ou seja, com toda abstração de acesso ao banco que a tecnologia oferece. Sendo assim, na classe DAO que mapeia a tabela, cuja a coluna da PK é inicializada com um valor que retorna de uma função de banco, tive que implementar um novo método para executar tal função, a qual é encapsulada em um package compilado no Oracle. Segue o código do método:

    public Long getGeraSeqChave() {
        // Prepara a chamada da procedure MEUPACKAGE.GERA_SEQ_CHAVE_JPA.
        StoredProcedureQuery query =
                getEntityManager().createStoredProcedureQuery("meupackage.gera_seq_chave_jpa")
                .registerStoredProcedureParameter(1, Long.class, ParameterMode.OUT);

        // Executa o package no banco de dados.
        query.execute();

        // Recupera o valor de retorno, armazenado no parâmetro do tipo OUT da
        // procedure MEUPACKAGE.GERA_SEQ_CHAVE_JPA.
        return (Long) query.getOutputParameterValue(1);
    }

No lado do PL/SQL, eu tive que criar uma procedure "GERA_SEQ_CHAVE_JPA" dentro do package MEUPACKAGE, pois createStoredProcedureQuery trabalha com procedures, onde a passagem de parâmetros e o retorno de valores são feitos por acoplamento de dados.

Segue o exemplo do código PL/SQL da procedure "gera_seq_chave_jpa"

-- Declaração do package specification
CREATE OR REPLACE PACKAGE  meupackage AS
...
FUNCTION gera_seq_chave
   RETURN NUMBER;

PROCEDURE gera_seq_chave_jpa
    (
      p_seq_chave OUT NUMBER
    ); 
...
END meupackage;

-- Implementação do package 
CREATE OR REPLACE PACKAGE  BODY meupackage AS

FUNCTION gera_seq_chave
   RETURN NUMBER
    IS
BEGIN
   <CÓDIGO>
  RETURN <valor>;
END gera_seq_chave;

   -- Essa procedure é chamada dentro do projeto Java
    PROCEDURE gera_seq_chave_jpa
    (
      p_seq_chave OUT NUMBER
    )
    IS
    BEGIN
      -- Faz uma chamada da função gera_seq_chave
      -- O valor é retornado para a aplicação Java através
      -- da atualização do parâmetro formal "p_seq_chave".
       p_seq_chave := gera_seq_chave;
    END gera_seq_chave_jpa;

BEGIN
   null;
END meupackage;

No que diz respeito ao DAO, as classes já encapsulam a conexão com o banco de dados por conta de hierarquia criada, o que me permitiu obter a conexão com o banco através do método getEntityManager(), que retorna uma instância de EntityManager. Portanto, foi só utilizar o método "createStoredProcedureQuery" para fazer a chamada da procedure dentro do package. Por fim, o resultado da execução da procedure é recuperado através do método "getOutputParameterValue(1)", onde 1 é para informar ao JPA que é o primeiro parâmetro que queremos recuperar (no meu caso só existe um parâmetro).

Espero que esse post seja útil para outros desenvolvedores iniciantes em JPA.

Referências: https://vladmihalcea.com/how-to-call-oracle-stored-procedures-and-functions-from-hibernate/ https://www.thoughts-on-java.org/hibernate-tips-call-stored-procedure/

Muito obrigado pela atenção e ajuda dos participantes do fórum.

Anderson Bestteti