저장 프로시저에 대한 입력으로 사용자 정의 유형을 전달하려면 어떻게 해야 합니까?
사용자 정의 유형과 관련된 두 가지 저장 프로시저가 있습니다.첫 번째는 개체 ID를 수락하고 사용자 정의 유형의 해당 인스턴스를 반환합니다.두 번째는 동일한 사용자 정의 유형의 인스턴스를 수락하고 이 인스턴스로 작업을 수행합니다.
저는 자바, JDBC와 약간의 봄 JDBC를 사용하고 있습니다.첫 번째 저장 절차를 성공적으로 마쳤습니다.DB에서 사용자 정의 유형의 인스턴스를 검색할 수 있지만 두 번째 저장 프로시저가 작동하지 않습니다.
지금까지 제가 가지고 있는 내용의 기본 개요는 다음과 같습니다.
스키마(PL/SQL)
create or replace type example_obj as object
(ID NUMBER,
NAME VARCHAR2(100))
create or replace type example_tab as table of example_obj
create or replace package
example as
procedure getExample
(p_id in number,
p_example out example_tab);
procedure useExample
(p_example in example_tab);
end example;
엔티티(Java) - Java에서 사용자 정의 유형을 나타냅니다.
public class Example {
public BigDecimal ID;
public String Name;
}
Mapper(자바) - SQL 유형에서 Java 유형 및 뒤로 맵
public class ExampleMapper extends Example implements SQLData {
public static final String SQL_OBJECT_TYPE_NAME = "example_obj";
public static final String SQL_TABLE_TYPE_NAME = "example_tab";
@Override
public String getSQLTypeName() throws SQLException {
return SQL_TABLE_TYPE_NAME;
}
@Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
ID = stream.readBigDecimal();
Name = stream.readString();
}
@Override
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeBigDecimal(ID);
stream.writeString(Name);
}
}
첫 번째 저장 프로시저(Java) - ID가 지정된 예제 개체를 검색합니다.
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import org.springframework.jdbc.core.JdbcTemplate;
public Example getExample(BigDecimal ID) throws SQLException {
String query = "begin example.getExample(?, ?); end;";
Connection connection = jdbcTemplate.getDataSource().getConnection();
CallableStatement callableStatement = connection.prepareCall(query);
callableStatement.setBigDecimal("p_id", ID);
Map<String, Class<?>> typeMap = connection.getTypeMap();
typeMap.put(Example.SQL_OBJECT_TYPE_NAME, ExampleMapper.class);
callableStatement.registerOutParameter("p_example", Types.ARRAY, Example.SQL_TABLE_TYPE_NAME);
connection.setTypeMap(typeMap);
callableStatement.execute();
Array array = (Array)callableStatement.getObject("p_example");
Object[] data = (Object[])array.getArray();
Example example = (Example)data[0]; // It's an ExampleMapper, but I only want Example
return example;
}
앞서 말씀드린 것처럼 첫 번째 저장 절차가 제대로 진행되고 있습니다.데이터베이스에서 검색된 개체는 해당 Java 개체에 자동으로 매핑됩니다.다음 단계는 이 사용자 정의 유형의 인스턴스를 수락하는 저장 프로시저를 호출하는 것입니다.
두 번째 저장 프로시저(Java) - 예제 개체 사용 - 미완성
public void useExample(Example example) throws SQLException {
String query = "begin example.useExample(?); end;";
Connection connection = jdbcTemplate.getDataSource().getConnection();
CallableStatement callableStatement = connection.prepareCall(query);
// Is this required (as per getExample())?
Map<String, Class<?>> typeMap = connection.getTypeMap();
typeMap.put(Example.SQL_OBJECT_TYPE_NAME, ExampleMapper.class);
connection.setTypeMap(typeMap);
/***
*** What goes here to pass the object in as a parameter?
***/
callableStatement.setObject("p_example", ???);
callableStatement.execute();
}
조금만 고민한 끝에 해결책을 개발할 수 있었습니다.몇 가지 관측치:
- 웹에서는 이 작업을 수행하는 방법에 대한 문서가 많지 않습니다.
- 사용자 정의 유형을 입력으로 사용하는 것은 잘 지원되지 않는 것 같습니다.
- 저는 제가 A를 사용해야 한다는 것을 알았습니다.
Struct
(출력에 배열만 사용되었기 때문에) 반대 intuitive이었습니다. - 그
SQLData
인터페이스가 사용되지 않았습니다.writeSQL()
구조물을 수동으로 만들어야 한다는 걸 알았기 때문에 전화를 받은 적이 없습니다readSQL()
출력을 매핑할 때 호출됩니다. - 어레이 생성을 위해 DB별 코드를 사용해야 했는데, 이 코드는 Oracle 클래스를 의미했습니다.
제가 일을 잘못 진행하고 있을 수도 있으니 제 해결책에 대한 의견을 듣고 싶습니다.
public void useExample(Example example) throws SQLException {
String query = "begin example.useExample(?); end;";
Connection connection = jdbcTemplate.getDataSource().getConnection();
CallableStatement callableStatement = connection.prepareCall(query);
Map<String, Class<?>> typeMap = connection.getTypeMap();
typeMap.put(Example.SQL_OBJECT_TYPE_NAME, ExampleMapper.class);
connection.setTypeMap(typeMap);
// Manually convert the example object into an SQL type.
Object[] exampleAttributes = new Object[]{example.ID, example.Name};
Struct struct = connection.createStruct(type.getObjectType(), exampleAttributes);
// Build the array using Oracle specific code.
DelegatingConnection<OracleConnection> delegatingConnection = (DelegatingConnection<OracleConnection>) new DelegatingConnection(connection);
OracleConnection oracleConnection = (OracleConnection) delegatingConnection.getInnermostDelegate();
Object[] data = new Object[]{struct};
Array array oracleConnection.createOracleArray(Example.SQL_TABLE_TYPE_NAME, data);
// Set the input value (finally).
callableStatement.setObject("p_example", array);
callableStatement.execute();
}
데이터를 수동으로 변환할 필요는 없습니다.스트럭트는 절대 필요 없어요다음은 단순화된 버전입니다.
final OracleConnection oracleConnection = (OracleConnection) connection.getClass().
getMethod("getUnderlyingConnection").invoke(connection);
List<Example> example = new ArrayList<>();
example.add(new Example(1L, "something"));
example.add(new Example(2L, "something else"));
Map<String, Class<?>> typeMap = connection.getTypeMap();
typeMap.put(Example.SQL_OBJECT_TYPE_NAME, Example.class);
connection.setTypeMap(typeMap);
Array array = oracleConnection.createOracleArray(Example.SQL_TABLE_TYPE_NAME, example.toArray());
statement.setObject(1, array);
statement.execute();
예제 맵퍼와 예제 맵퍼를 하나의 클래스로 결합했습니다(간단하게 하기 위해)예에서 또한 잘못된 점은 다음과 같습니다.
@Override
public String getSQLTypeName() throws SQLException {
return SQL_OBJECT_TYPE_NAME;
}
보다시피 이 재정의된 메서드는 테이블 유형 이름이 아니라 개체 유형 이름을 반환해야 합니다.
언급URL : https://stackoverflow.com/questions/46069490/how-do-i-pass-a-user-defined-type-as-an-input-to-a-stored-procedure
'programing' 카테고리의 다른 글
자바 프로젝트:ApplicationContext를 로드하지 못했습니다. (0) | 2023.09.20 |
---|---|
C/C++에서 div 또는 ldiv를 사용하는 이유는 무엇입니까? (0) | 2023.09.20 |
알파벳을 어떻게 반복합니까? (0) | 2023.07.22 |
구성 맵 kubernetes 여러 환경 (0) | 2023.07.22 |
SQLSTATE[42S22]:열을 찾을 수 없음: 1054 'where 절'의 'id' 알 수 없는 열(SQL: 'id'에서 * 선택 = 5 제한 1) (0) | 2023.07.22 |