Spring Data JPA : Stored Procedure with Schema Name - oracle

Please if anyone has experienced calling oracle stored procedure from spring data specifying schema, package and procedure name.
I have the following entity :
#Entity
#Table(name = "ENTITY", schema = "SCHEMA_ENTITY")
#NamedStoredProcedureQueries({
#NamedStoredProcedureQuery(name = "name1",
procedureName = "packageName.procName",
parameters = {
#StoredProcedureParameter(mode = ParameterMode.IN, name = "param1", type = String.class),
#StoredProcedureParameter(mode = ParameterMode.OUT, name = "return_value", type = BigDecimal.class)
})})
public class EntityExp {
#Id
private Long keyId;
...
}
Repository :
public interface EntityRepository extends JpaRepository<EntityExp, Long> {
#Procedure(name = "name1")
BigDecimal test(#Param("param1") String param1);
}
In the service implemetation, after autowiring it, I call the procedure like :
BigDecimal returnVal = entityRepository.test(param1);
The oracle stored proc definition is :
create or replace PACKAGE packageName as
function procName(param1 IN VARCHAR)
RETURN NUMBER;
END packageName;
create or replace PACKAGE BODY packageName
IS
function procName (param1 IN VARCHAR)
RETURN NUMBER
IS
BEGIN
return 1;
END;
END;
The procedure works fine if I call it using PL/Sql..
And I got the following error :
PLS-00201: identifier 'package.procName' must be declared
I also tested many configs, like specifying the schema in the procedure:
#Procedure(name = "SCHEMA_ENTITY.name1")
BigDecimal test(#Param("param1") String param1);
But still fails...
I can't find any example using schema + package + procedureName while calling the stored procedure...
Any suggestions ?

I'm not quite sure that understood your issue completely, but I'll try to share some information since I had a related issue recently.
The first item is related to the way you call the stored procedure. It looks like the returned type of the stored procedure is not currently supported by Spring-data. For more info link. However, with void it is working fine.
I resolved that by creating a separate Repository where I call the procedures which return result through the entityManager. An example which illustrates that link.
The second item is about schema name. I believe when you resolve the first item you could use this question to try to resolve it. In a nutshell, I set the schema name in the procedureName annotation attribute name.
#NamedStoredProcedureQuery(
name="procName",
procedureName="<schema_name>.proc_name"
)
#Entity
#Table
public class User {
...
}
Hope it helps.

Related

Calling Stored Procedure having Table/TableType as In/OUT params from Springboot

I am following this guide for calling my stored procedure. I searched around for other resources for an example where IN and OUT parameters are of type table but couldn't find one.
I am having error: Cannot convert SQL type TABLE to Java type java.lang.Object
There are no ways to know what exactly caused the problem. But I'm giving an example of a stored function that actually worked for me. Stored functions could be an alternative approach to stored procedures which need to return DTOs.
#Repository
public interface CustomRepository extends CrudRepository<YourObject, UUID> {
#Query(value = "SELECT * from your_stored_procedure(:key)", nativeQuery = true)
List<YourObject> findAlldata(#Param("key") String key);
}
#Service
public class CustomService {
#Autowired
private CustomRepository customRepository;
public List<YourObject> getAllData(String key) {
List<YourObject> yourObjects = this.customRepository.findAlldata(key);
}
}
And here is the code for the stored function in Postgres. The return objects should have a similar structure for both the repository and the procedure.
CREATE FUNCTION public.your_stored_procedure(key text) RETURNS TABLE(id uuid, name character varying)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY SELECT
id,
name
FROM public.your_table;
END;
$$;

wrong number or types of arguments while calling Stored Proc

I am calling Stored Proc from Spring Data JPA :
Procedure is:
create or replace procedure GET_LATEST_GC (arg1 IN VARCHAR2, res1 OUT VARCHAR2, res2 OUT VARCHAR2)
AS
BEGIN
DELETE FROM GC_T WHERE id = arg1;
COMMIT;
BEGIN
SELECT gc.NAME, s.SIP INTO res1, res2
FROM GC_T gc, STAFF_T s WHERE s.id = gc.id
AND START_TIME = (SELECT MAX(START_TIME) FROM GC_T);
EXCEPTION
WHEN others THEN
res1 := '';
END;
END;
Spring Data JPA code
//Repository
public interface ActiveDao extends JpaRepository<GcT,Integer> {
#Procedure(procedureName="GET_LATEST_GC")
Object[] plus1(#Param("arg1") String arg1);
}
//Entity
#Data
#Entity
#NamedStoredProcedureQuery(name = "GET_LATEST_GC",
procedureName = "GET_LATEST_GC", parameters = {
#StoredProcedureParameter(mode = ParameterMode.IN, name = "arg1", type = String.class),
#StoredProcedureParameter(mode = ParameterMode.OUT, name = "res1", type = String.class),
#StoredProcedureParameter(mode = ParameterMode.OUT, name = "res2", type = String.class)})
#Table(schema = "abc", name = "GC_T")
public class GcT implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
private String id;
#Column(name = "NAME")
private String name;
}
//Call
Object[] activeGCInfo =activeDao.plus1(arg);
Procedure is accepting one parameter and I am also passing 1 argument.Then also I am getting this error:
Hibernate: {call GET_LATEST_GC(?,?)}
ERROR o.h.e.jdbc.spi.SqlExceptionHelper - ORA-06550: line 1, column 7:\nPLS-00306: wrong number or types of arguments in call to 'GET_LATEST_GC'\nORA-06550: line 1, column 7:\nPL/SQL: Statement ignored\n
Please let me know where I am doing wrong.
Thank you
Update- Tried to add OUT params also as per suggestion
//Repo
public interface ActiveDao extends JpaRepository<GcT,Integer> {
#Procedure(procedureName="GET_LATEST_GC")
Object[] plus1(#Param("arg1") String arg1,#Param("res1") String res1,#Param("res2") String res2);
}
//Call
Object[] activeGCInfo =activeDao.plus1(arg,"","");
I am sending three args but it is showing me 4 args in error:
Hibernate: {call GET_LATEST_GC(?,?,?,?)} SqlExceptionHelper - ORA-06550: line 1, column 7:\nPLS-00306: wrong number or types of
arguments in call to 'GET_LATEST_GC'\nORA-06550: line 1, column
7:\nPL/SQL: Statement ignored\n
Try changing the result from Object[] to Map<String, Object, along with referencing the proc name with name instead of procedureName. Based on the error, I'm not sure that it will fix it. Spring Data JPA does expect a Map as the return value for multiple output params, so each output param can be found as the key in that Map. But I think the main error is that procedureName maps directly to the db, but name= will map to the correct Entity
//Repo
public interface ActiveDao extends JpaRepository<GcT,Integer> {
#Procedure(name="GET_LATEST_GC")
Map<String, Object> plus1(#Param("arg1") String arg1);
}
//Call
Map<String, Object> activeGCInfo =activeDao.plus1(arg);
Here's what happened:
you declared a procedure with 3 parameters: 1 in and 2 out
you said: "Procedure is accepting one parameter and I am also passing 1 argument"
that was the 1st procedure's parameter (arg1 IN)
it results in "PLS-00306: wrong number or types of arguments"
Of course it does; you need to provide 2 more arguments (datatype should be able to accept VARCHAR2 values returned by the procedure).

Error calling CallableStatement.getMoreResults. Calling Stored Procedure using JPA Repository

I use this page to guide myself on how to do it
https://dzone.com/articles/calling-stored-procedures-from-spring-data-jpa
I need to call a stored procedure in an Oracle DB, using Hibernate and Repositories. My procedure receives 1 IN parameter and 2 OUT parameters, I don't really use the OUT parameters in my application.
My procedure receives this
create or replace procedure PRD_DIC_SISTEMA(P_NRO_EVALUACION number
,P_DICTAMEN out varchar2
,P_RESPUESTA out varchar2) is
My service
public ResultsDTO getSystemEvaluation(Long evaluationCode) throws BusinessException {
ResultsDTO response = new ResultsDTO();
dictaminationOperationRepository.systemDictaminationResults(evaluationCode);
return response;
}
Repository
public interface DictaminationOperationRepository extends CrudRepository<DictaminationOperation,DictaminationOperationPK>{
#Transactional
#Procedure(procedureName="prd_dic_sistema")
void systemDictaminationResults(#Param("p_nro_evaluacion") Long evaluationCode);
}
My bean class
#Entity
#Table(name="WP_EVA_DIC_OPERACIONES")
#JsonInclude(Include.NON_NULL)
#NamedStoredProcedureQueries({
#NamedStoredProcedureQuery(
name = "prd_dic_sistema",
procedureName = "prd_dic_sistema",
parameters = {
#StoredProcedureParameter(mode = ParameterMode.IN, name = "p_nro_evaluacion", type = Long.class),
#StoredProcedureParameter(mode = ParameterMode.OUT, name = "p_dictamen", type = String.class),
#StoredProcedureParameter(mode = ParameterMode.OUT, name = "p_respuesta", type = String.class)
}
)
}
)
public class DictaminationOperation implements Serializable {
attributes, constructor, getters and setters...
}
My error
Error calling CallableStatement.getMoreResults; SQL [prd_dic_sistema];
wrong number of types of arguments in call to 'PRD_DIC_SISTEMA'

Spring Data JPA + Oracle Trigger increments the ID twice

I use the following tech stack:
spring-boot-starter-data-jpa
HikariCP for connection pooling
Oracle DB
My actual code looks similar to this.
/// My trigger looks like this
CREATE OR REPLACE TRIGGER FILE_BRI
BEFORE INSERT
ON FILE
FOR EACH ROW
BEGIN
SELECT FILE_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
END;
///
public class FILE implements Serializable {
#Id
#SequenceGenerator(
name = "FILE_SEQ",
sequenceName = "FILE_SEQ",
allocationSize = 1)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "FILE_SEQ"
)
private long id;
}
public class ServiceA () {
#Transactional(propagation = REQUIRES_NEW, isolation = READ_COMMITTED)
public File insertFile() {
// Below line returns the inserted File object with ID as '58496'
return fileRepository.save(file)
}
#Transactional(propagation = REQUIRES_NEW, isolation = READ_COMMITTED)
public AccessControl insertAccessControl() {
// Below line results in 'SQLIntegrityConstraintViolationException' (full error at the bottom of this post)
return accessControlRepository.save(accessControlFile)
}
}
Public class FileProcessor() {
ServiceA serviceA;
public void someMethod() {
// insert the file and get the inserted record
File insertedFile = serviceA.insertFile(file);
// get the ID from the inserted file and make another insert into another table
serviceA.insertAccessControl(insertedFile.getId()); // inserted file ID is '58496'
}
}
This is my investigation:
When I verified the ID of the inserted record in the table "FILE" is '58497', however repository.save() returned a different value.
When I make the second insert on table "ACCESS_CONTROL_FILE" with FILE_ID as '58496' it results in the error below because the FILE with ID as '58496' does not exist.
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("DB_OWNER"."ACCESS_CONTROL_FILE"."FILE_ID")
I'm puzzled as to why would repository.save() return a different ID(i.e. ID=58496) than what is actually inserted(ID=58497) in the database!
I've investigated all options that I could find on the internet related to 'Propagation and Isolation'.
As mentioned in comments, Looks like a database trigger is causing the issue. Disable the trigger to let JPA to manage the ID generation.

Using function in where clause with clob parameter

We are using a #NamedNativeQuery to fetch entities from our database that are qualified by the stored procedure flexmatch in the where clause of a query.
This works fine in general, but when the parameter chimeString exceeds 4.000 characters it fails raising the following exception:
ORA-01460: unimplemented or unreasonable conversion requested
This does make sense, as 4.000 characters are Oracle's border between String and Clob.
We tried to
use org.hibernate.engine.jdbc.ClobProxy
return entityManager
.createNamedQuery("Structure.findByExactMatch", Structure.class)
.setParameter("chime", ClobProxy.generateProxy(chimeString))
.getResultList();
use javax.persistence.criteria.ParameterExpression together with org.hibernate.engine.jdbc.ClobProxy
ParameterExpression<Clob> chimeParam = entityManager
.getCriteriaBuilder()
.parameter(Clob.class, "chime");
return entityManager
.createNamedQuery("Structure.findByExactMatch", Structure.class)
.setParameter(chimeParam, ClobProxy.generateProxy(chimeString))
.getResultList();
Libraries & System:
Oracle 11g
Hibernate 3.6.6
The find method.
public List<Structure> findByExactMatch(String chimeString) {
return entityManager
.createNamedQuery("Structure.findByExactMatch", Structure.class)
.setParameter("chime", chimeString)
.getResultList();
}
The Structure entity.
#Entity
#NamedNativeQueries({
#NamedNativeQuery(
name = "Structure.findByExactMatch",
query = "SELECT id, molfile(ctab) ctab FROM structure " +
"WHERE flexmatch(ctab, :chime, 'all')=1",
resultClass = Structure.class) })
public class Structure {
#Id
#Column(name = "ID")
private long id;
#Lob
#Column(name = "CTAB")
private String ctab;
// getter & setter
}
Edit 1 The pl/sql function, as you can see it is overloaded.
FUNCTION flexmatch(
molobj IN BLOB,
querymol IN VARCHAR2,
args IN VARCHAR2)
RETURN NUMBER
FUNCTION Flexmatch(
molobj IN BLOB,
querymol IN CLOB,
args IN VARCHAR2)
RETURN NUMBER
After some days of trying, we gave up to solve it within Hiberante. We ran the query using SpringJDBC, which is also present in the project, and used the ID to populate a Hiberante entity. You could do this with plain old JDBC also.

Resources