Piszę prostą aplikację internetową do wywoływania procedury składowanej i pobierania niektórych danych. Jest to bardzo prosta aplikacja, która współpracuje z bazą danych klienta. Przekazujemy identyfikator pracownika i identyfikator firmy, a procedura składowana zwróci dane pracownika.
Aplikacja internetowa nie może aktualizować / usuwać danych i używa SQL Server.
Wdrażam moją aplikację internetową w Jboss AS. Czy powinienem używać JPA, aby uzyskać dostęp do procedury składowanej lub CallableStatement
. Wszelkie korzyści z używania JPA w tym przypadku.
Jaka będzie instrukcja sql, aby wywołać tę procedurę składowaną. Nigdy wcześniej nie korzystałem z procedur składowanych i zmagam się z tym. Google niewiele pomogło.
Oto procedura składowana:
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
Aktualizacja:
Dla każdego, kto ma problem z wywołaniem procedury składowanej przy użyciu JPA .
Query query = em.createNativeQuery("{call getEmployeeDetails(?,?)}",
EmployeeDetails.class)
.setParameter(1, employeeId)
.setParameter(2, companyId);
List<EmployeeDetails> result = query.getResultList();
Rzeczy, które zauważyłem:
- Nazwy parametrów nie działały, więc spróbuj użyć indeksu parametrów.
- Prawidłowa instrukcja sql
{call sp_name(?,?)}
zamiastcall sp_name(?,?)
- Jeśli procedura składowana zwraca zestaw wyników, nawet jeśli wiesz, że ma tylko jeden wiersz,
getSingleResult
nie zadziała - Przekaż
resultSetMapping
nazwę lub szczegóły klasy wyników
Odpowiedzi:
JPA 2.1 obsługuje teraz procedurę składowaną, przeczytaj dokumentację Java tutaj .
Przykład:
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");
Zobacz szczegółowy przykład tutaj .
źródło
Tak naprawdę nie jest obsługiwany przez JPA, ale jest wykonalny . Mimo to nie poszedłbym w ten sposób:
Dlatego wolałbym rozważyć użycie obsługi Springa dla dostępu do danych JDBC lub mapera danych, takiego jak MyBatis lub, biorąc pod uwagę prostotę aplikacji, surowe JDBC i
CallableStatement
. Właściwie JDBC byłby prawdopodobnie moim wyborem. Oto podstawowy przykład rozpoczęcia:CallableStatement cstmt = con.prepareCall("{call getEmployeeDetails(?, ?)}"); cstmt.setInt("employeeId", 123); cstmt.setInt("companyId", 456); ResultSet rs = cstmt.executeQuery();
Odniesienie
źródło
Musisz przekazać parametry do procedury składowanej.
Powinno działać tak:
List result = em .createNativeQuery("call getEmployeeDetails(:employeeId,:companyId)") .setParameter("emplyoyeeId", 123L) .setParameter("companyId", 456L) .getResultList();
Aktualizacja:
A może nie powinno.
W książce EJB3 w akcji na stronie 383 jest napisane, że JPA nie obsługuje procedur składowanych (strona jest tylko podglądem, nie otrzymujesz pełnego tekstu, cała książka jest dostępna do pobrania w kilku miejscach, w tym w tej , Nie wiem, czy jest to legalne).
W każdym razie tekst jest taki:
JPA i procedury składowane w bazie danych
źródło
Jak pobrać parametr wyjściowy procedury składowanej za pomocą JPA (2.0 wymaga importu EclipseLink, a 2.1 nie)
Mimo że ta odpowiedź zawiera szczegółowe informacje na temat zwracania zestawu rekordów z procedury składowanej, publikuję tutaj, ponieważ zajęło mi to wieki, a ten wątek mi pomógł.
Moja aplikacja korzystała z Eclipselink-2.3.1, ale wymuszę aktualizację do Eclipselink-2.5.0, ponieważ JPA 2.1 ma znacznie lepszą obsługę procedur składowanych.
Korzystanie z EclipseLink-2.3.1 / JPA-2.0: zależne od implementacji
Ta metoda wymaga importu klas EclipseLink z „org.eclipse.persistence”, więc jest specyficzna dla implementacji Eclipselink.
Znalazłem go pod adresem „ 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
Korzystanie z EclipseLink-2.5.0 / JPA-2.1: niezależne od implementacji (udokumentowane już w tym wątku)
Ta metoda jest niezależna od implementacji (nie wymaga importu 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"));
źródło
xml
pliku i nie zadziałała. Nie mogę odczytaćOUT
parametru.Dla prostej procedury składowanej, która używa parametrów IN / OUT takich jak ten
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;
Możesz to nazwać z JPA w następujący sposób:
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);
W przypadku procedury składowanej, która używa
SYS_REFCURSOR
parametru 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;
Możesz to nazwać w następujący sposób:
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();
W przypadku funkcji SQL, która wygląda następująco:
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;
Możesz to nazwać tak:
BigDecimal commentCount = (BigDecimal) entityManager .createNativeQuery( "SELECT fn_count_comments(:postId) FROM DUAL" ) .setParameter("postId", 1L) .getSingleResult();
Przynajmniej przy korzystaniu z Hibernate 4.x i 5.x, ponieważ JPA
StoredProcedureQuery
nie działa w przypadku FUNKCJI SQL.Aby uzyskać więcej informacji na temat wywoływania procedur składowanych i funkcji podczas korzystania z JPA i hibernacji, zapoznaj się z następującymi artykułami
źródło
createNativeQuery
. Przerzuciłem się nacreateStoredProcedureQuery
. Następnie voila!Dla mnie tylko następujące działały z Oracle 11g i Glassfish 2.1 (łącze górne):
Query query = entityManager.createNativeQuery("BEGIN PROCEDURE_NAME(); END;"); query.executeUpdate();
Wariant z aparatami kręconymi zaowocował ORA-00900.
źródło
W przypadku korzystania z EclipseLink można użyć @NamedStoredProcedureQuery lub StoreProcedureCall do wykonania dowolnej procedury składowanej, w tym procedur z parametrami wyjściowymi lub kursorów. Dostępna jest również obsługa zapisanych funkcji i typów danych PLSQL.
Zobacz, http://en.wikibooks.org/wiki/Java_Persistence/Advanced_Topics#Stored_Procedures
źródło
U mnie działa:
Query query = em.createNativeQuery("BEGIN VALIDACIONES_QPAI.RECALC_COMP_ASSEMBLY('X','X','X',0); END;"); query.executeUpdate();
źródło
Może to nie to samo dla Sql Srver, ale dla osób używających Oracle i Eclipslink to działa dla mnie
np. procedura, która ma jeden parametr IN (typ CHAR) i dwa parametry OUT (NUMBER i VARCHAR)
w pliku persistence.xml zadeklaruj jednostkę-trwałości:
<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>
i zadeklaruj strukturę proc w 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>
w kodzie wystarczy wywołać swój proc w ten sposób:
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); }
aby uzyskać dwa parametry wyjściowe:
Integer myInt = (Integer) resultQuery[0]; String myStr = (String) resultQuery[1];
źródło
To zadziałało dla mnie.
@Entity @Table(name="acct") @NamedNativeQueries({ @NamedNativeQuery(callable=true, name="Account.findOne", query="call sp_get_acct(?), resultClass=Account.class)}) public class Account{ // Code }
Uwaga: w przyszłości, jeśli zdecydujesz się użyć domyślnej wersji findOne, po prostu skomentuj adnotację NamedNativeQueries, a JPA przełączy się na domyślną
źródło
Ta odpowiedź może być pomocna, jeśli masz menedżera encji
Miałem procedurę składowaną, aby utworzyć następny numer, a po stronie serwera mam szkielet szwu.
Strona klienta
Object on = entityManager.createNativeQuery("EXEC getNextNmber").executeUpdate(); log.info("New order id: " + on.toString());
Strona bazy danych (serwer SQL) Mam procedurę składowaną o nazwie
getNextNmber
źródło
Możesz użyć
@Query(value = "{call PROC_TEST()}", nativeQuery = true)
w swoim repozytorium. To zadziałało dla mnie.Uwaga: użyj '{' i '}', bo inaczej to nie zadziała.
źródło
JPA 2.0 nie obsługuje wartości RETURN, tylko wywołania.
Moje rozwiązanie było. Utwórz FUNKCJĘ wywołującą PROCEDURĘ.
Tak więc w kodzie JAVA wykonujesz NATIVE QUERY wywołując funkcję Oracle FUNCTION.
źródło
Aby wywołać procedurę składowaną, możemy użyć instrukcji Callable w pakiecie java.sql.
źródło
Wypróbuj ten kod:
return em.createNativeQuery("{call getEmployeeDetails(?,?)}", EmployeeDetails.class) .setParameter(1, employeeId) .setParameter(2, companyId).getResultList();
źródło
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(); }
źródło
Od JPA 2.1, JPA obsługuje wywoływanie procedur składowanych przy użyciu dynamicznej StoredProcedureQuery i deklaratywnej @NamedStoredProcedureQuery.
źródło
Moje rozwiązanie było. Utwórz FUNKCJĘ wywołującą PROCEDURĘ.
Tak więc w kodzie JAVA wykonujesz NATIVE QUERY wywołując funkcję Oracle FUNCTION.
źródło