[英]How to call a stored procedure from Java and JPA
我正在編寫一個簡單的 web 應用程序來調用存儲過程並檢索一些數據。 它是一個非常簡單的應用程序,它與客戶的數據庫進行交互。 我們傳遞員工 ID 和公司 ID,存儲過程將返回員工詳細信息。
Web 應用程序無法更新/刪除數據,並且正在使用 SQL 服務器。
我正在 Jboss AS 中部署我的 web 應用程序。 我應該使用 JPA 來訪問存儲過程還是CallableStatement
。 在這種情況下使用 JPA 的任何優勢。
還有什么是 sql 語句來調用這個存儲過程。 我以前從未使用過存儲過程,我正在努力解決這個問題。 谷歌沒有太大幫助。
這是存儲過程:
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
更新:
對於使用JPA調用存儲過程時遇到問題的任何其他人。
Query query = em.createNativeQuery("{call getEmployeeDetails(?,?)}",
EmployeeDetails.class)
.setParameter(1, employeeId)
.setParameter(2, companyId);
List<EmployeeDetails> result = query.getResultList();
我注意到的事情:
{call sp_name(?,?)}
而不是call sp_name(?,?)
getSingleResult
不會工作resultSetMapping
名稱或結果 class 詳細信息JPA 2.1 現在支持存儲過程,請在此處閱讀 Java 文檔。
例子:
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");
請參閱此處的詳細示例。
我正在 Jboss AS 中部署我的 Web 應用程序。 我應該使用 JPA 來訪問存儲過程還是 CallableStatement。 在這種情況下使用 JPA 的任何優勢。
JPA 並不真正支持它,但它是可行的。 我仍然不會走這條路:
因此,我寧願考慮使用Spring 對 JDBC 數據訪問的支持,或者像MyBatis這樣的數據映射器,或者考慮到您的應用程序的簡單性,原始 JDBC 和CallableStatement
。 實際上,JDBC 可能是我的選擇。 這是一個基本的開球示例:
CallableStatement cstmt = con.prepareCall("{call getEmployeeDetails(?, ?)}");
cstmt.setInt("employeeId", 123);
cstmt.setInt("companyId", 456);
ResultSet rs = cstmt.executeQuery();
您需要將參數傳遞給存儲過程。
它應該像這樣工作:
List result = em
.createNativeQuery("call getEmployeeDetails(:employeeId,:companyId)")
.setParameter("emplyoyeeId", 123L)
.setParameter("companyId", 456L)
.getResultList();
更新:
或者也許它不應該。
在 Book EJB3 in Action 中,它在第 383 頁說, JPA 不支持存儲過程(頁面只是預覽,你沒有得到全文,整本書可以在幾個地方下載,包括這個,我不知道這是否合法)。
不管怎樣,正文是這樣的:
如果您是 SQL 的忠實粉絲,您可能願意利用數據庫存儲過程的強大功能。 不幸的是,JPA 不支持存儲過程,您必須依賴持久性提供程序的專有功能。 但是,您可以在本機 SQL 查詢中使用簡單的存儲函數(不帶輸出參數)。
對於使用這樣的 IN/OUT 參數的簡單存儲過程
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;
您可以從 JPA 調用它,如下所示:
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);
對於使用SYS_REFCURSOR
OUT 參數的存儲過程:
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;
您可以按如下方式調用它:
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();
對於如下所示的 SQL 函數:
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;
你可以這樣稱呼它:
BigDecimal commentCount = (BigDecimal) entityManager .createNativeQuery( "SELECT fn_count_comments(:postId) FROM DUAL" ) .setParameter("postId", 1L) .getSingleResult();
至少在使用 Hibernate 4.x 和 5.x 時,因為 JPA StoredProcedureQuery
不適用於 SQL FUNCTIONS。
有關在使用 JPA 和 Hibernate 時如何調用存儲過程和函數的更多詳細信息,請查看以下文章
盡管這個答案確實詳細說明了從存儲過程返回記錄集,但我還是在這里發帖,因為我花了很長時間才弄明白,這個線程幫助了我。
我的應用程序使用 Eclipselink-2.3.1,但我將強制升級到 Eclipselink-2.5.0,因為 JPA 2.1 對存儲過程有更好的支持。
此方法需要從“org.eclipse.persistence”導入 EclipseLink 類,因此它特定於 Eclipselink 實現。
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
此方法與實現無關(不需要 Eclipslink 導入)。
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"));
對我來說,只有以下內容適用於 Oracle 11g 和 Glassfish 2.1 (Toplink):
Query query = entityManager.createNativeQuery("BEGIN PROCEDURE_NAME(); END;");
query.executeUpdate();
帶有花括號的變體導致 ORA-00900。
如果使用 EclipseLink,您可以使用 @NamedStoredProcedureQuery 或 StoreProcedureCall 來執行任何存儲過程,包括帶有輸出參數或輸出游標的存儲過程。 還提供對存儲函數和 PLSQL 數據類型的支持。
見, http://en.wikibooks.org/wiki/Java_Persistence/Advanced_Topics#Stored_Procedures
以下對我有用:
Query query = em.createNativeQuery("BEGIN VALIDACIONES_QPAI.RECALC_COMP_ASSEMBLY('X','X','X',0); END;");
query.executeUpdate();
對於 Sql Srver 可能不一樣,但對於使用 oracle 和 eclipslink 的人來說,它對我有用
例如:具有一個 IN 參數(類型 CHAR)和兩個 OUT 參數(NUMBER & VARCHAR)的過程
在persistence.xml 中聲明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>
並在 eclipselink-orm.xml 中聲明 proc 的結構
<?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>
在代碼中,您只需像這樣調用 proc:
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);
}
獲取兩個輸出參數:
Integer myInt = (Integer) resultQuery[0];
String myStr = (String) resultQuery[1];
這對我有用。
@Entity
@Table(name="acct")
@NamedNativeQueries({
@NamedNativeQuery(callable=true, name="Account.findOne", query="call sp_get_acct(?), resultClass=Account.class)})
public class Account{
// Code
}
注意:將來如果您決定使用 findOne 的默認版本,那么只需注釋 NamedNativeQueries 注釋,JPA 將切換到默認版本
如果您有實體管理器,此答案可能會有所幫助
我有一個存儲過程來創建下一個數字,在服務器端我有接縫框架。
客戶端
Object on = entityManager.createNativeQuery("EXEC getNextNmber").executeUpdate();
log.info("New order id: " + on.toString());
數據庫端(SQL 服務器)我有一個名為getNextNmber
存儲過程
您可以在存儲庫中使用@Query(value = "{call PROC_TEST()}", nativeQuery = true)
。 這對我有用。
注意:使用 '{' 和 '}' 否則將不起作用。
JPA 2.0 不支持 RETURN 值,只支持調用。
我的解決方案是。 創建一個調用 PROCEDURE 的函數。
因此,在 JAVA 代碼中,您執行一個調用 oracle FUNCTION 的 NATIVE QUERY。
試試這個代碼:
return em.createNativeQuery("{call getEmployeeDetails(?,?)}",
EmployeeDetails.class)
.setParameter(1, employeeId)
.setParameter(2, companyId).getResultList();
持久化文件
<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/>
爪哇爪哇
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();
}
要調用存儲過程,我們可以使用 java.sql 包中的 Callable Statement。
從 JPA 2.1 開始,JPA 支持使用動態 StoredProcedureQuery 和聲明式 @NamedStoredProcedureQuery 調用存儲過程。
最簡單的方法是使用 JpaRepository
1- Create a stored procedure
CREATE PROCEDURE dbo.getEmployeeDetails
(
@employeeId int,
@companyId int
) AS
BEGIN
SELECT firstName,lastName,gender,address
FROM employee et
WHERE et.employeeId = @employeeId and et.companyId = @companyId
END
2- Create Entity
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class EmployeeDetails {
@Id
private String firstName;
private String lastName;
private String gender;
private String address;
}
3- Create Repository
public interface EmployeeDetailsRepository extends
JpaRepository<EmployeeDetails,String> {
@Query(value = "EXEC dbo.getEmployeeDetails @employeeId=:empId,
@companyId=:compId",nativeQuery =true)
List<EmployeeDetails> getEmployeeList(@Param("employeeId") Integer empId,
@Param("companyId") Integer compId);
}
4- create Controller
@CrossOrigin(origins = "*")
@RestController
@RequestMapping(value = "/api/employee")
public class EmployeeController {
@Autowired
private EmployeeDetailsRepository empRepo;
@GetMapping(value = "/details")
public ResponseEntity<List<EmployeeDetails>> getEmployeeDetails(@RequestParam
String empId, @RequestParam String compId) {
try {
List<EmployeeDetails> result = empRepo.getEmployeeList(
Integer.valueOf(empId),Integer.valueOf(compId));
return ResponseEntity.status(HttpStatus.OK).body(result);
}
catch (Exception ex)
{
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(null);
}
}
}
如果您不太喜歡使用 JPA 或 JDBC 調用此特定過程,則可以使用jOOQ,這是一個第三方庫,可為您的所有存儲過程生成存根以簡化調用它們,並使調用類型安全。
在您的特定情況下,該過程返回一個無類型、未聲明的 cursor (它可能返回多個游標和交錯更新計數)。 因此,您可以使用 jOOQ 調用這樣的過程:
GetEmployeeDetails proc = new GetEmployeeDetails();
proc.setEmployeeId(1);
proc.setCompanyId(2);
proc.execute(configuration);
// Iterate over potentially multiple results
for (Result<?> result : proc.getResults()) {
// Print the first result set (your employee query)
System.out.println(result);
// Use your implicit knowledge of the content of the query
// Without type safety
for (Record record : result) {
// All tables / columns are also generated
System.out.println("First name: " + record.get(EMPLOYEE.FIRSTNAME));
System.out.println("Last name: " + record.get(EMPLOYEE.LASTNAME));
System.out.println("Gender: " + record.get(EMPLOYEE.GENDER));
System.out.println("Address: " + record.get(EMPLOYEE.ADDRESS));
}
}
就個人而言,我不太喜歡一些 RDBMS(包括 SQL Server、MySQL)返回任意無類型游標的特性。 為什么不直接聲明結果類型? SQL 服務器具有強大的表值功能。 例如,只需在此處使用此語法:
CREATE FUNCTION getEmployeeDetails (@employeeId int, @companyId int)
RETURNS TABLE
AS RETURN
SELECT
firstName,
lastName,
gender,
address
FROM employee et
WHERE et.employeeId = @employeeId
AND et.companyId = @companyId
現在,您的目錄中有與此 function 相關聯的完整類型信息,如果您仍在使用 jOOQ,代碼生成器將可以使用該信息,因此您可以像這樣調用 function:
for (GetEmployeeDetailsRecord record : ctx.selectFrom(getEmployeeDetails(1, 2))) {
System.out.println("First name: " + record.getFirstName());
System.out.println("Last name: " + record.getLastName());
System.out.println("Gender: " + record.getGender());
System.out.println("Address: " + record.getAddress());
}
免責聲明:我為 jOOQ 背后的公司工作
我正在編寫一個簡單的Web應用程序以調用存儲過程並檢索一些數據。 它是一個非常簡單的應用程序,可以與客戶的數據庫進行交互。 我們傳遞員工ID和公司ID,並且存儲過程將返回員工詳細信息。
Web應用程序無法更新/刪除數據,並且正在使用SQL Server。
我正在將Web應用程序部署在Jboss AS中。 我應該使用JPA訪問存儲過程還是CallableStatement
。 在這種情況下使用JPA的任何優勢。
調用該存儲過程的sql語句也將是什么。 我以前從未使用過存儲過程,因此我正在為此而苦苦掙扎。 Google並沒有太大幫助。
這是存儲過程:
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
更新:
對於使用JPA調用存儲過程時遇到問題的其他任何人。
Query query = em.createNativeQuery("{call getEmployeeDetails(?,?)}",
EmployeeDetails.class)
.setParameter(1, employeeId)
.setParameter(2, companyId);
List<EmployeeDetails> result = query.getResultList();
我注意到的事情:
{call sp_name(?,?)}
而不是call sp_name(?,?)
getSingleResult
無法工作resultSetMapping
名稱或結果類詳細信息
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.