How to catch the exception of the execute method while using Spring jdbcTemplate - spring

I use the Spring JDBCTemplate to call a stored procedure ,like this
Map receive10PrmtBill = (Map) getJdbcTemplate().execute(sql, new CallableStatementCallback() {
#Override
public Object doInCallableStatement(CallableStatement cs) throws SQLException,
DataAccessException {
cs.setString(1, Constants.ADD_PROC_TYPE);
Map<String,PrmtBillInfoDatagram> tempMap = new HashMap();
cs.execute();
but the execute method return false and didn't throw any exception ,so I don't know what's wrong with my program , how to catch the Exception? Any help?
This is the proc
create proc sp_xx ( #userid int)
as
begin
select personid, personname from person where personid = #userid
select teamid, teamname from team
end

Try setting "IGNORE_DONE_IN_PROC" property to "true". See at:
http://javabob64.wordpress.com/2011/04/12/sybase-and-the-jdbc-driver/

Related

Using H2 alias for mocking stored procedure which has output parameters. Is this even possible with H2?

I have created an H2 alias function which is being called when I execute the corresponding spring jdbc StoredProcedure.
public static ResultSet dao_cashflow_read(Integer contractId, Integer contractVersion, Date rateDtmDate, String refSrc,
String rateStatus, String rateType, String message, Integer rowCount) throws SQLException, IOException {
File f = new File(FixingDaoIT.class.getResource("/").getFile().concat("csv/ldn/fixing.csv"));
return new Csv().read(new FileReader(f), null);
}
This part is working as expected as the associated row mapper function parses the result set correctly and generates the entity objects. The problem is, the JdbcTemplate class then attempts to extract the associated output parameters that were registered with the StoredProcedure implementation. Which in turn calls the method below in class JdbcCallableStatement.java
private JdbcResultSet getOpenResultSet() throws SQLException {
try {
checkClosed();
if (resultSet == null) {
throw DbException.get(ErrorCode.NO_DATA_AVAILABLE);
}
if (resultSet.isBeforeFirst()) {
resultSet.next();
}
return resultSet;
} catch (Exception e) {
throw logAndConvert(e);
}
}
This call fails as the resultSet reference is now null and it throws the DbException NO_DATA_AVAILBLE
If I run the application against Sybase, the output params are as expected. Is this an issue with H2 not supporting output params?

How do you map the output of a Spring stored procedure execute?

I am using Spring and stored procedures to retrieve data from a mySQL database. I have the stored procedure and parameters working OK but I'm having problems mapping the result set. At the moment I have some truly ugly code to get the values and I'm sure there has to be a better, cleaner and more elegant way. Can anyone guide me to a better solution?
After the stored procedure class, I have:
List<String> outList = new ArrayList<String>();
Map<String,Object> outMap = execute(parameters_map);
List list = (List) outMap.get("#result-set-1");
for (Object object : list) {
Map map2 = (Map) object;
list.add(map2.get("runname"));
}
return outList;
runname is the column from the database query.
Is there a better way to achieve this?
Example from spring docs using RowMapper:
public class JdbcActorDao implements ActorDao {
private SimpleJdbcCall procReadAllActors;
public void setDataSource(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.procReadAllActors = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("read_all_actors")
.returningResultSet("actors",
BeanPropertyRowMapper.newInstance(Actor.class));
}
public List getActorsList() {
Map m = procReadAllActors.execute(new HashMap<String, Object>(0));
return (List) m.get("actors");
}
// ... additional methods
}
took a while to interpret the Spring docs but I finally got there.
My solution:
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("DistinctRunNames")
.withoutProcedureColumnMetaDataAccess();
simpleJdbcCall.addDeclaredParameter(new SqlParameter("environment", Types.VARCHAR));
simpleJdbcCall.addDeclaredParameter(new SqlParameter("username", Types.VARCHAR));
simpleJdbcCall.addDeclaredParameter(new SqlParameter("test_suite", Types.VARCHAR));
SqlParameterSource parameters = new MapSqlParameterSource().addValue("environment", environment)
.addValue("username", username).addValue("test_suite", testSuite);
Map map = simpleJdbcCall.returningResultSet("runnames", new ParameterizedRowMapper<RunNameBean>() {
public RunNameBean mapRow(ResultSet rs, int rowNum) throws SQLException {
RunNameBean runNameBean = new RunNameBean();
runNameBean.setName(rs.getString("runname"));
return runNameBean;
}
}).execute(parameters);
return (List) map.get("runnames");
Had problems with expected parameters versus actual, had to break up the simpleJdbcCall object. Maps the results into a list beautifully.
Thank you for answers, helped me to learn about Spring mapping.

Getting IncorrectResultSetColoumnCount exception in queryForList

I am using queryForList to get a list from DB ,
my code looks like,
List<RoleIdBean> role = jdbcTemplate.queryForList(query , new Object[] {userId},RoleIdBean.class);
query = select * from role where userid=?
role table has two coloumns and roleIdBean has two variables .
When I am running this code it is saying expected 1, actual 2
Could someone please check where i am going wrong and assist how to use this method.
As M. Deinum mentions, you have to provide implementation of RowMapper interface so that Spring knows which columns from your table to map to which properties of your object (RoleIdBean). For instance like this:
List<RoleIdBean> list = jdbcTemplate.query("SELECT * FROM role_id", new Object[]{ userId }, new RowMapper<RoleIdBean>() {
#Override
public RoleIdBean mapRow(ResultSet rs, int rowNum) throws SQLException {
RoleIdBean bean = new RoleIdBean();
// Set properties from the ResultSet, e.g:
// bean.setRole(rs.getString(1));
return bean;
}
});

Insert byte[] into blob field with spring's jdbcTemplate and stored procedure

I'm try to insert byte[] into blob field with stored procedure, and get an Exception :
Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [SELECT ID FROM sp_NEWFILE(?,?,?)]; nested exception is org.firebirdsql.jdbc.field.TypeConversionException: Error converting to object.
Model:
public class fileBody {
private int ID;
private byte[] BODY;
private String FILENAME; //getters an setters}
Insert it to database
public class FileBodyDaoImpl implements FileBodyDao {
public int insertData(final FileBody fileBody) throws IOException {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
LobHandler lobHandler = new DefaultLobHandler();
final InputStream in = new ByteArrayInputStream(fileBody.getBODY());
final int fileSize = in.available();
Map<String, Object> out = jdbcTemplate.queryForMap("SELECT ID FROM sp_NEWFILE(?,?,?)",
new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException,
DataAccessException {
ps.setString(1, fileBody.getFILENAME());
lobCreator.setBlobAsBinaryStream(ps, 2, in, fileSize);
ps.setNull(3, java.sql.Types.INTEGER);
}
});
int last_inserted = Integer.parseInt(String.valueOf(out.get("ID")));
return last_inserted;
}
And my stored procedure
create or alter procedure sp_NEWFILE (
FILENAME varchar(255),
BODY blob sub_type 0 segment size 80,
USEID integer)
returns (
ID integer)
as
begin
if (useid is not null) then ID=USEID;
else ID=GEN_ID(gen_filebody_id,1);
if ((FILENAME is NULL) or (FILENAME='')) then FILENAME='UNDEFINED';
INSERT INTO t_filebody(ID,BODY,FILENAME) VALUES(:ID,:BODY,:FILENAME);
suspend;
end^
and i get an Exception:
Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException:
PreparedStatementCallback;
bad SQL grammar [SELECT ID FROM sp_NEWFILE(?,?,?)]; nested exception is org.firebirdsql.jdbc.field.TypeConversionException: Error converting to object.
Versions:
jaybird-jdk17-2.2.5;
Source: firebird2.5 Version: 2.5.1.26351.ds4-2ubuntu0.1;
The problem is that queryForMap does not support a PreparedStatementCallback (contrary to for example execute), instead your anonymous object is considered a normal parameter for the query to execute, and Jaybird does not support this object type. And if Jaybird had supported it, you would have received an error for missing parameters 2 and 3.
Your code can be greatly simplified by passing the byte array:
Map<String, Object> out = jdbcTemplate.queryForMap("SELECT ID FROM sp_NEWFILE(?,?,?)",
fileBody.getFILENAME(), fileBody.getBODY(), null);
This works as Jaybird considers a BLOB SUB_TYPE 0 as a java.sql.Types.LONGVARBINARY and JDBC 4.2 appendix B declares that byte[] is the default type for that (although you can also use it as a java.sql.Types.BLOB).
As a side note, your stored procedure does not need to be selectable (removing SUSPEND makes it executable), and the procedure could also be replaced by using a TRIGGER to generate the primary key and retrieving the value either by using INSERT .. RETURNING .. or through the JDBC generated keys facility (which in turn is implemented in Jaybird through INSERT .. RETURNING ..).
For those seeking non-Jaybird solution to insert BLOB using spring jdbctemplate, the following syntax worked for me to use stored procedures which is different compared to insert via queries.
Insert via Queries
ByteArrayInputStream inputStream = new ByteArrayInputStream(file.getBytes());
ps.setBlob(1, inputStream);
Insert via Stored Procedure Call
Map<String, Object> inParams = new HashMap<>();
inParams.put("pi_some_id", id);
inParams.put("pi_file_blob", new SqlLobValue(file.getBytes()));
SqlParameterSource sqlParameterSource = new MapSqlParameterSource(inParams);
SqlParameter[] sqlParameters = {
new SqlParameter("pi_some_id", Types.VARCHAR),
new SqlParameter("pi_file_blob", Types.BLOB),
new SqlOutParameter("po_error_flag", Types.VARCHAR),
new SqlOutParameter("po_message", Types.VARCHAR)};
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate).withoutProcedureColumnMetaDataAccess().
withProcedureName(storedProcName).withCatalogName(packageName).
declareParameters(sqlParameters);
Map<String, Object> storedProcResult = simpleJdbcCall.execute(sqlParameterSource);

Spring JdbcTemplate and oracle arrayofnumber

I'm using Spring and Oracle database in my solution and i need to execute script
select count(1) from ELEMENTS, table(cast(? as arrayofnumbers)) session_ids
where root_session_id in session_ids.VALUE
but i have a problem with passing input parameter.
i try to pass List or array of BigInteger into
JdbcTemplate.queryForObject("select count(1) from ELEMENTS, table(cast(? as arrayofnumbers)) session_ids
where root_session_id in session_ids.VALUE", Integer.class, INPUT_PARAMS)
but has an Exception:
java.sql.SQLException: Invalid column type
at oracle.jdbc.driver.OraclePreparedStatement.setObjectCritical(OraclePreparedStatement.java:8861)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:8338)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:9116)
at oracle.jdbc.driver.OraclePreparedStatement.setObject(OraclePreparedStatement.java:9093)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.setObject(OraclePreparedStatementWrapper.java:234)
at weblogic.jdbc.wrapper.PreparedStatement.setObject(PreparedStatement.java:357)
Does anyone have the same problem?
EDIT:
Forget to describe arrayofnumber. It's custom type:
TYPE arrayofnumbers as table of number(20)
Found the solution:
final BigInteger[] ids = new BigInteger[]{BigInteger.valueOf(9137797712513092132L)};
int count = jdbc.query("select count(1) from NC_DATAFLOW_ELEMENTS\n" +
" where root_session_id in (select /*+ cardinality(t 10) */ * from table(cast (? as arrayofnumbers)) t)"
, new PreparedStatementSetter() {
public void setValues(PreparedStatement preparedStatement) throws SQLException {
Connection conn = preparedStatement.getConnection();
OracleConnection oraConn = conn.unwrap(OracleConnection.class);
oracle.sql.ARRAY widgets = oraConn.createARRAY("ARRAYOFNUMBERS", ids);
preparedStatement.setArray(1, widgets);
}
}, new ResultSetExtractor<Integer>() {
public Integer extractData(ResultSet resultSet) throws SQLException, DataAccessException {
resultSet.next();
return resultSet.getInt(1);
}
});
out.println(count);
should note that type of array (ARRAYOFNUMBER) should be in upper case

Resources