spring
SimpleJdbcCall에서 SqlRowSet 구하기
수색…
소개
여기서는 커서 출력 매개 변수 가있는 데이터베이스의 저장 프로 시저와 함께 SimpleJdbcCall 을 사용하여 SqlRowSet 을 직접 가져 오는 방법,
나는 오라클 데이터베이스를 가지고 일하고 있는데, 나는 다른 데이터베이스에서 작동해야하는 예제를 만들려고 시도했다.
SimpleJdbcCall 생성
일반적으로 SimpleJdbcCalls을 서비스에 생성하려고합니다.
이 예에서는 프로 시저에 커서 인 단일 출력 매개 변수가 있다고 가정합니다. 프로 시저에 맞게 declareParameters를 조정해야합니다.
@Service
public class MyService() {
@Autowired
private DataSource dataSource;
// Autowire your configuration, for example
@Value("${db.procedure.schema}")
String schema;
private SimpleJdbcCall myProcCall;
// create SimpleJdbcCall after properties are configured
@PostConstruct
void initialize() {
this.myProcCall = new SimpleJdbcCall(dataSource)
.withProcedureName("my_procedure_name")
.withCatalogName("my_package")
.withSchemaName(schema)
.declareParameters(new SqlOutParameter(
"out_param_name",
Types.REF_CURSOR,
new SqlRowSetResultSetExtractor()));
}
public SqlRowSet myProc() {
Map<String, Object> out = this.myProcCall.execute();
return (SqlRowSet) out.get("out_param_name");
}
}
여기에 사용할 수있는 옵션이 많이 있습니다.
- withoutProcedureColumnMetaDataAccess () 는 프로 시저 이름이 오버로드되었거나 SimpleJdbcCall이 데이터베이스에 대해 유효성을 확인하지 못하게하려는 경우 필요합니다.
- 프로 시저에 반환 값이 있으면 withReturnValue () 입니다. declareParameters에 주어진 첫 번째 값은 반환 값을 정의합니다. 또한 프로 시저가 함수이면 실행시 withFunctionName 및 executeFunction을 사용 하십시오 .
- position 대신 이름을 사용하여 인수를 제공하려면 withNamedBinding () .
- useInParameterNames () 는 인수의 순서를 정의합니다. 나는 당신이 인수 이름을 값의지도 대신 목록으로 전달할 때 이것이 필요할 것이라고 생각한다. withoutProcedureColumnMetaDataAccess ()를 사용하는 경우에만 필요할 수도 있지만,
오라클 데이터베이스
오라클에는 여러 가지 문제가 있습니다. 문제를 해결하는 방법은 다음과 같습니다.
프로 시저 출력 매개 변수가 ref cursor
라고 가정하면이 예외가 발생합니다.
java.sql.SQLException : 유효하지 않은 열 유형 : 2012
따라서 simpleJdbcCall.declareParameters () 에서 Types.REF_CURSOR
를 OracleTypes.CURSOR
로 변경 Types.REF_CURSOR
OracleTypes 지원
데이터에 특정 열 유형이있는 경우에만이 작업을 수행하면됩니다.
내가 만난 다음 문제는 oracle.sql.TIMESTAMPTZ
와 같은 독점적 유형이 SqlRowSetResultSetExtractor에서이 오류를 발생시킨 것입니다.
column에 대한 SQL 유형이 잘못되었습니다. 중첩 예외는 java.sql.SQLException입니다. column에 대한 SQL 유형이 잘못되었습니다.
따라서 Oracle 유형을 지원하는 ResultSetExtractor 를 작성해야합니다.
이 코드 뒤에 암호 이유를 설명 할 것입니다.
package com.boost.oracle;
import oracle.jdbc.rowset.OracleCachedRowSet;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* OracleTypes can cause {@link org.springframework.jdbc.core.SqlRowSetResultSetExtractor}
* to fail due to a Oracle SQL type that is not in the standard {@link java.sql.Types}.
*
* Also, types such as {@link oracle.sql.TIMESTAMPTZ} require a Connection when processing
* the ResultSet; {@link OracleCachedRowSet#getConnectionInternal()} requires a JNDI
* DataSource name or the username and password to be set.
*
* For now I decided to just set the password since changing SpringBoot to a JNDI DataSource
* configuration is a bit complicated.
*
* Created by Arlo White on 2/23/17.
*/
public class OracleSqlRowSetResultSetExtractor implements ResultSetExtractor<SqlRowSet> {
private String oraclePassword;
public OracleSqlRowSetResultSetExtractor(String oraclePassword) {
this.oraclePassword = oraclePassword;
}
@Override
public SqlRowSet extractData(ResultSet rs) throws SQLException, DataAccessException {
OracleCachedRowSet cachedRowSet = new OracleCachedRowSet();
// allows getConnectionInternal to get a Connection for TIMESTAMPTZ
cachedRowSet.setPassword(oraclePassword);
cachedRowSet.populate(rs);
return new ResultSetWrappingSqlRowSet(cachedRowSet);
}
}
특정 Oracle 유형에는 ResultSet에서 열 값을 가져 오기 위해 Connection이 필요합니다. TIMESTAMPTZ는 이러한 유형 중 하나입니다. 따라서 rowSet.getTimestamp(colIndex)
가 호출되면이 예외가 발생합니다.
원인 : java.sql.SQLException : oracle.jdbc.rowset.OracleCachedRowSet.getTemplate (OracleCachedRowSet.java)의 oracle.jdbc.rowset.OracleCachedRowSet.getConnectionInternal (OracleCachedRowSet.java:560)에 하나 이상의 인증 RowSet 속성이 설정되지 않았습니다. : 3717) at org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet.getTimestamp
이 코드를 살펴보면 Connection을 가져 오기 위해 OracleCachedRowSet에 암호 또는 JNDI DataSource 이름이 필요함을 알 수 있습니다. JNDI 조회를 선호한다면 OracleCachedRowSet에 DataSourceName이 설정되어 있는지 확인하십시오.
그래서 내 서비스에서, 나는 패스워드를 Autowire하고 다음과 같이 출력 매개 변수를 선언한다 :
new SqlOutParameter("cursor_param_name", OracleTypes.CURSOR, new OracleSqlRowSetResultSetExtractor(oraclePassword))