How to call Oracle function or stored procedure using spring persistence framework? - spring

I am using Spring persistence framework for my project.
I want to call oracle function or stored procedure from this framework.
Can anybody suggest how can I achieve this.
Please give solution for both * oracle function and *stored procedure.
Thanks.

Assuming you are referring to JdbcTemplate:
jdbcTemplate.execute(
new CallableStatementCreator() {
public CallableStatement createCallableStatement(Connection con) throws SQLException{
CallableStatement cs = con.prepareCall("{call MY_STORED_PROCEDURE(?, ?, ?)}");
cs.setInt(1, ...); // first argument
cs.setInt(2, ...); // second argument
cs.setInt(3, ...); // third argument
return cs;
}
},
new CallableStatementCallback() {
public Object doInCallableStatement(CallableStatement cs) throws SQLException{
cs.execute();
return null; // Whatever is returned here is returned from the jdbcTemplate.execute method
}
}
);
Calling a function is almost identical:
jdbcTemplate.execute(
new CallableStatementCreator() {
public CallableStatement createCallableStatement(Connection con) {
CallableStatement cs = con.prepareCall("{? = call MY_FUNCTION(?, ?, ?)}");
cs.registerOutParameter(1, Types.INTEGER); // or whatever type your function returns.
// Set your arguments
cs.setInt(2, ...); // first argument
cs.setInt(3, ...); // second argument
cs.setInt(4, ...); // third argument
return cs;
}
},
new CallableStatementCallback {
public Object doInCallableStatement(CallableStatement cs) {
cs.execute();
int result = cs.getInt(1);
return result; // Whatever is returned here is returned from the jdbcTemplate.execute method
}
}
);

Simpler way of calling a Oracle function in Spring is subclassing StoredProcedure like below
public class MyStoredProcedure extends StoredProcedure{
private static final String SQL = "package.function";
public MyStoredProcedure(DataSource ds){
super(ds,SQL);
declareParameter(new SqlOutParameter("param_out",Types.NUMERIC));
declareParameter(new SqlParameter("param_in",Types.NUMERIC));
setFunction(true);//you must set this as it distinguishes it from a sproc
compile();
}
public String execute(Long rdsId){
Map in = new HashMap();
in.put("param_in",rdsId);
Map out = execute(in);
if(!out.isEmpty())
return out.get("param_out").toString();
else
return null;
}
}
And call it like this
#Autowired DataSource ds;
MyStoredProcedure sp = new MyStoredProcedure(ds);
String i = sp.execute(1l);
The Oracle function used here just takes in a numeric parameter and returns a numeric paramter.

In my opinion this is one of the easiest approaches:
public class ServRepository {
private JdbcTemplate jdbcTemplate;
private SimpleJdbcCall functionGetServerErrors;
#Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.functionGetServerErrors = new SimpleJdbcCall(jdbcTemplate).withFunctionName("THIS_IS_YOUR_DB_FUNCTION_NAME").withSchemaName("OPTIONAL_SCHEMA_NAME");
}
public String callYourFunction(int parameterOne, int parameterTwo) {
SqlParameterSource in = new MapSqlParameterSource().addValue("DB_FUNCTION_INCOMING_PARAMETER_ONE", parameterOne).addValue("DB_FUNCTION_INCOMING_PARAMETER_TWO", parameterTwo);
return functionGetServerErrors.executeFunction(String.class, in);
}
}

Calling function using NamedParameterJdbcTemplate:
final String query = "select MY_FUNCTION(:arg1, :arg2, :arg3) from dual";
Map<String, Object> argMap = new HashMap<>();
argMap.put("arg1", "value1");
argMap.put("arg2", 2);
argMap.put("arg3", "value3");
final String result = new NamedParameterJdbcTemplate(dataSource)
.queryForObject(query, argMap, String.class);
Calling procedure using JdbcTemplate:
final String query = "call MY_PROCEDURE(?, ?, ?)";
final Object[] args = {"arg1", "arg2", "arg3"};
new JdbcTemplate(dataSource).execute(query, args, String.class);
Calling function using SimpleJdbcCall:
Map<String, Object> inParameters = new HashMap<>();
inParameters.put("arg1", 55); // arg1 value
inParameters.put("arg2", 20); // arg2 value
MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource(inParameters);
BigDecimal result = new SimpleJdbcCall(dataSource)
.withCatalogName("MY_PACKAGE")
.withSchemaName("MY_SCHEMA")
.withFunctionName("MY_FUNCTION")
.executeFunction(BigDecimal.class, mapSqlParameterSource);
Calling procedure using SimpleJdbcCall:
new SimpleJdbcCall(dataSource)
.withCatalogName("MY_PACKAGE")
.withProcedureName("MY_PROCEDURE")
.execute("arg1", arg2);

Related

Replacing RowMapper object with java 8 lambda expression

