I've a class "User" as follows:
public class User{
private String firstname;
private String lastname;
private String[] departments;
}
I want to pass an object of User type to my oracle stored procedure that takes an object type as parameter. So I did this to achieve my solution:-
1. Created USER_SEARCH_OBJ as a type in database as follows:-
create or replace
TYPE USER_SEARCH_OBJ AS OBJECT (
FIRST_NAME VARCHAR2(256),
LAST_NAME VARCHAR2(256),
DEPARTMENTS TABLE_OF_VALUES,
);
2. Created TABLE_OF_VALUES as a type in database as follows:-
create or replace TYPE TABLE_OF_VALUES AS TABLE OF VARCHAR2(20);
3. Passed the User object from Java class as follows:-
Object[] departments = {"1","2"};
StructDescriptor objDescriptor = StructDescriptor.createDescriptor("USER_SEARCH_OBJ", conn.getMetaData().getConnection());
ArrayDescriptor arrayDescriptor = ArrayDescriptor.createDescriptor("TABLE_OF_VALUES", conn.getMetaData().getConnection());
ARRAY departmentArr = new ARRAY(arrayDescriptor, conn.getMetaData().getConnection(), departments );
Object[] userProperties = new Object[2];
userProperties [0] = "paras";//first_name
userProperties [1] = "anand";//last_name
userProperties [2] = departmentArr ;//department array
STRUCT searchObj = new STRUCT(objDescriptor, conn.getMetaData().getConnection(), userProperties );
CallableStatement cStmt = conn.prepareCall("PCK_SEARCH2.USER_SEARCH(?,?)");
cStmt.setObject(1, searchObj);
cStmt.registerOutParameter(2, OracleTypes.CURSOR);
cStmt.execute();
But when I run this code I get an exception as follows:-
java.sql.SQLException: Inconsistent java and sql object types
at oracle.sql.StructDescriptor.toOracleArray(StructDescriptor.java:709)
at oracle.sql.StructDescriptor.toArray(StructDescriptor.java:1296)
at oracle.sql.STRUCT.<init>(STRUCT.java:165)
at com.ensenda.lmp.web.controller.User.main(User.java:75)
This exception comes at the following line of code:-
STRUCT searchObj = new STRUCT(objDescriptor, conn.getMetaData().getConnection(), userProperties );
Please let me know where I'm going wrong.
Firstly I think you should get an ArrayOutOfBoundException because you have declared an Object[2] and trying to put a 3rd element into it. Considering that is a typo.
For the original problem instead of using setObject you can use
*JDBCUtil.setStruct(conn,cStmt,1,"USER_SEARCH_OBJ",userProperties);*
Related
I'm using MyBatis / Spring-Boot
I'm learning it :)
I get this error message:
Mapper method attempted to return null from a method with a primitive return type (int)
I tried different way to write it, with the help of different advises in forum. None working
There is the code:
#Select("INSERT INTO TB_Users(userId, firstName, lastName) VALUES( #{userId}, #{firstName}, #{lastName})")
#SelectKey(statement = "SELECT LAST_INSERT_ID()", keyProperty = "id", before = false, resultType = Integer.class)
public int save(UserEntity userEntity);
Thanks for your help
I'm trying to call an oracle stored function using eclipselink 2.6.5, the storedfunction has an oracle type input.
This is te stored func:
create or replace function TEST_FUNC(PAR1 IN VARCHAR2, PAR2 IN MY_TYPE_T)
return varchar2 is
begin
if PAR2 is null then
return (PAR1 || ' 0');
else
return(PAR1 || ' ' || PAR2.count);
end if;
end TEST_FUNC;
This is type declaration:
CREATE OR REPLACE MY_TYPE_T as table of MY_TYPE_R
CREATE OR REPLACE TYPE MY_TYPE_R as object
(
COD_P VARCHAR2(3),
COD_S VARCHAR2(10)
)
This is the type mapping:
#Embeddable
#Struct(name="MY_TYPE_R", fields= {"COD_P", "COD_S"})
#PLSQLTable(
name="MY_TYPE_R",
compatibleType="MY_TYPE_T",
nestedType="MY_TYPE_R"
)
public class MyRecordType {
#Column(name="COD_P")
private String codP;
#Column(name="COD_S")
private String codS;
//costructors, getter and setter...
}
This is the stored mapping:
#NamedPLSQLStoredFunctionQuery(
name="testFunc",
functionName="TEST_FUNC",
returnParameter=#PLSQLParameter(
name="RESULT",
databaseType = "VARCHAR_TYPE"),
parameters = {
#PLSQLParameter(name = "firstParam", queryParameter="PAR1", databaseType = "VARCHAR_TYPE"),
#PLSQLParameter(name = "secondParam", queryParameter="PAR2", databaseType = "MY_TYPE_T"),
}
)
This is the call:
#Test
public void testFuncTest() {
List<MyRecordType> recTypeList = new ArrayList<MyRecordType>();
MyRecordType rec = new MyRecordType();
rec.setCodP("PValue");
rec.setCodS("SValue");
recTypeList.add(rec);
Query query = getEM().createNamedQuery("testFunc");
query.setParameter("firstParam", "FOO");
query.setParameter("secondParam", recTypeList);
Assert.assertEquals("FOO 1", query.getSingleResult());
}
When I execute the test I get:
PLS-00306: wrong number or types of arguments in call to 'TEST_FUNC'
Actually reading the documentation on #PLSQTable I'm a bit confused on the tree parameters:
name
compatibleType
nestedType
Anyone has some suggestion?
First you'll want to use the OracleObject annotation to define your fields into an Oracle object. It's somewhat duplicate to Struct but required anyways.
#OracleObject(
name = "MY_TYPE_R"
, javaType = MyRecordType.class
, fields = {
#PLSQLParameter(name = "COD_P")
,#PLSQLParameter(name = "COD_S")
}
)
For the PLSQLTable you want
#NamedPLSQLStoredFunctionQuery(
name="testFunc",
functionName="TEST_FUNC",
returnParameter=#PLSQLParameter(
name="RESULT",
databaseType = "VARCHAR_TYPE"),
parameters = {
#PLSQLParameter(name = "PAR1", queryParameter="PAR1", databaseType = "VARCHAR_TYPE"),
#PLSQLParameter(name = "PAR2", queryParameter="PAR2", databaseType = "MY_TYPE_T"),
}
)
so the name needs to match what's in your function. The PLSQL table piece looks right, I believe the OracleObject part was your missing component and causing your error.
OrientDB question...
Does anyone know how I can get the recordId after an insert:
db.save(person)
I tried below on the Person POJO:
#Id
private Object id;
but the id field was null after the save. I've googled and googled to no avail. I just need to insert an object, then get the recordid that orientdb generates.
Define field in pojo:
#Id
private Object rid;
public Object getRid() {
return rid;
}
When save:
YourClass proxy = db.save(yourClassInstance);
Object rid = proxy.getRid();
I got it to work using ODocuments instead of POJOs (which works for my project). Code sample:
ODatabaseDocumentTx db = null;
ODocument doc = null;
db = new ODatabaseDocumentTx("local:" + System.getProperty("user.home") + "/testDB");
db.create();
doc = new ODocument("Person");
doc.field("name", "Peter");
doc.save();
String rid = doc.getIdentity().toString();
List<ODocument> results = db.query(new OSQLSynchQuery<ODocument>("select from " + rid));
for (ODocument aDoc : results) {
System.out.println(aDoc.field("name"));
}
db.close();
It's just simple here is the code:
//insertquery will be the sql statement you want to insert
ODocument result=db.command(new OCommandSQL(insertquery)).execute();
System.out.println(result.field("#rid"));
Alternatively you can make use of getRecordByUserObject() of OObjectDatabaseTx,
OObjectDatabaseTx db = new OObjectDatabaseTx("local:" + System.getProperty("user.home") + "/testDB");
ODocument oDocument = db.getRecordByUserObject( person, true );
oDocument.save();
String rid = oDocument.getIdentity().toString();
If you already have access to your proxy object from the save, you can totally do a cool cast on it to get the underlying ODocument object which has a record ID (Identity).
Person proxyPerson = db.save(person);
ODocument oDocument = ((OObjectProxyMethodHandler)((ProxyObject)proxyPerson).getHandler()).getDoc();
person.setId(oDocument.getIdentity().toString());
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.
I have an Oracle stored procedure that returns a BLOB in an output parameter:
PROCEDURE GET_IMAGE_DATA(i_image_type IN NUMBER, o_image_data OUT BLOB) IS
BEGIN
SELECT IMAGE_DATA
INTO o_image_data
FROM IMAGES
WHERE IMAGE_TYPE = i_image_type;
END GET_IMAGE_DATA;
I want to use JDBC Spring to read this data. However, DefaultLobHandler (and I think OracleLobHandler) getBlobAsBytes() requires a resultset.
private static class QueryForBinaryCryptKey extends StoredProcedure {
private static final String SQL = "IMAGE_PKG.GET_IMAGE_DATA";
private DefaultLobHandler lobHandler;
QueryForImageData(DataSource dataSource) {
super(dataSource, SQL);
setFunction(false);
lobHandler = new DefaultLobHandler();
declareParameter(new SqlParameter(KEY_TYPE, OracleTypes.NUMBER));
declareParameter(new SqlOutParameter(KEY_BLOB, OracleTypes.BLOB));
}
public Map getImage(int keyType) {
Map outParams = super.execute(inParams(keyType));
//how can I get the contents of the blob right here since
//getBlobAsBytes requires a resultSet???
return outParams;
}
private Map inParams(int keyType) {
Map params = new HashMap();
params.put(KEY_TYPE, new Integer(keyType));
return params;
}
}
How can I get the blob data when all I have is an out parameter and not a resultset?
The JDBC Spring API (DefaultLobHandler and OracleLobHandler) require a ResultSet object for their BLOB related methods.
You need to convert your GET_IMAGE_DATA procedure into a function:
FUNCTION ATTACHMENT_BLOB_GET(IN_IMAGE_TYPE IN IMAGES.IMAGE_TYPE%TYPE)
RETURN SYS_REFCURSOR AS
results_cursor SYS_REFCURSOR;
BEGIN
OPEN results_cursor FOR
SELECT t.image_data
FROM IMAGES t
WHERE t.image_type = IN_IMAGE_TYPE;
RETURN results_cursor;
END;
OUT parameters are always good for some grief, BLOBs especially.
It is indeed possible to read Blob as Stream/byte[] without a ResultSet. Check this one out.