1.11.1以降のSpringDataJPAは、結果セットを返すSPをサポートしていません。 対応する欠陥 を提出しました 春のデータ付き。
解決策は、APIレベルを下げて、JPAを使用することです。これは、MSSQLSPで動作する私が作成した汎用クラスです。
import com.google.common.base.Strings;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.ParameterMode;
import javax.persistence.Query;
import javax.persistence.StoredProcedureQuery;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class StoredProcRepository {
//region Injected beans (via a RequiredArgsConstructor)
private final EntityManager em;
//endregion
/**
* Calls a stored procedure via JPA and retrieves a single implicit result set (in DBs that
* support them e.g. MS SQL or MySQL). The call is not dependent on a DB dialect. Be
* aware that large result sets should be paginated and not entirely read to memory. Recreates
* StoredProcedureQuery instance and its parameters on each call.
* To execute MS SQL SPs performing multiple queries, SET NOCOUNT ON.
*
* @param procedureName stored procedure name, optionally qualified per DB syntax
* @param resultClass converts (maps) each result set row into instances of resultClass via JPA
* @param spArgs stored procedure arguments, supplied positionally (optional SP arguments at the
* end of the list could be omitted)
* @param <T> class of row instances converted per JPA
* @return the entire result set
*/
public <T> List<T> queryViaStoredProc(String procedureName, Class<T> resultClass,
Object... spArgs) {
StoredProcedureQuery spq = em.createStoredProcedureQuery(procedureName, resultClass);
int pos = 0;
for (Object arg : spArgs) {
spq.registerStoredProcedureParameter(++pos, arg.getClass(), ParameterMode.IN);
spq.setParameter(pos, arg);
}
return spq.getResultList();
}
/**
* Calls a stored procedure via JPA and retrieves only the top row of a single implicit result
* set (in DBs that support them e.g. MS SQL or MySQL).
* Assumes that result set has at least one row.
* The call is not dependent on a DB dialect.
* Be aware that large result sets should be paginated and not entirely read to memory.
* Recreates StoredProcedureQuery instance and its parameters on each call.
* To execute MS SQL SPs performing multiple queries, SET NOCOUNT ON.
*
* @param procedureName stored procedure name, optionally qualified per DB syntax
* @param resultClass converts (maps) each result set row into instances of resultClass via JPA
* @param spArgs stored procedure arguments, supplied positionally (optional SP arguments at the
* end of the list could be omitted)
* @param <T> class of row instances converted per JPA
* @return the entire result set
*/
public <T> T queryTopRowViaStoredProc(String procedureName, Class<T> resultClass,
Object... spArgs) {
return queryViaStoredProc(procedureName, resultClass, spArgs).get(0);
}
}
MS SQL SPの場合、追加の要件はSET NOCOUNT ON
を持つことです。 複数のクエリを実行するすべてのSPに対して。これは、少なくとも3つの方法のいずれかで設定できます。
- JPAを使用する汎用Javaラッパー(以下のコードを参照)。このアプローチは、jTDSJDBCドライバーでのみ機能します。 対応する問題 MSJDBCドライバープロジェクトに提出されました。
- 各SPの冒頭。
- データベース内でグローバルに 。
彼女は#1のコードです:同じStoredProcRepository
の対応するメソッド クラス。
/**
* Calls an MS SQL stored procedure via JPA and retrieves a single implicit result set.
* Protects against lack of SET NOCOUNT in stored procedures.
* This works with jTDS JDBC driver, but not with MS JDBC driver.
* Be aware that large result sets should be paginated and not entirely read to memory.
*
* @param procedureName stored procedure name, optionally qualified per DB syntax
* @param resultClass converts (maps) each result set row into instances of resultClass via JPA
* @param spArgs stored procedure arguments, supplied positionally (optional SP arguments at the
* end of the list could be omitted)
* @param <T> class of row instances converted per JPA
* @return the entire result set
*/
public <T> List<T> queryViaMsSqlStoredProc(String procedureName, Class<T> resultClass,
Object... spArgs) {
String spBindParams = (spArgs.length == 0) ? "" : "?" + Strings.repeat(",?", spArgs.length - 1);
// The following works with jTDS driver, but not with MS driver
String spQuery = String.format("EXEC %s %s", procedureName, spBindParams);
// The following works with jTDS driver, but not with MS driver
/*
String spQuery = String.format("{call %s(%s)}", procedureName, spBindParams);
Query q = em.createNativeQuery("SET NOCOUNT ON; " + spQuery, resultClass)
.setHint("org.hibernate.readOnly", true);
*/
Query q = em.createNativeQuery(spQuery, resultClass);
int pos = 0;
for (Object arg : spArgs) {
q.setParameter(++pos, arg);
}
return q.getResultList();
}
/**
* Calls an MS SQL stored procedure via JPA and retrieves only the top row of a single implicit
* result set.
* Assumes that result set has at least one row.
* The call sets the "NOCOUNT ON" MS SQL batch option.
* Be aware that large result sets should be paginated and not entirely read to memory.
*
* @param procedureName stored procedure name, optionally qualified per DB syntax
* @param resultClass converts (maps) each result set row into instances of resultClass via JPA
* @param spArgs stored procedure arguments, supplied positionally (optional SP arguments at the
* end of the list could be omitted)
* @param <T> class of row instances converted per JPA
* @return the entire result set
*/
public <T> T queryTopRowViaMsSqlStoredProc(String procedureName, Class<T> resultClass,
Object... spArgs) {
return queryViaMsSqlStoredProc(procedureName, resultClass, spArgs).get(0);
}