I am using Spring JdbcTemplate class in my project.I have the following code:
List<PersonDTO> personList = jdbcTemplate.query(query, new RowMapper<PersonDTO>() {
#Override
public PersonDTO mapRow(ResultSet rs, int rowNumber) throws SQLException {
PersonDTO personDTO = new PersonDTO ();
personDTO.setPerId(rs.getString("COL_ONE"));
personDTO.setIdTypeCd(rs.getString("COL_TWO"));
return personDTO;
}
});
Now I want to replace the anonymous class RowMapper with java8 lambda expression something like this:
Runnable r1 = () -> {
System.out.println("My Runnable");
};
Is it possible?
AFAIK RowMapper is a functional interface, so this would work. lambda expression can't declare checked exceptions, so you will need to wrap that...
jdbcTemplate.query(query, (ResultSet rs, int rowNumber) -> {
PersonDTO personDTO = new PersonDTO ();
personDTO.setPerId(rs.getString("COL_ONE"));
personDTO.setIdTypeCd(rs.getString("COL_TWO"));
return personDTO;
});
The comments are spot on: since that functional interface already declares to throw the exception, there is no need to catch it.
try this
List<PersonDTO> personList = jdbcTemplate.query(query, (rs,rowNumber) -> {
try
{PersonDTO personDTO = new PersonDTO ();
personDTO.setPerId(rs.getString("COL_ONE"));
personDTO.setIdTypeCd(rs.getString("COL_TWO"));
return personDTO;
}catch(SQLException e){
throw new RuntimeException("your error message",e) // or other unchecked exception here
}
});

Get Statement from JDBCTemplate

