I have two related stored procedures that involve user-defined types. The first accepts an object ID and returns the corresponding instance of a user-defined type. The second accepts an instance of the same user-defined type and does something with it.
I am using Java, JDBC and a little bit of Spring JDBC. I have successfully completed the first stored procedure, ie. I can retrieve instances of my user-defined type from the DB, However, I cannot get the second stored procedure to work.
Here's the basic outline of what I have so far:
Schema (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;
Entity (Java) - represents the user-defined type in Java
public class Example {
public BigDecimal ID;
public String Name;
}
Mapper (Java) - maps from the SQL type to the Java type and back
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);
}
}
First Stored Procedure (Java) - retrieves an Example object given its 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;
}
As I noted earlier, the first stored procedure is working correctly. The object retrieved from the database is automagically mapped into the corresponding Java object. The next step is to be able to call the stored procedure that accepts an instance of this user-defined type.
Second Stored Procedure (Java) - uses an Example object - incomplete
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();
}
Create a user-defined table type that corresponds to the table that you want to populate. Pass the user-defined table to the stored procedure as a parameter. Inside the stored procedure, select the data from the passed parameter and insert it into the table that you want to populate.
Is it possible to call a stored procedure in a user defined function in sql server, mysql or oracle ? In SQL Server: no, you cannot call stored procedures from inside functions.
First a Table Variable of User Defined Table Type has to be created of the same schema as that of the Table Valued parameter. Then it is passed as Parameter to the Stored Procedure and the Stored Procedure is executed using the EXEC command in SQL Server.
After a fair bit of mucking around, I was able to develop a solution. A few observations:
Struct
which was counter-intuitive (as only arrays were used for outputs).SQLData
interface was not used, ie. writeSQL()
was never called as I found I had to build the struct manually. readSQL()
is called when mapping outputs.It's possible I may be going about things the wrong way, so I'd welcome comments on my solution.
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();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With