可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am writing a simple web application to call a stored procedure and retrieve some data.
Its a very simple application, which interacts with client\'s database. We pass employee id and company id and the stored procedure will return employee details.
Web application cannot update/delete data and is using SQL Server.
I am deploying my web application in Jboss AS. Should I use JPA to access the stored procedure or CallableStatement
. Any advantage of using JPA in this case.
Also what will be the sql statement to call this stored procedure. I have never used stored procedures before and I am struggling with this one. Google was not much of a help.
Here is the stored procedure:
CREATE procedure getEmployeeDetails (@employeeId int, @companyId int)
as
begin
select firstName,
lastName,
gender,
address
from employee et
where et.employeeId = @employeeId
and et.companyId = @companyId
end
Update:
For anyone else having problem calling stored procedure using JPA.
Query query = em.createNativeQuery(\"{call getEmployeeDetails(?,?)}\",
EmployeeDetails.class)
.setParameter(1, employeeId)
.setParameter(2, companyId);
List<EmployeeDetails> result = query.getResultList();
Things I have noticed:
- Parameter names didn\'t work for me, so try using parameter index.
- Correct sql statement
{call sp_name(?,?)}
instead of call sp_name(?,?)
- If stored procedure is returning a result set, even if you know with only one row,
getSingleResult
wont work
- Pass a
resultSetMapping
name or result class details
回答1:
JPA 2.1 now support Stored Procedure, read the Java doc here.
Example:
StoredProcedureQuery storedProcedure = em.createStoredProcedureQuery(\"sales_tax\");
// set parameters
storedProcedure.registerStoredProcedureParameter(\"subtotal\", Double.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter(\"tax\", Double.class, ParameterMode.OUT);
storedProcedure.setParameter(\"subtotal\", 1f);
// execute SP
storedProcedure.execute();
// get result
Double tax = (Double)storedProcedure.getOutputParameterValue(\"tax\");
See detailed example here.
回答2:
I am deploying my web application in Jboss AS. Should I use JPA to access the stored procedure or CallableStatement. Any advantage of using JPA in this case.
It is not really supported by JPA but it\'s doable. Still I wouldn\'t go this way:
- using JPA just to map the result of a stored procedure call in some beans is really overkill,
- especially given that JPA is not really appropriate to call stored procedure (the syntax will be pretty verbose).
I would thus rather consider using Spring support for JDBC data access, or a data mapper like MyBatis or, given the simplicity of your application, raw JDBC and CallableStatement
. Actually, JDBC would probably be my choice. Here is a basic kickoff example:
CallableStatement cstmt = con.prepareCall(\"{call getEmployeeDetails(?, ?)}\");
cstmt.setInt(\"employeeId\", 123);
cstmt.setInt(\"companyId\", 456);
ResultSet rs = cstmt.executeQuery();
Reference
- JDBC documentation: Java SE 6
回答3:
How to retrieve Stored Procedure output parameter using JPA (2.0 needs EclipseLink imports and 2.1 does not)
Even though this answer does elaborate on returning a recordset from a stored procedure,
I am posting here, because it took me ages to figure it out and this thread helped me.
My application was using Eclipselink-2.3.1, but I will force an upgrade to
Eclipselink-2.5.0, as JPA 2.1 has much better support for stored procedures.
Using EclipseLink-2.3.1/JPA-2.0: Implementation-Dependent
This method requires imports of EclipseLink classes from \"org.eclipse.persistence\", so it is specific to Eclipselink implementation.
I found it at \"http://www.yenlo.nl/en/calling-oracle-stored-procedures-from-eclipselink-with-multiple-out-parameters\".
StoredProcedureCall storedProcedureCall = new StoredProcedureCall();
storedProcedureCall.setProcedureName(\"mypackage.myprocedure\");
storedProcedureCall.addNamedArgument(\"i_input_1\"); // Add input argument name.
storedProcedureCall.addNamedOutputArgument(\"o_output_1\"); // Add output parameter name.
DataReadQuery query = new DataReadQuery();
query.setCall(storedProcedureCall);
query.addArgument(\"i_input_1\"); // Add input argument names (again);
List<Object> argumentValues = new ArrayList<Object>();
argumentValues.add(\"valueOf_i_input_1\"); // Add input argument values.
JpaEntityManager jpaEntityManager = (JpaEntityManager) getEntityManager();
Session session = jpaEntityManager.getActiveSession();
List<?> results = (List<?>) session.executeQuery(query, argumentValues);
DatabaseRecord record = (DatabaseRecord) results.get(0);
String result = String.valueOf(record.get(\"o_output_1\")); // Get output parameter
Using EclipseLink-2.5.0/JPA-2.1: Implementation-Independent (documented already in this thread)
This method is implementation independent (don\'t need Eclipslink imports).
StoredProcedureQuery query = getEntityManager().createStoredProcedureQuery(\"mypackage.myprocedure\");
query.registerStoredProcedureParameter(\"i_input_1\", String.class, ParameterMode.IN);
query.registerStoredProcedureParameter(\"o_output_1\", String.class, ParameterMode.OUT);
query.setParameter(\"i_input_1\", \"valueOf_i_input_1\");
boolean queryResult = query.execute();
String result = String.valueOf(query.getOutputParameterValue(\"o_output_1\"));
回答4:
You need to pass the parameters to the stored procedure.
It should work like this:
List result = em
.createNativeQuery(\"call getEmployeeDetails(:employeeId,:companyId)\")
.setParameter(\"emplyoyeeId\", 123L)
.setParameter(\"companyId\", 456L)
.getResultList();
Update:
Or maybe it shouldn\'t.
In the Book EJB3 in Action, it says on page 383, that JPA does not support stored procedures (page is only a preview, you don\'t get the full text, the entire book is available as a download in several places including this one, I don\'t know if this is legal though).
Anyway, the text is this:
JPA and database stored procedures
If you’re a big fan of SQL, you may be
willing to exploit the power of
database stored procedures.
Unfortunately, JPA doesn’t support
stored procedures, and you have to
depend on a proprietary feature of
your persistence provider. However,
you can use simple stored functions
(without out parameters) with a native
SQL query.
回答5:
For me, only the following worked with Oracle 11g and Glassfish 2.1 (Toplink):
Query query = entityManager.createNativeQuery(\"BEGIN PROCEDURE_NAME(); END;\");
query.executeUpdate();
The variant with curly braces resulted in ORA-00900.
回答6:
If using EclipseLink you can use the @NamedStoredProcedureQuery or StoreProcedureCall to execute any stored procedure, including ones with output parameters, or out cursors. Support for stored functions and PLSQL data-types is also available.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Advanced_Topics#Stored_Procedures
回答7:
The following works for me:
Query query = em.createNativeQuery(\"BEGIN VALIDACIONES_QPAI.RECALC_COMP_ASSEMBLY(\'X\',\'X\',\'X\',0); END;\");
query.executeUpdate();
回答8:
May be it\'s not the same for Sql Srver but for people using oracle and eclipslink it\'s working for me
ex: a procedure that have one IN param (type CHAR) and two OUT params (NUMBER & VARCHAR)
in the persistence.xml declare the persistence-unit :
<persistence-unit name=\"presistanceNameOfProc\" transaction-type=\"RESOURCE_LOCAL\">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/DataSourceName</jta-data-source>
<mapping-file>META-INF/eclipselink-orm.xml</mapping-file>
<properties>
<property name=\"eclipselink.logging.level\" value=\"FINEST\"/>
<property name=\"eclipselink.logging.logger\" value=\"DefaultLogger\"/>
<property name=\"eclipselink.weaving\" value=\"static\"/>
<property name=\"eclipselink.ddl.table-creation-suffix\" value=\"JPA_STORED_PROC\" />
</properties>
</persistence-unit>
and declare the structure of the proc in the eclipselink-orm.xml
<?xml version=\"1.0\" encoding=\"UTF-8\"?><entity-mappings version=\"2.0\"
xmlns=\"http://java.sun.com/xml/ns/persistence/orm\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xsi:schemaLocation=\"http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd\">
<named-stored-procedure-query name=\"PERSIST_PROC_NAME\" procedure-name=\"name_of_proc\" returns-result-set=\"false\">
<parameter direction=\"IN\" name=\"in_param_char\" query-parameter=\"in_param_char\" type=\"Character\"/>
<parameter direction=\"OUT\" name=\"out_param_int\" query-parameter=\"out_param_int\" type=\"Integer\"/>
<parameter direction=\"OUT\" name=\"out_param_varchar\" query-parameter=\"out_param_varchar\" type=\"String\"/>
</named-stored-procedure-query>
in the code you just have to call your proc like this :
try {
final Query query = this.entityManager
.createNamedQuery(\"PERSIST_PROC_NAME\");
query.setParameter(\"in_param_char\", \'V\');
resultQuery = (Object[]) query.getSingleResult();
} catch (final Exception ex) {
LOGGER.log(ex);
throw new TechnicalException(ex);
}
to get the two output params :
Integer myInt = (Integer) resultQuery[0];
String myStr = (String) resultQuery[1];
回答9:
For a simple stored procedure that using IN/OUT parameters like this
CREATE OR REPLACE PROCEDURE count_comments (
postId IN NUMBER,
commentCount OUT NUMBER )
AS
BEGIN
SELECT COUNT(*) INTO commentCount
FROM post_comment
WHERE post_id = postId;
END;
You can call it from JPA as follows:
StoredProcedureQuery query = entityManager
.createStoredProcedureQuery(\"count_comments\")
.registerStoredProcedureParameter(1, Long.class,
ParameterMode.IN)
.registerStoredProcedureParameter(2, Long.class,
ParameterMode.OUT)
.setParameter(1, 1L);
query.execute();
Long commentCount = (Long) query.getOutputParameterValue(2);
For a stored procedure which uses a SYS_REFCURSOR
OUT parameter:
CREATE OR REPLACE PROCEDURE post_comments (
postId IN NUMBER,
postComments OUT SYS_REFCURSOR )
AS
BEGIN
OPEN postComments FOR
SELECT *
FROM post_comment
WHERE post_id = postId;
END;
You can call it as follows:
StoredProcedureQuery query = entityManager
.createStoredProcedureQuery(\"post_comments\")
.registerStoredProcedureParameter(1, Long.class,
ParameterMode.IN)
.registerStoredProcedureParameter(2, Class.class,
ParameterMode.REF_CURSOR)
.setParameter(1, 1L);
query.execute();
List<Object[]> postComments = query.getResultList();
For a SQL function that looks as follows:
CREATE OR REPLACE FUNCTION fn_count_comments (
postId IN NUMBER )
RETURN NUMBER
IS
commentCount NUMBER;
BEGIN
SELECT COUNT(*) INTO commentCount
FROM post_comment
WHERE post_id = postId;
RETURN( commentCount );
END;
You can call it like this:
BigDecimal commentCount = (BigDecimal) entityManager
.createNativeQuery(
\"SELECT fn_count_comments(:postId) FROM DUAL\"
)
.setParameter(\"postId\", 1L)
.getSingleResult();
At least when using Hibernate 4.x and 5.x because the JPA StoredProcedureQuery
does not work for SQL FUNCTIONS.
For more details about how to call stored procedures and functions when using JPA and Hibernate, check out the following articles
- How to call Oracle stored procedures and functions with JPA and Hibernate
- How to call SQL Server stored procedures and functions with JPA and Hibernate
- How to call PostgreSQL functions (stored procedures) with JPA and Hibernate
- How to call MySQL stored procedures and functions with JPA and Hibernate
回答10:
This worked for me.
@Entity
@Table(name=\"acct\")
@NamedNativeQueries({
@NamedNativeQuery(callable=true, name=\"Account.findOne\", query=\"call sp_get_acct(?), resultClass=Account.class)})
public class Account{
// Code
}
Note : in future if you decide to use default version of findOne then just comment the NamedNativeQueries annotation and JPA will switch to default
回答11:
This answer might be helpful if you have entity manager
I had a stored procedure to create next number and on server side I have seam framework.
Client side
Object on = entityManager.createNativeQuery(\"EXEC getNextNmber\").executeUpdate();
log.info(\"New order id: \" + on.toString());
Database Side (SQL server) I have stored procedure named getNextNmber
回答12:
JPA 2.0 doesn\'t support RETURN values, only calls.
My solution was. Create a FUNCTION calling PROCEDURE.
So, inside JAVA code you execute a NATIVE QUERY calling the oracle FUNCTION.
回答13:
To call stored procedure we can use Callable Statement in java.sql package.
回答14:
Try this code:
return em.createNativeQuery(\"{call getEmployeeDetails(?,?)}\",
EmployeeDetails.class)
.setParameter(1, employeeId)
.setParameter(2, companyId).getResultList();
回答15:
You can use @Query(value = \"{call PROC_TEST()}\", nativeQuery = true)
in your repository. This worked for me.
Attention: use \'{\' and \'}\' or else it will not work.
回答16:
persistence.xml
<persistence-unit name=\"PU2\" transaction-type=\"RESOURCE_LOCAL\">
<non-jta-data-source>jndi_ws2</non-jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties/>
codigo java
String PERSISTENCE_UNIT_NAME = \"PU2\";
EntityManagerFactory factory2;
factory2 = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em2 = factory2.createEntityManager();
boolean committed = false;
try {
try {
StoredProcedureQuery storedProcedure = em2.createStoredProcedureQuery(\"PKCREATURNO.INSERTATURNO\");
// set parameters
storedProcedure.registerStoredProcedureParameter(\"inuPKEMPRESA\", BigDecimal.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter(\"inuPKSERVICIO\", BigDecimal.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter(\"inuPKAREA\", BigDecimal.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter(\"isbCHSIGLA\", String.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter(\"INUSINCALIFICACION\", BigInteger.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter(\"INUTIMBRAR\", BigInteger.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter(\"INUTRANSFERIDO\", BigInteger.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter(\"INTESTADO\", BigInteger.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter(\"inuContador\", BigInteger.class, ParameterMode.OUT);
BigDecimal inuPKEMPRESA = BigDecimal.valueOf(1);
BigDecimal inuPKSERVICIO = BigDecimal.valueOf(5);
BigDecimal inuPKAREA = BigDecimal.valueOf(23);
String isbCHSIGLA = \"\";
BigInteger INUSINCALIFICACION = BigInteger.ZERO;
BigInteger INUTIMBRAR = BigInteger.ZERO;
BigInteger INUTRANSFERIDO = BigInteger.ZERO;
BigInteger INTESTADO = BigInteger.ZERO;
BigInteger inuContador = BigInteger.ZERO;
storedProcedure.setParameter(\"inuPKEMPRESA\", inuPKEMPRESA);
storedProcedure.setParameter(\"inuPKSERVICIO\", inuPKSERVICIO);
storedProcedure.setParameter(\"inuPKAREA\", inuPKAREA);
storedProcedure.setParameter(\"isbCHSIGLA\", isbCHSIGLA);
storedProcedure.setParameter(\"INUSINCALIFICACION\", INUSINCALIFICACION);
storedProcedure.setParameter(\"INUTIMBRAR\", INUTIMBRAR);
storedProcedure.setParameter(\"INUTRANSFERIDO\", INUTRANSFERIDO);
storedProcedure.setParameter(\"INTESTADO\", INTESTADO);
storedProcedure.setParameter(\"inuContador\", inuContador);
// execute SP
storedProcedure.execute();
// get result
try {
long _inuContador = (long) storedProcedure.getOutputParameterValue(\"inuContador\");
varCon = _inuContador + \"\";
} catch (Exception e) {
}
} finally {
}
} finally {
em2.close();
}
回答17:
My solution was.
Create a FUNCTION calling PROCEDURE.
So, inside JAVA code you execute a NATIVE QUERY calling the oracle FUNCTION.