Unable to call stored procedure in oracle through JDBC - jdbc

I am trying to call a stored procedure where i am inserting 7 values into a table. But the below code is not working, please tell what am i doing wrong ?
i do not get any error, the page just remains static though after successful query execution it is suppose to redirect to a new page.
public class admincontrol extends TagSupport
{
HttpServletRequest request;
HttpServletResponse response;
String msg="";
public int doStartTag() throws JspException
{
request=(HttpServletRequest)pageContext.getRequest();
response=(HttpServletResponse)pageContext.getResponse();
return EVAL_PAGE;
}
public void check ()
{
JspWriter out=pageContext.getOut();
Connection con;
CallableStatement stmt;
ResultSet rs;
try
{
try
{
Class.forName("oracle.jdbc.driver.OracleDriver");
}
catch(ClassNotFoundException ex)
{
out.println(ex.getMessage());
}
HttpSession mysession=request.getSession();
String sess=(String)mysession.getAttribute("user");
String rr=(String)adminmodel.time.trim();
String tempid=(String)adminmodel.employeid.trim();
String tdept=(String)adminmodel.department.trim();
String tsup=(String)adminmodel.supervisor.trim();
String tact=(String)adminmodel.action.trim();
String tdate=(String)adminmodel.date.trim();
HttpSession session1=request.getSession();
session1.setAttribute("requestnum",rr);
Random rand = new Random();
int r= rand.nextInt(80001) + 19999;
String reff = String.valueOf(r);
if (!tempid.matches(".*[%#^<>&;'\0-].*") && !tdept.matches(".*
[%#^<>&;'\0-].*") && !tsup.matches(".*[%#^<>&;'\0-].*"))
{
if (tempid.equals(sess) )
{
if (adminmodel.department!="" && adminmodel.supervisor!="" && adminmodel.action!="" && adminmodel.date!="" && adminmodel.time!="")
{
try
{
con= DriverManager.getConnection("jdbc:oracle:thin:#localhost:1521:XE","gaurav","oracle");
stmt=con.prepareCall("begin requestdetail (?,?,?,?,?,?,?); end;");
stmt.setString(1,tempid);
stmt.setString(2,tsup);
stmt.setString(3,tdept);
stmt.setString(4,tact);
stmt.setString(5,tdate);
stmt.setString(6,rr);
stmt.setString(7,reff);
rs=stmt.executeQuery();
response.sendRedirect("requestnum.jsp");
}
catch(SQLException ex)
{
out.println(ex.getMessage());
}
catch(Exception ex)
{
out.println(ex.getMessage());
}
}
else
out.println("Enter complete details");
}
else
out.println("Incorrect Employee Id");
}
else
out.println("Invalid Details ");
}
catch(Exception ex)
{
}
}
public int doEndTag() throws JspException
{
check();
return super.doEndTag();
}
}
Below is the stored procedure
create or replace procedure requestdetail (id number, sup varchar2, department varchar2,aaction varchar2, adate number,atime number, ref number)
is
begin
insert into myadmin(employe_id,supervisor,department,action,sdate,stime,reference_no)values (id,sup,department,aaction,adate,atime, ref);
end;
/

You can't use a procedure in a query, only a function. To execute a procedure you need to do:
stmt=con.prepareCall("{ call requestdetail (?,?,?,?,?,?,?) }");
stmt.setString(1,tempid);
stmt.setString(2,tsup);
stmt.setString(3,tdept);
stmt.setString(4,tact);
stmt.setString(5,tdate);
stmt.setString(6,rr);
stmt.setString(7,reff);
rs=stmt.execute();
Or if you prefer you can pass a PL/SQL block:
stmt=con.prepareCall("begin; requestdetail (?,?,?,?,?,?,?); end;");
You will also need to commit at some point, unless you have auto commit turned on for the connection.
Also, you're setting all the parameters using setString, and you said in a comment that the table was created with all varchar2 columns; but your procedure is expecting numbers for the id, adate and atime columns:
create or replace procedure requestdetail (id number, sup varchar2,
department varchar2, aaction varchar2, adate number, atime number,
ref number) is
That will get a numeric or value error if the values you pass in for tempid, tdate and rr are not actually numbers. It looks like you're expecting tempid at least to be a string.
But if they are numeric (or, as the name suggests, date) values then your table columns should be of the appropriate type anyway, not all varchar2. Always use the correct data type; don't try to store numbers or dates as strings, it will only cause you pain later. It affects performance as Oracle can't optimise execution plans and indexes based on the actual data type. But more importantly because you can get invalid or just bad data, which you might not notice until much later, and will struggle to correct.
If someone inserts a string into your 'number' field nothing will stop them; but at some point when you query with where adate = 1 it'll throw an error. Same with dates, but even worse depending on the format(s) used - even with a single format, if you think everything is DD/MM/YYYY and someone puts in a value by mistake as MM/DD/YYYY, again you won't know; and it'll either work when retrieved (if the DD and MM are both <= 12) but have the wrong value and you will have no way to tell; or it'll fail when retrieved. If the column was DATE then it's up to the person inserting to get it right, not up to you to try and fix mistakes when you retrieve the data.

Related

JDBC: Call Oracle function like a procedure

I use an Oracle procedure and I batch it.
CallableStatement st = con.prepareCall ("{call MyProc (123)}");
...
st.addBatch ();
Now the procedure was converted into a function. Therefore batching is not longer working and ignoring the return-value does not work too.
If I do not set the "? =" the function is not found.
CallableStatement st = con.prepareCall ("{? = call MyFunc (123)}");
But without batching it takes too long to run all the calls. I have to do many of them.
So is there a way I can ignore the return-value on the JDBC level without touching the function? I want my batching back.
You may of course used the Oracle syntax and ignore the function value in the PL/SQL Block.
Something like this
con.prepareCall("""
declare
v_ignore number;
begin
v_ignore := MyFunc (?);
end;""")
for oracle db it will be like:
private Long getResultOfFunction(final long param1) {
CallableStatementCallback<Long> action = new CallableStatementCallback<Long>() {
public Long doInCallableStatement(CallableStatement cs)
throws SQLException, DataAccessException {
cs.registerOutParameter(1, Types.NUMERIC);
cs.setLong(2, param1);
cs.executeQuery();
return cs.getLong(1);
}
};
return getJdbcTemplate().execute("{call ? := package_name.function_name (?)}", action);
}

Readable output from Oracle procedure that returns a cursor in JMeter

I have a procedure that takes in one VARCHAR parameter and returns an OUT CURSOR which is a list, in this case 3 rows with 9 columns.
My current JDBC Request:
CALL foo.bar.procedure('123456', ?)
Query type: Callable Statement
Parameter values: OUT
Parameter types: OUT -10
Variable names: outList
Result variable name: resultList (this is always null, does it mean that the result is empty?)
Handle ResultSet: Store as Object
Response data:
-1 updates.
Output variables by position:
[1] oracle.jdbc.driver.OracleResultSetImpl#21512d0b
outList is now oracle.jdbc.driver.OracleResultSetImpl#21512d0b
I've tried getting some information from outList in a BeanShell sampler like is suggested here but I haven't been able to get anything from it except just the ResultSet object id.
I'm new to calling procedures through Jmeter, View Result Tree doesn't seem to show me any result data and I've been going back and forth with the SQL Query and the parameter values, trying to fix it, but I always have the same trouble with the output.
I've also tried something like this with similar settings:
DECLARE
refCursor sys_refcursor;
Type MyRec Is Record (
v1 varchar2(
v2 varchar2(50),
...
v13 varchar2(10));
rec MyRec;
BEGIN
foo.bar.procedure('123456',refCursor);
LOOP
FETCH refCursor INTO rec;
EXIT WHEN refCursor%NOTFOUND;
dbms_output.put_line(
rec.v1||','||
rec.v2||','||
...
rec.v13);
END LOOP;
END;
Am I calling the procedure correctly or is something missing in the JDBC Request settings?
I finally solved this by moving away from JDBC Request sampler and used the BeanShell Sampler instead.
import java.sql.*;
import oracle.jdbc.*;
import org.apache.jmeter.protocol.jdbc.config.DataSourceElement;
ResultSet rs = null;
ResultSetMetaData rsmd = null;
CallableStatement stmt;
// "myConnConfigName" is the 'JDBC Connection Configuration' variable name
Connection conn = DataSourceElement.getConnection("myConnConfigName");
try {
stmt = conn.prepareCall("CALL foo.bar.procedure(?,?)");
stmt.setString(1, "123456");
stmt.registerOutParameter(2, OracleTypes.CURSOR);
stmt.executeUpdate();
rs = (ResultSet) stmt.getObject(2);
while (rs.next()) {
rsmd = rs.getMetaData();
log.info("ColumnCount:" + rsmd.getColumnCount().toString());
log.info("RowNo:" + rs.getRow().toString());
// TODO: Store data.
// Loop through columns with rs.getString(i);
}
}
catch(Throwable ex) {
log.error("Error message: ", ex);
throw ex;
}
finally {
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
}
Add JSR223 PostProcessor as a child of your request
Extract the "interesting" values from the OracleResultSetImpl as per Retrieving and Modifying Values from Result Sets article, something like:
import java.sql.ResultSet
ResultSet rs = (ResultSet)vars.getObject('outList')
while (rs.next()) {
vars.put('column_1_row_1_value`, rs.getString(0))
///...
}
See JDBC Tutorial and Apache Groovy - Why and How You Should Use It for more details.

Need DB Table name for multiple queries executed using spring JDBCTemplate

I am executing multiple queries concurrently and retrieving the results. But, the queries belong to multiple tables so, when resultset is retrieved, it is difficult to identify that a resultset belong to which table.
Can anyone help here as to how to identify the table names for each query resultset?
I tried below code but table name is blank!!!!
public static void getColumnNames(ResultSet rs) throws SQLException {
if (rs == null) {
return;
}
// get result set meta data
ResultSetMetaData rsMetaData = rs.getMetaData();
int numberOfColumns = rsMetaData.getColumnCount();
// get the column names; column indexes start from 1
for (int i = 1; i < numberOfColumns + 1; i++) {
String columnName = rsMetaData.getColumnName(i);
// Get the name of the column's table name
String tableName = rsMetaData.getTableName(i);
System.out.println("column name=" + columnName + " table=" + tableName + "");
}
}
I am calling this method like this:
jdbcTemplate.query(sql, new ResultSetExtractor<ResultSet>() {
#Override
public ResultSet extractData(ResultSet resultSet) throws SQLException,
DataAccessException {
getColumnNames(resultSet);
return resultSet;
}
});
Please advise, what is done wrong here? :(
You're not doing anything wrong here. The problem is caused by the method itself in connection with your DBMS or your JDBC driver, respectively.
See this doc please. 'table name or "" if not applicable' suggests that in your case the DBMS/driver does not provide the required information, causing the method to return an empty string.
I'm afraid, you'll have to find another way to detect which query the result originated from.

Call Oracle Stored procedure from JDBC with complex Input and Output type

I'm so close in solving this question but I'm apparently missing something. My requirement is to call a stored procedure in Oracle from JDBC. The stored procedure takes 1 user-defined Oracle object as INput and another user-defined Oracle object as OUTput. The INput and OUTput objects have mix of both primitive Oracle data types and collection of another set of user-defined objects. I'm successfully able to call the stored procedure and get results back as long as I set NULL for the collection types in the INput and OUTput objects. If I try to create ArrayDescriptor for the list of Oracle objects to send it to the stored procedure I keep hitting roadblocks. So I need help with figuring out how to set the Array to the INput object and set that to CallableStatement. Please note, I am aware of how I can send the primitive type and array as direct inputs to the stored procedure. But I do not want to go that way as we later have to send 10 additional fields to the procedure, I do not want to add them to method signature. Here's the list of classes. Also, there is no compilation errors for the code below.
Package in oracle:
CREATE OR REPLACE PACKAGE testPkg AS
PROCEDURE spGetTestData (
TESTDATA_IN IN TESTDATA_IN_OBJ,
TESTDATA_OUT OUT TESTDATA_OUT_OBJ
);
END;
INput object for the stored procedure:
CREATE OR REPLACE TYPE TESTDATA_IN_OBJ AS OBJECT(
testStr1 VARCHAR2(5),
arrObj1 ARR_OBJ_1_NT);
Array Object as part of INput Object:
create or replace TYPE ARR_OBJ_1_NT AS TABLE OF ARR_OBJ_1_OBJ;
UserDefined Object part of INput Object:
CREATE OR REPLACE TYPE ARR_OBJ_1_OBJ AS OBJECT
(
teststr VARCHAR2(14),
testNumber NUMBER(4),
);
TestDataINObj.java:
import java.sql.Array;
import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;
public class TestDataINObj implements SQLData
{
private String sql_type = "TESTDATA_IN_OBJ";
protected String testStr1;
protected Array arrObj1;
#Override
public String getSQLTypeName() throws SQLException
{
return this.sql_type;
}
// getter and setter for fields
#Override
public void readSQL(SQLInput stream, String typeName) throws SQLException
{
this.sql_type=typeName;
this.testStr1 = stream.readString();
this.arrObj1 = stream.readArray();
}
#Override
public void writeSQL(SQLOutput stream) throws SQLException
{
stream.writeString(this.testStr1);
stream.writeArray(this.arrObj1);
}
}
TestDataINObjConverter.java:
public class TestDataINObjConverter
{
public static TestDataINObj convertPOJOToDBInObj(Connection connection)
throws SQLException
{
TestDataINObj testDataINObj = new TestDataINObj();
testDataINObj.setTestStr1("some string");
ArrObj1NT[] ArrObj1NTList = ArrObj1NTConverter.convertPOJOToDBObj(); // this will return Java array of ArrObj1NT class
testDataINObj.setArrObj1(getOracleArray("ARR_OBJ_1_NT",connection, ArrObj1NTList));
return testDataINObj;
}
private static Array getOracleArray(final String typeName, Connection connection, ArrObj1NT[] ArrObj1NTList) throws SQLException
{
if (typeName == null)
{
return null;
}
Array oracleArray = new ARRAY(new ArrayDescriptor(typeName, connection), connection, ArrObj1NTList);
return oracleArray;
}
Code that actually executes call to stored procedure:
... //code to get connection
..// connection is of type T4CConnection
Map typeMap = connection.getTypeMap();
typeMap.put("TESTDATA_IN_OBJ", TestDataINObj.class);
typeMap.put("TESTDATA_OUT_OBJ", TestDataOUTObj.class);
typeMap.put("ARR_OBJ_1_NT", ArrObj1NT.class);
TestDataINObj testDataINObj = TestDataINObjConverter.convertPOJOToDBInObj(connection);
getMetaDataCallableStatement = connection.prepareCall("begin " + "testPkg" + ".spGetTestData (?,?);"+ " end;");
getMetaDataCallableStatement.setObject(1, testDataINObj);
getMetaDataCallableStatement.registerOutParameter(2, Types.STRUCT, "TESTDATA_OUT_OBJ");
rs = getMetaDataCallableStatement.executeQuery();
TestDataOUTObj testDataOUTObj = (TestDataOUTObj) getMetaDataCallableStatement.getObject(2, typeMap);
Miscellaneous:
1. The objects are declared in Schema level and is available for the db user to access it.
2. I've not included all of the corresponding Java objects here as it will take more space. They implement SQLData interface and their type names match with DB names. The read and writeSQL methods uses getString, getArray and corresponding setter methods.
This is a very old approach, why are you not using "Oradata" and "Oradatum" interface?
It will save lot of effort.
Your approach leaves a lot of scopr for error, you will have to read the stream in proper manner and check for ordering of fields yourself which can be tricky. Oradata approach will do that for you.
Coming to your approach, Your code is not very clear.
But just to give an overview, StructDescriptor will map to oracle record type and ArrayDescriptor will map to oracle table type, from your code i am confused about whta you are trying to achieve.
I can help if you can make it more clear.

Stored Procedure does not and should not return anything results in error

I have a stored Procedure that we are using to simply do an insert of a new record into various tables. This Stored Procedure is setup to not return anything only to execute. When I pull it into my DBML it sets it up as a "Void" which I think is correct. Here is the auto generated code from the DMBL.
[global::System.Data.Linq.Mapping.FunctionAttribute(Name="dbo.sp_PTS_Action_Insert_Comment")]
public void sp_PTS_Action_Insert_Comment([global::System.Data.Linq.Mapping.ParameterAttribute(Name="Form_Name", DbType="VarChar(20)")] string form_Name, [global::System.Data.Linq.Mapping.ParameterAttribute(Name="Form_Number", DbType="VarChar(25)")] string form_Number, [global::System.Data.Linq.Mapping.ParameterAttribute(Name="UID", DbType="VarChar(15)")] string uID, [global::System.Data.Linq.Mapping.ParameterAttribute(Name="Cmt", DbType="VarChar(200)")] string cmt)
{
this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), form_Name, form_Number, uID, cmt);
}
}
I call this SP as follows:
_context.sp_PTS_Action_Insert_Comment(formInformation["formType"], formInformation["formNumber"], userId, comment);
When this code executes it throws an error saying
"System Void not a valid return type
for a mapped Stored Procedure"
My question is can you use linq to Stored Procedure with SP's that purposely do not return anything? And if so what am I doing wrong in this example?
I'm unsure of why, when putting a procedure that doesn't return anything, that Linq builds the above void function, because it DOES throw the 'not a valid return type' error.
To fix this I've added a return to the procedure using SCOPE_IDENTITY() as my return so then I can use it in my code also.
Hope that helps.

Resources