I have this code,
SimpleJdbcCall sql = new SimpleJdbcCall(dataSource).withProcedureName(procName);
sql.execute(parameters);
And I believe that under the hood this uses a JDBC Statement. How can I get to that object from here? (I need to call the .getWarnings() method on the statement).
In other words how can I get SQLWarnings AND named parameters?
It took a lot of digging, but here is how you can get SQLWarnings (or Print statements) AND named parameters. I extended JdbcTemplate and overrode the handleWarnings() method, and then passed that into my SimpleJdbcCall.
public class JdbcTemplateLoggable extends JdbcTemplate{
List<String> warnings;
public JdbcTemplateLoggable(DataSource dataSource){
super(dataSource);
warnings = new ArrayList<String>();
}
protected void handleWarnings(Statement stmt){
try {
SQLWarning warning = stmt.getWarnings();
while(warning != null){
warnings.add(warning.getMessage());
warning = warning.getNextWarning();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public List<String> getWarnings(){
return warnings;
}
}
Then in my main program
JdbcTemplateLoggable template = new JdbcTemplateLoggable(dataSource);
SimpleJdbcCall sql = new SimpleJdbcCall(template).withProcedureName(procName);
sql.execute(parameters);
for(String s : template.getWarnings()){
log.info(s);
}
You should perhaps use JdbcTemplate directly, or subclass it for usage with your SimpleJdbcCall (instead of a DataSource). JdbcTemplate has a method execute(CallableStatementCreator, CallableStatementCallback), where a callback can be passed which gets the used Statement object.
You could override that method and wrap the passed callback with an own which stores the statement for later use.
public class CustomJdbcTemplate extends JdbcTemplate {
private CallableStatement lastStatement;
public CustomJdbcTemplate(DataSource dataSource) {
super(dataSource);
}
public CallableStatement getLastStatement() {
return lastStatement;
}
#Override
public <T> T execute(CallableStatementCreator csc, CallableStatementCallback<T> action) throws DataAccessException {
StoringCallableStatementCallback<T> callback = new StoringCallableStatementCallback<T>(action);
try {
return super.execute(csc, callback);
}
finally {
this.lastStatement = callback.statement;
}
}
private static class StoringCallableStatementCallback<T> implements CallableStatementCallback<T> {
private CallableStatementCallback<T> delegate;
private CallableStatement statement;
private StoringCallableStatementCallback(CallableStatementCallback<T> delegate) {
this.delegate = delegate;
}
#Override
public T doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
this.statement = cs;
return delegate.doInCallableStatement(cs);
}
}
}
Note that the statement will most probably be closed when you retrieve it later, so getWarnings() may cause errors, depending on used JDBC driver. So maybe you should store the warnings instead of the statement itself.

InstantiationException when using SQLData and OracleCallableStatement Struct Mapping

I cannot figure out an answer to the following problem. Hope somebody can help me. I am mapping JAVA class to a Struct as described here:
http://docs.oracle.com/cd/F49540_01/DOC/java.815/a64685/samapp4.htm
I have an Oracle Object:
create or replace TYPE DK1 AS OBJECT( zahl CHAR(1) );
and corresponding JAVA class:
public class DK1 implements SQLData {
private String sql_type;
public static final int _SQL_TYPECODE = OracleTypes.STRUCT;
private String zahl;
public DK1(String sql_type, String z) {
this.sql_type = sql_type;
setZahl(z);
}
public String getSQLTypeName() throws SQLException {
return sql_type;
}
public void readSQL(SQLInput stream, String typeName) throws SQLException {
sql_type = typeName;
this.setZahl(stream.readString());
}
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(getZahl());
}
public String getSql_type() {
return sql_type;
}
public void setSql_type(String sql_type) {
this.sql_type = sql_type;
}
public String getZahl() {
return zahl;
}
public void setZahl(String zahl) {
this.zahl = zahl;
}
}
my test method is the following:
public class SQLDataExample {
public static void main(String args[]) throws Exception {
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
OracleConnection conn = (OracleConnection) DriverManager.getConnection(
"jdbc:oracle:thin:#................", "...",
"...");
Dictionary<String, Class<?>> map = (Dictionary) conn.getTypeMap();
map.put("BONI.DK1", Class.forName("com.gwb.db.objects.DK1"));
OracleCallableStatement cs = (OracleCallableStatement) conn.prepareCall("{call BOX.DK(?)}");
DK1 dd = new DK1("BONI.DK1", "1");
cs.setObject(1, dd);
cs.registerOutParameter(1, OracleTypes.STRUCT, "BONI.DK1");
cs.execute();
DK1 df = (DK1) cs.getObject(1);
}
}
Now, the last step
DK1 df = (DK1) cs.getObject(1);
in this procedure fails and although I have tried so many things in the last couple of days, I cannot get it to run! I get a
Exception in thread "main" java.sql.SQLException: Inconsistent Java-
ans SQL-Objecttypes: InstantiationException: com.gwb.db.objects.DK1
If I replace getObject with getSTRUCT I see that the DB procedure works and returns values as expected. I cannnot figure out why a I unable to map a JAVA Object.
I would be very grateful for any help or tipps!
Thank you in advance
I forgot the default constructor
public DK1() { }

JdbcTemplate delete syntax

Can someone point out any mistake in my following code of Spring Jdbc Template?
When I click delete, the record is not getting deleted and there are no errors showing.
public void delete(String id) {
logger.debug("Deleting existing person");
// Prepare our SQL statement using Unnamed Parameters style
String query = "delete from person where id = ?";
// Assign values to parameters
Object[] person = new Object[] {id};
// Delete
jdbcTemplate.update(query, person);
}
Here is an example. Pay attention:
Integer id
public boolean delete(Integer id){
String sql = "DELETE FROM organization WHERE id = ?";
Object[] args = new Object[] {id};
return jdbcTemplate.update(sql, args) == 1;
}
#Override
public String deleteXXById(String id) {
String sql = "DELETE FROM VENUE WHERE id =:id?";
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("id", id);
Object[] args = new Object[] {id};
int update = jdbcTemplate.update(sql, paramMap);
String updatecount = "Failed";
if (update == 0) {
updatecount = "Failed";
} else {
updatecount = "SUCCESS";
}
return updatecount;
}

Incorrect number of arguments for PROCEDURE; expected 1, got 0. Cant determine the error from code

//set input parameters
Map<String,Object> inParams = new HashMap<String,Object>();
inParams.put("Sig",resourceHistoryBean.getId());
List<ResourceHistoryBean> resourceHistoryList= new ArrayList<ResourceHistoryBean>();
// define stored procedure
try{
SimpleJdbcCall readResult = new SimpleJdbcCall(getDataSource())
.useInParameterNames("Sig")
.declareParameters(new SqlParameter("Sig", Types.VARCHAR))
.withProcedureName("SP_ResourceAllocationDtls")
.withSchemaName("hrms")
.returningResultSet("ResourceHistory", new ParameterizedRowMapper<ResourceHistoryBean>() {
public ResourceHistoryBean mapRow(ResultSet rs, int rowNum)
throws SQLException {
ResourceHistoryBean bean = new ResourceHistoryBean();
resourceHistoryBean.setProjectName(rs.getString(RH_PROJECT_NAME));
return bean;
}
});
readResult.compile();
// execute stored procedure
Map<String, Object> out = readResult.execute(inParams);
resourceHistoryList = (List<ResourceHistoryBean>) out.get("ResourceHistory");
Looks like I was able to find an alternative solution to above problem (Parameter passing to stored procedure and use a mapping class as well ):
public List<ResourceHistoryBean> getResourceHistory(final ResourceHistoryBean resourceHistoryBean)throws Exception{
try {
// call stored procedure and pass parameter to it
List resourceHistoryList = getJdbcTemplate().query(
"call hrms.SP_ResourceAllocationDtls(?)",
new Object[] {resourceHistoryBean.getId()}, new HistoryMapper());
return resourceHistoryList;
} catch (Exception e) {
throw e;
} finally {
closeTemplate();
}
}
// mapper class
class HistoryMapper implements RowMapper, IDatabaseConstants {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
ResourceHistoryBean resourceHistoryBean = new ResourceHistoryBean();
resourceHistoryBean.setProjectName(rs.getString(RH_PROJECT_NAME));
return resourceHistoryBean;
}
}

Resources