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