I am creating a web app which requires connecting to a database and getting the catagories of various types of reports. I am getting a weird error when it comes to executing my SQL via storedproc in java. SELECT * FROM RPT_CTGR; is the sql I submit but when I look at the stack trace it comes back as this:
org.springframework.jdbc.BadSqlGrammarException: CallableStatementCallback; bad SQL grammar [{call SELECT * FROM RPT_CTGR;(?)}]; nested exception is java.sql.SQLException: ORA-06550: line 1, column 38:
PLS-00103: Encountered the symbol ")" when expecting one of the following:
. ( * # % & = - + < / > at in is mod remainder not rem
<an exponent (**)> <> or != or ~= >= <= <> and or like like2
like4 likec as between || indicator multiset member
submultiset
org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:98)
org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:969)
org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:1003)
org.springframework.jdbc.object.StoredProcedure.execute(StoredProcedure.java:144)
org.ifmc.qies.reportaudit.impl.CataImpl.search(CataImpl.java:59)
org.ifmc.qies.reportaudit.web.CataAction.execute(CataAction.java:31)
org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431)
org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236)
org.apache.struts.action.ActionServlet.process(ActionServlet.java:1196)
org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432)
javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Which for some reason is appending the extra "(?)" to the end of my statement which causes the error. Any ideas?
Code
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.ifmc.qies.reportaudit.dao.CataDao;
import org.ifmc.qies.reportaudit.model.Catagory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import oracle.jdbc.OracleTypes;
public class CataImpl extends JdbcDaoSupport implements CataDao {
protected static ApplicationContext ctx = null;
//BaseStoredProcedure extends StoredProcedure
public class CataProc extends BaseStoredProcedure{
public CataProc(DataSource ds, String name) {
super(ds,name);
System.out.println(name);
System.out.println(getSql());
declareParameter(new SqlOutParameter("catagories", OracleTypes.CURSOR,
new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum)
throws SQLException {
Catagory t = new Catagory();
t.setCatId(rs.getString(1));
t.setCat_name(rs.getString(2));
System.out.println(t.getCat_name());
return t;
}
}));
}
}
#Override
public List search() {
String[] paths = {"V:path/to/applicationcontext.xml"};
Map params=new HashMap();
ctx = new FileSystemXmlApplicationContext(paths);
DataSource ds = (DriverManagerDataSource)ctx.getBean("dataSource");
CataProc proc = new CataProc(ds,"SELECT * FROM RPT_CTGR;");
Map results = proc.execute(params);
List catagory = (List)results.get("catagories");
//Test
System.out.println(catagory.get(1).toString());
return catagory;
}
}
I get all my print statements except for the catagories.get(1).toString to return properly the SQL I submitted
You should create a Store Procedure in your Oracle Database which executes your SELECT statement.
You can see this question as reference on how to do it.
E.g.
CREATE OR REPLACE PROCEDURE getCatagories(categories in out sys_refcursor) is
...
SELECT * FROM RPT_CTGR;
...
After that, you need to call the store procedure by its name.
public List search() {
...
DataSource ds = (DriverManagerDataSource) ctx.getBean("dataSource");
CataProc proc = new CataProc(ds,"getCatagories");
Map results = proc.execute(params);
List catagory = (List)results.get("catagories");
...
}
Related
I'm trying to make an insert and get the auto generated id returned. This is an Oracle database. For that i am using org.apache.commons.dbutils.QueryRunner insert which returns the first column.
The problem is i don't know for which reason i am getting added a parameter in the query and it won't let it work.
I have this code:
import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Optional;
#Singleton
public class ConceptToBonusRepository extends GenericSQLRepository{
private static final String QUERY_SAVE_CONCEPT_TO_BONUS = "INSERT INTO concept_to_bonus (ID, CONCEPT_ID, CONCEPT_TYPE)" +
" VALUES (SEQ_CONCEPT_TO_BONUS_ID.NEXTVAL,?,?)";
#Inject
public ConceptToBonusRepository(#Named("OracleDataSource") #Nonnull DataSource dataSource) {
super(dataSource);
}
public Optional<Long> saveConceptToBonus(ConceptToBonus conceptToBonus)
try {
return Optional.ofNullable(
runInsert(
QUERY_SAVE_CONCEPT_TO_BONUS, conceptToBonus.getConceptId(), conceptToBonus.getConceptType()
)
);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
and my GenericlSQLRepository
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import javax.annotation.Nonnull;
import javax.sql.DataSource;
import java.sql.SQLException;
public abstract class GenericSQLRepository {
private QueryRunner queryRunner;
private ScalarHandler<Long> autogeneratedIdHandler = new ScalarHandler<>();
protected GenericSQLRepository(#Nonnull final DataSource dataSource) {
this.queryRunner = new QueryRunner(dataSource);
}
protected Long runInsert(#Nonnull final String sql,
Object... args) throws SQLException {
return queryRunner.insert(sql, this.autogeneratedIdHandler, args);
}
}
When i try to run this i get this error
"java.sql.SQLException: Wrong number of parameters: expected 3, was given 2 Query: INSERT INTO concept_to_bonus (ID, CONCEPT_ID, CONCEPT_TYPE) VALUES (SEQ_CONCEPT_TO_BONUS_ID.NEXTVAL,?,?) Parameters: [1731472066, ORDER]"
I really don't understand why is it adding a parameter in the parameter count. When i run this insert with a simple execute, it works just fine
I have a simple Query method that runs cypher queries as noted below. If I run the EXACT same query in the web console (yes, same db instance, correct path), I get a non-empty iterator in the console. Shouldn't I 1) not get that message and 2) get the results I see in my database?
This class has other methods that add data to the database and that functionality works well. This query method is not working...
Class:
import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.helpers.collection.IteratorUtil;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.sql.*;
public class NeoProcessor {
//private GraphDatabaseService handle;
private static final String DB_PATH = "/usr/local/Cellar/neo4j/2.0.1/libexec/data/new_graph.db";
static GraphDatabaseService graphDb = new GraphDatabaseFactory().newEmbeddedDatabase( DB_PATH );
public NeoProcessor()
{
}
public void myQuery(String cypherText)
{
//System.out.println("executing the above query");
cypherText = "MATCH (n:Phone{id:'you'}) MATCH n-[r:calling]->m WHERE n<>m RETURN n, r, m";
ExecutionEngine engine = new ExecutionEngine( this.graphDb );
ExecutionResult result;
try ( Transaction ignored = graphDb.beginTx() )
{
result = engine.execute( cypherText + ";");
System.out.println(result);
ignored.success();
}
}
}
Below is a pic showing how the query rreturns results from the DB:
result = engine.execute(cypherText + ";");
System.out.println(result.dumpToString());
Specified by:
http://api.neo4j.org/2.0.3/org/neo4j/cypher/javacompat/ExecutionResult.html#dumpToString()
To consume the result you need to use the iterator. If you just want a string representation use the ExecutionResult.dumpToString(). Be aware this method exhausts the iterator.
You should be calling:
System.out.println(result.dumpToString)
Which will prettify it for you. Of course, there is always the possibility that your match returns no results. You shouuld also close the transaction in a finally block, although that won't matter much here.
EDIT: Taking a second look at this, your Cypher query is wrongly formed, It should be
MATCH (n:Phone) - [r:calling] -> (m)
WHERE n.id = `you'
RETURN n, r, m
This is a continuation of the question posted under the following location:
Java program to pass List of Bean to a oracle stored procedure - Pass entire list at one shot rather than appending objects one after the other
I have been trying to enhance the stored procedure mentioned in the above link location and am confused in the implementation. Rather than VARCHAR2 as a output from the procedure i now want to return NUM_ARRAY as the output from the procedure. Can you please help me in implementing the logic to read the NUM_ARRAY in my java code. Normally output is returned using Map out = super.execute(inParams); How can i now extract the NUM_ARRAY to my bean?
The source code implementation is as follows.
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import oracle.jdbc.OracleTypes;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import org.apache.log4j.Logger;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlTypeValue;
import org.springframework.jdbc.object.StoredProcedure;
public class RevPrdBrkDwnSP extends StoredProcedure{
private final Logger log = Logger.getLogger(this.getClass().getName());
public RevPrdBrkDwnSP(DataSource dataSource, String storeProcName) {
// Run the Parent
super(dataSource, storeProcName);
// Declare the Parameter Details
declareParameter(new SqlParameter("IN_ARRAY", OracleTypes.ARRAY, "****.PROD_PRCT_BRKDWN_TYPE_ARRAY"));
declareParameter(new SqlOutParameter("OUT_ARRAY", OracleTypes.ARRAY, "****.PROD_PRCT_BRKDWN_TYPE_ARRAY"));
// Compile the SP
compile();
}
public boolean execute(final RevAppViewBean appViewBean$Session, final DataSource dataSource) throws Exception {
boolean returnVal = false;
Map<String, Object> inParams = new HashMap<String, Object>();
log.info("Setting up the Store Procedure Params");
inParams.put("IN_ARRAY", new SqlTypeValue() {
public void setTypeValue(PreparedStatement cs, int index, int sqlType, String typeName) throws SQLException {
Connection con = cs.getConnection();
ArrayDescriptor des = ArrayDescriptor.createDescriptor("****.PROD_PRCT_BRKDWN_TYPE_ARRAY", con);
ARRAY a = new ARRAY(des, con, appViewBean$Session.getExcelRecLst().toArray());
cs.setObject(1, (Object)a);
}
});
inParams.put("OUT_ARRAY", identifier); // what should the identifier be ?????????
if (log.isDebugEnabled()) {
log.debug("Executing the **** Store Procedure ");
}
Map out = super.execute(inParams); // how to get the same array as value ??????
log.info("output size is --------------------->>>>>>>>>> "+out.size());
for(Object o : out.keySet()){
log.info((String)out.get(o));
returnVal = Boolean.parseBoolean((String)out.get(o));
}
if (log.isDebugEnabled()) {
log.info("Output from **** Store Procedure :" + out);
}
return returnVal;
}
}
Update:
After making use of the Spring Data JDBC Extension the source code had to be changed to accommodate the the new response which is pasted below, but the problem of connection still exists when the bean.getAttributes() method is called. Looks like a way needs to be found to not close the connection or access the values before the connection gets closed.
Map out = super.execute(inParams);
log.info("output size is --------------------->>>>>>>>>> "+out.size()); //prints the actual value
Object[] idOutArraz = (Object[])out.get("OUT_ARRAY");
log.info("size of returnValue is "+idOutArraz.length); //prints the right number of results
for(int i= 0; i<idOutArraz.length;i++){
Object[] attrs = null;
Struct bean = (Struct) idOutArraz[i];
attrs = bean.getAttributes();
if (attrs != null) {
System.out.println(Arrays.asList(attrs));
}
}
Answered after lot many trial and errors with different approaches.
After trying to implement lot many solutions the Callable statement worked for me. Looks like a workaround, but any solution to resolve the actual implementation is welcome.
Please find below the working copy of the implementation.
import java.math.BigDecimal;
import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import oracle.jdbc.OracleTypes;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import org.apache.log4j.Logger;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.StoredProcedure;
import com.****.****.****.ExcelListenerBean;
import com.****.****.****.RevAppViewBean;
public class RevPrdBrkDwnSP extends StoredProcedure{
private final Logger log = Logger.getLogger(this.getClass().getName());
private Connection con = null;
private DataSource ds = null;
public RevPrdBrkDwnSP(DataSource dataSource, String storeProcName) throws SQLException {
// Run the Parent
super(dataSource, storeProcName);
con = dataSource.getConnection();
ds = dataSource;
if (log.isInfoEnabled()) {
log.info("Stored Procedure Name : "+ storeProcName);
}
// Declare the Parameter Details
declareParameter(new SqlParameter("IN_ARRAY", OracleTypes.ARRAY, "****.PROD_PRCT_BRKDWN_TYPE_ARRAY"));
declareParameter(new SqlOutParameter("OUT_ARRAY", OracleTypes.ARRAY, "****.PROD_PRCT_BRKDWN_TYPE_ARRAY"));
// Compile the SP
compile();
}
public List<ExcelListenerBean> execute(final RevAppViewBean appViewBean$Session, DataSource dataSource) throws Exception {
dataSource = ds;
List<ExcelListenerBean> beans = new ArrayList<ExcelListenerBean>();
log.info("Setting up the Store Procedure Params");
String getDBUSERByUserIdSql = "{call ****.PRCS_PROD_PRCT_BRKDWN_ENTRIES(?,?)}";
CallableStatement cs = con.prepareCall(getDBUSERByUserIdSql);
ArrayDescriptor des = ArrayDescriptor.createDescriptor("PBAREV.PROD_PRCT_BRKDWN_TYPE_ARRAY", con);
ARRAY a = new ARRAY(des, con, appViewBean$Session.getExcelRecLst().toArray());
cs.setObject(1, (Object)a);
cs.registerOutParameter(2, OracleTypes.ARRAY, "****.PROD_PRCT_BRKDWN_TYPE_ARRAY");
if (log.isDebugEnabled()) {
log.debug("Executing the PBAREV Store Procedure ");
}
cs.execute();
log.info("Executed ****.PRCS_PROD_PRCT_BRKDWN_ENTRIES... Processing values to beans");
Array arr = cs.getArray(2);
Object[] objArr = (Object[]) arr.getArray();
for(int i=0; i<objArr.length;i++){
STRUCT st = (STRUCT)objArr[i];
ExcelListenerBean bean = new ExcelListenerBean();
Object[] obj = st.getAttributes();
bean.setPrntGdwIdN(((BigDecimal)obj[1]).longValue());
bean.setChldGdwIdN(((BigDecimal)obj[2]).longValue());
bean.setChldAsetPrcntN(Double.valueOf(String.valueOf(obj[4])));
bean.setStatus(String.valueOf(obj[8]));
bean.setStatusMessage(String.valueOf(obj[9]));
beans.add(bean);
}
if (log.isDebugEnabled()) {
log.info("Finised processing SP output values to ExcelListenerBeans");
}
return beans;
}
}
On the Oracle side, your code could look like this:
Global type delcration:
CREATE OR REPLACE TYPE NUM_ARRAY AS TABLE OF NUMBER;
Stored procedure:
CREATE OR REPLACE PROCEDURE PROD_PRCT_BRKDWN_TYPE_ARRAY (
in_array IN NUM_ARRAY,
out_status OUT VARCHAR2)
IS
...
Plain JDBC code (with some Oracle specific parts):
Connection con = ...;
CallableStatementcs = con.prepareCall(" ... ");
ArrayDescriptor des = ArrayDescriptor.createDescriptor("PBAREV.PROD_PRCT_BRKDWN_TYPE_ARRAY", con);
Integer[] idArray = new Integer[50000];
// fill the array of integers here
for (int i = 0; i < idArray.length; i++)
idArray[i] = ....;
ARRAY a = new ARRAY(des, con, idArray);
cs.setObject(1, (Object)a);
cs.registerOutParameter(2, OracleTypes.ARRAY, "PBAREV.PROD_PRCT_BRKDWN_TYPE_ARRAY");
cs.execute();
ARRAY outArray = (ARRAY)cs.getArray(2);
Integer[] idOutArraz = (Integer[])outArray.getArray();
I haven't tested the code. But it should give you an idea.
Update:
For the conversion to the Spring Framework, you might want to look at the Spring Data JDBC Extension project that contains the class org.springframework.data.jdbc.support.oracle.SqlReturnArray and declare you parameter like this:
declareParameter(new SqlOutParameter("OUT_ARRAY", Types.ARRAY,
"PBAREV.PROD_PRCT_BRKDWN_TYPE_ARRAY", new SqlReturnArray()));
I wonder what the Map of the execute method contains for the out array because the documentation doesn't say anything.
Can somebody give one java example of sparql insert/Delete query in Stardog.
there is only queryExecution.execSelect() method available.
there is no queryExecution.execInsert() or queryExecution.execDelete() available.
Please give one working example.
EDIT
I've found this this from stardog docs page.
http://stardog.com/docs/#notes
As of 1.1.5, Stardog's SPARQL 1.1 support does not include: UPDATE query language
does that means no way out for editing a tuple once entered?
Stardog does not yet support SPARQL update, but as was pointed out to you on the mailing list, there are 5 ways you can modify the data once it's loaded. You can use our HTTP protocol directly, any of the 3 Java API's we support, or you can use the command line interface.
Below one is a sample program of inserting a graph and removing it.
package com.query;
import java.util.List;
import org.openrdf.model.Graph;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.impl.GraphImpl;
import org.openrdf.model.impl.ValueFactoryImpl;
import org.openrdf.query.QueryEvaluationException;
import com.clarkparsia.stardog.StardogDBMS;
import com.clarkparsia.stardog.StardogException;
import com.clarkparsia.stardog.api.Connection;
import com.clarkparsia.stardog.api.ConnectionConfiguration;
import com.clarkparsia.stardog.jena.SDJenaFactory;
import com.hp.hpl.jena.query.ParameterizedSparqlString;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.query.ResultSetFormatter;
import com.hp.hpl.jena.rdf.model.Model;
public class test {
public static void main(String[] args) throws StardogException, QueryEvaluationException {
String appDbName ="memDb";
String selectQuery="SELECT DISTINCT ?s ?p ?o WHERE { ?s ?p ?o }";
StardogDBMS dbms = StardogDBMS.toServer("snarl://localhost:5820/")
.credentials("admin", "admin".toCharArray()).login();
List<String> dbList = (List<String>) dbms.list();
if (dbList.contains(appDbName)) {
System.out.println("droping " + appDbName);
dbms.drop(appDbName);
}
dbms.createMemory(appDbName);
dbms.logout();
Connection aConn = ConnectionConfiguration
.to("memDb") // the name of the db to connect to
.credentials("admin", "admin") // credentials to use while connecting
.url("snarl://localhost:5820/")
.connect();
Model aModel = SDJenaFactory.createModel(aConn);
System.out.println("################ GRAPH IS EMPTY B4 SUBMITTING ="+aModel.getGraph()+ "################");
URI order = ValueFactoryImpl.getInstance().createURI("RDF:president1");
URI givenName = ValueFactoryImpl.getInstance().createURI("RDF:lincoln");
URI predicate = ValueFactoryImpl.getInstance().createURI("RDF:GivenNane");
Statement aStmt = ValueFactoryImpl.getInstance().createStatement(order,predicate,givenName);
Graph aGraph = new GraphImpl();
aGraph.add(aStmt);
insert(aConn, aGraph);
ParameterizedSparqlString paraQuery = new ParameterizedSparqlString(selectQuery);
QueryExecution qExecution = QueryExecutionFactory.create(paraQuery.asQuery(),aModel);
ResultSet queryResult = qExecution.execSelect();
System.out.println("############### 1 TUPPLE CAME AFTER INSERT ################");
ResultSetFormatter.out(System.out, queryResult);
aGraph.add(aStmt);
remove(aConn, aGraph);
paraQuery = new ParameterizedSparqlString(selectQuery);
qExecution = QueryExecutionFactory.create(paraQuery.asQuery(),aModel);
queryResult = qExecution.execSelect();
System.out.println("################ DB AGAIN EMPTY AFTER REMOVE ################");
ResultSetFormatter.out(System.out, queryResult);
System.out.println("closing connection and model");
aModel.close();
aConn.close();
}
private static void insert(final Connection theConn, final Graph theGraph) throws StardogException {
theConn.begin();
theConn.add().graph(theGraph);
theConn.commit();
}
private static void remove(final Connection theConn, final Graph theGraph) throws StardogException {
theConn.begin();
theConn.remove().graph(theGraph);
theConn.commit();
}
}
In Oracle DB:
I have the following stored procedure:
procedure getInfo ( p_ids IN IDS_TABLE, p_details OUT cursor )
Type IDS_TABLE is:
create or replace type IDS_TABLE as table of IDS
create or replace type IDS as object ( id1 NUMBER, id2 NUMBER, id3 NUMBER )
How can I call getInfo in Java?
Setting up a link between Oracle SQL objects and java objects manually is not a trivial task. In particular, arrays (or nested tables) of user-defined objects are more complex to pass from java to Oracle than arrays of standard datatypes. In other words, it is easier to call a procedure with signature:
(TABLE OF NUMBER, TABLE OF NUMBER, TABLE OF NUMBER)`
than a procedure whose signature is:
(TABLE OF (NUMBER, NUMBER, NUMBER)) <- your case
You can write a wrapper around your procedure to transform the second case into the first case.
That being said, it is by far not impossible to map your procedure. The following example is largely inspired by a post by Tom Kyte. Tom describes how to map a TABLE OF NUMBER using oracle.sql.ARRAY. In your case we will also have to use oracle.sql.STRUCT to map the IDS SQL object.
You may also want to browse the Oracle JDBC doc, in particular the chapter Working with Oracle Object Types.
First is a setup similar to yours:
SQL> CREATE OR REPLACE TYPE IDS AS OBJECT ( id1 NUMBER, id2 NUMBER, id3 NUMBER );
2 /
Type created
SQL> CREATE OR REPLACE TYPE IDS_TABLE AS TABLE OF IDS;
2 /
Type created
SQL> CREATE OR REPLACE PROCEDURE getInfo(p_ids IN IDS_TABLE) IS
2 BEGIN
3 FOR i IN 1 .. p_ids.COUNT LOOP
4 dbms_output.put_line(p_ids(i).id1
5 || ',' || p_ids(i).id2
6 || ',' || p_ids(i).id3);
7 END LOOP;
8 END getInfo;
9 /
Procedure created
This is the java procedure:
SQL> CREATE OR REPLACE
2 AND COMPILE JAVA SOURCE NAMED "ArrayDemo"
3 as
4 import java.io.*;
5 import java.sql.*;
6 import oracle.sql.*;
7 import oracle.jdbc.driver.*;
8
9 public class ArrayDemo {
10
11 public static void passArray() throws SQLException {
12
13 Connection conn =
14 new OracleDriver().defaultConnection();
15
16
17 StructDescriptor itemDescriptor =
18 StructDescriptor.createDescriptor("IDS",conn);
19
20 Object[] itemAtributes = new Object[] {new Integer(1),
21 new Integer(2),
22 new Integer(3)};
23 STRUCT itemObject1 = new STRUCT(itemDescriptor,conn,itemAtributes);
24
25 itemAtributes = new Object[] {new Integer(4),
26 new Integer(5),
27 new Integer(6)};
28 STRUCT itemObject2 = new STRUCT(itemDescriptor,conn,itemAtributes);
29
30 STRUCT[] idsArray = {itemObject1,itemObject2};
31
32 ArrayDescriptor descriptor =
33 ArrayDescriptor.createDescriptor( "IDS_TABLE", conn );
34
35 ARRAY array_to_pass =
36 new ARRAY( descriptor, conn, idsArray );
37
38 OraclePreparedStatement ps =
39 (OraclePreparedStatement)conn.prepareStatement
40 ( "begin getInfo(:x); end;" );
41
42 ps.setARRAY( 1, array_to_pass );
43 ps.execute();
44
45 }
46 }
47 /
Java created
Let's call it:
SQL> CREATE OR REPLACE
2 PROCEDURE show_java_calling_plsql
3 AS LANGUAGE JAVA
4 NAME 'ArrayDemo.passArray()';
5 /
Procedure created
SQL> exec show_java_calling_plsql ;
1,2,3
4,5,6
PL/SQL procedure successfully completed
If you're using Spring, you may want to look at Spring Data JDBC Extensions, which provides a SqlArrayValue type.
Chapter 7.2.1 Setting ARRAY values using SqlArrayValue for an IN parameter explains how to call procedures with array parameters.
This is a pretty good example. if you see java.sql.SQLException: invalid name pattern: still. Check the scope of the type that you declared in Oracle. I am using Oracle 11g and had to declare both Object of String Array and Table of Objects of my type in schema level. Spent some 3 hours and found that.
oracle.sql.StructDescriptor docObjDescriptor = StructDescriptor.createDescriptor("SSIADM.DOCUMENT_OBJECT",conn);
String[] strArray = new String[] {"doc1","file1"};
oracle.sql.STRUCT DocObject1 = new STRUCT(docObjDescriptor,conn,strArray);
strArray = new String[] {"doc2","file2"};
oracle.sql.STRUCT DocObject2 = new STRUCT(docObjDescriptor,conn,strArray);
oracle.sql.STRUCT[] docObjArray = {DocObject1,DocObject2};
arrDesc = ArrayDescriptor.createDescriptor("DOCUMENT_TABLE", conn);
oracle.sql.ARRAY array = new ARRAY(arrDesc, conn, docObjArray);
The solution I used lets Spring parse the object instead of having to manually create the STRUCT arrays.
Unfortunately, it still isn't environment independent.
Stored Proc DAO:
package ****.dao.storedProcedures;
import java.sql.Array;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlTypeValue;
import org.springframework.jdbc.object.StoredProcedure;
import ****.persistent.ComplexTypeObj;
import ****.persistent.InnerType;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
public class SaveStoredProc extends StoredProcedure implements InitializingBean {
public static final String IT_COMPLEX_TYPE = "it_complex_type";
public SaveStoredProc() {
}
#Override
public void afterPropertiesSet() {
Validate.notNull(getJdbcTemplate());
super.setFunction(true);
super.declareParameter(new SqlOutParameter(RESULT, Types.NUMERIC));
super.declareParameter(new SqlParameter(IT_COMPLEX_TYPE, Types.OTHER, ComplexTypeObj.ORACLE_OBJECT_NAME));
compile();
}
public long execute(final ComplexTypeObj complexTypeObj) {
Map<String, Object> inParameters = new HashMap<String, Object>();
inParameters.put(IT_COMPLEX_TYPE, new ComplexSqlTypeValue(complexTypeObj));
#SuppressWarnings("unchecked")
Map<String, Object> resp = super.execute(inParameters);
return ((Number)resp.get(RESULT)).longValue();
}
private static final class ComplexSqlTypeValue implements SqlTypeValue {
private final Log logger = LogFactory.getLog(getClass());
private final ComplexTypeObj complexTypeObj;
public ComplexSqlTypeValue(ComplexTypeObj complexTypeObj) {
this.complexTypeObj = complexTypeObj;
}
#Override
public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName) throws SQLException {
Connection conn = ps.getConnection();
try {
conn = conn.unwrap(oracle.jdbc.OracleConnection.class);
} catch (Exception e) {
logger.debug("Could not unrap connection");
}
Map<String, Class<?>> typeMap = conn.getTypeMap();
typeMap.put(typeName, ComplexTypeObj.class); //The name of the outer object type.
typeMap.put(InnerType.ORACLE_OBJECT_NAME, InnerType.class); //The name of the inner object type.
ArrayDescriptor des = ArrayDescriptor.createDescriptor(InnerType.ORACLE_LIST_NAME, conn); //The name of the inner list type.
Array objArray = new ARRAY(des, conn, complexTypeObj.getInnerList().toArray());
complexTypeObj.setInnerArray(objArray);
ps.setObject(paramIndex, complexTypeObj);
}
}
}
Outer Type:
import java.sql.*;
import java.util.*;
public class OuterType extends BaseSQLData implements SQLData {
public static final String ORACLE_OBJECT_NAME = "T_OUTER_TYPE";
private List<InnerType> innerList;
private Array innerArray;
public OuterType() {
this.innerList = new ArrayList<InnerType>();
}
public String getSQLTypeName() throws SQLException {
return ORACLE_OBJECT_NAME;
}
#Override
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeArray(innerArray);
}
Inner Type:
public final class InnerType extends BaseSQLData {
public static final String ORACLE_OBJECT_NAME = "T_INNER_TYPE";
public static final String ORACLE_LIST_NAME = "T_INNER_TYPE_LIST";
private String valueA;
private Long valueB = 0;
public String getSQLTypeName() throws SQLException {
return ORACLE_OBJECT_NAME;
}
#Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
throw new UnsupportedOperationException("This class doesn't support read opperations.");
}
#Override
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(valueA);
stream.writeBigDecimal(valueB == null ? null : new BigDecimal(valueB.toString()));
}