I'm trying to call oracle db function by java as following :
StoredProcedureCall getTaxProcedureCall = new StoredProcedureCall();
getTaxProcedureCall.setProcedureName("getTaxByCustomerId");
getTaxProcedureCall.addNamedArgument("ImpactedParty");
getTaxProcedureCall.addNamedArgument("ImpactedPartyType");
getTaxProcedureCall.addNamedOutputArgument("P_TAX");
getTaxProcedureCall.addNamedOutputArgument("A_TAX");
DataReadQuery query = new DataReadQuery();
query.setCall(getTaxProcedureCall);
query.addArgument("ImpactedParty");
query.addArgument("ImpactedPartyType");
Vector queryValues = new Vector();
queryValues.add(cusId);
queryValues.add("CSID");
Vector<DatabaseRecord> records = (Vector<DatabaseRecord>)TransactionContext.getCurrent().getUnitOfWork().executeQuery(query, queryValues);
return records;
but when run it, it gave : oracle.jdbc.driver.OraclePreparedStatementWrapper cannot be cast
to java.sql.CallableStatement
at Vector<DatabaseRecord> records = (Vector<DatabaseRecord>)TransactionContext.getCurrent().getUnitOfWork().executeQuery(query, queryValues);
I have solved the issue by replacing StoredProcedureCall with SQLCall
and pass sql string like:
SELECT getTaxByCustomerId(#ImpactedParty, #ImpactedPartyType, #TaxProfile) AS P_TAX FROM DUAL
now it's work fine.
Related
i am getiing proper values from DB but Getting Duplicate List Values while add list object to class object, in Spring Boot
Please suggest to me how to do it.
Get data from DB Code : Here Rooms is my DB Entity class
CriteriaBuilder roomsBuilder = roomSession.getCriteriaBuilder();
CriteriaQuery<Rooms> query = roomsBuilder.createQuery(Rooms.class);
Root<Rooms> root = query.from(Rooms.class);
Predicate userRestriction = roomsBuilder.or(roomsBuilder.notEqual(root.get(SmatrEntityParameters.IS_DELETED), "Y"),
roomsBuilder.isNull(root.get(SmatrEntityParameters.IS_DELETED)));
Predicate userRestriction2 = roomsBuilder.and(roomsBuilder.equal(root.join("properties").get(SmatrEntityParameters.PROPERTY_ID), propertyId));
query.where(roomsBuilder.and(userRestriction, userRestriction2));
Query q = roomSession.createQuery(query);
List<Rooms> getroomslistobj= q.getResultList();
Iterate the list code: Here getAllRoomsobj means main response pojo class
List<GetAllRooms> getallroomslistobj = new ArrayList<GetAllRooms>();
for (int i = 0; i < getroomslistobj.size(); i++) {
int dbroomId = getroomslistobj.get(i).getRoomId();
String dbroomName = getroomslistobj.get(i).getRoomName();
// Actual code start
getAllRoomsobj.setRoomId(dbroomId);
getAllRoomsobj.setRoomName(dbroomName);
getallroomslistobj.add(getAllRoomsobj);
// Actual code end
}
I tried one code at the middle of the Actual code but I did not want create a new object for the response class:
GetAllRooms object = new GetAllRooms();
object.setRoomId(dbroomId);
object.setRoomName(dbroomName);
getallroomslistobj.add(object);
Please Help me Out,
Thanks in Advance
u can try it by stream.map() of java8
i am doing a task converting VB script written from Powerbuild to java,
i am struggled at converting the DataStore Object into java ,
i have something like this :
lds_appeal_application = Create DataStore
lds_appeal_application.DataObject = "ds_appeal_application_report"
lds_appeal_application.SetTransObject(SQLCA)
ll_row = lds_appeal_application.retrieve(as_ksdyh, adt_start_date, adt_end_date, as_exam_name, as_subject_code)
for ll_rc = 1 to ll_row
ldt_update_date = lds_appeal_application.GetItemDatetime(ll_rc, "sqsj")
ls_caseno = trim(lds_appeal_application.GetItemString(ll_rc, "caseno"))
ls_candidate_no = trim(lds_appeal_application.GetItemString(ll_rc, "zkzh"))
ls_subjectcode = trim(lds_appeal_application.GetItemString(ll_rc, "kmcode"))
ls_papercode = trim(lds_appeal_application.GetItemString(ll_rc, "papercode"))
ls_name = trim(lds_appeal_application.GetItemString(ll_rc, "mc"))
ll_ksh = lds_appeal_application.GetItemDecimal(ll_rc, "ks_h")
ll_kmh = lds_appeal_application.GetItemDecimal(ll_rc, "km_h")
simply speaking, a datasoure is created and a data table is point to it by sql query(ds_appeal_application_report). Finally using a for loop to retrieve information from the table.
in java way of doing, i use an entities manager to createnativequery and the query can result a list of object array. However, i just dont know how to retrieve the information like VB using the DataStore Object.
please give me some advice . Thanks
I am trying to pass in an ARRAY of BLOBs, but I am getting errors.
uploadFiles = new SimpleJdbcCall(dataSource).withCatalogName("FILES_PKG")
.withFunctionName("insertFiles").withReturnValue()
.declareParameters(new SqlParameter("p_userId", Types.NUMERIC),
new SqlParameter("p_data", Types.ARRAY, "BLOB_ARRAY"),
new SqlOutParameter("v_groupId", Types.NUMERIC));
uploadFiles.compile();
List<Blob> fileBlobs = new ArrayList<>();
for(int x = 0; x < byteFiles.size(); x++){
fileBlobs.add(new javax.sql.rowset.serial.SerialBlob(byteFiles.get(x)));
}
final Blob[] data = fileBlobs.toArray(new Blob[fileBlobs.size()]);
SqlParameterSource in = new MapSqlParameterSource()
.addValue("p_files", new SqlArrayValue<Blob>(data, "BLOB_ARRAY"))
.addValue("p_userId", userId);
Map<String, Object> results = uploadFiles.execute(in);
I created a SQL Type in the DB
create or replace TYPE BLOB_ARRAY is table of BLOB;
Function Spec
FUNCTION insertFiles(p_userId IN NUMBER,
p_files IN BLOB_ARRAY)
RETURN NUMBER;
Function Body
FUNCTION insertFiles (p_userId IN NUMBER,
p_files IN BLOB_ARRAY)
RETURN NUMBER
AS
v_groupId NUMBER := FILE_GROUP_ID_SEQ.NEXTVAL;
v_fileId NUMBER;
BEGIN
FOR i IN 1..p_files.COUNT
LOOP
v_fileId := FILE_ID_SEQ.NEXTVAL;
BEGIN
INSERT INTO FILES
(FILE_ID,
FILE_GROUP_ID,
FILE_DATA,
UPDT_USER_ID)
SELECT
v_fileId,
v_groupId,
p_files(i),
USER_ID
FROM USERS
WHERE USER_ID = p_userId;
EXCEPTION WHEN OTHERS THEN
v_groupId := -1;
END;
END LOOP;
RETURN v_groupId;
END insertFiles;
I am not sure how to correctly pass the array of Blobs to the SQL Function.
Error :
java.sql.SQLException: Fail to convert to internal representation:
javax.sql.rowset.serial.SerialBlob#87829c90 at
oracle.jdbc.oracore.OracleTypeBLOB.toDatum(OracleTypeBLOB.java:69)
~[ojdbc7.jar:12.1.0.1.0] at
oracle.jdbc.oracore.OracleType.toDatumArray(OracleType.java:176)
~[ojdbc7.jar:12.1.0.1.0] at
oracle.sql.ArrayDescriptor.toOracleArray(ArrayDescriptor.java:1321)
~[ojdbc7.jar:12.1.0.1.0] at oracle.sql.ARRAY.(ARRAY.java:140)
~[ojdbc7.jar:12.1.0.1.0] at
UPDATE
After trying Luke's suggestion, I am getting the following error:
uncategorized SQLException for SQL [{? = call FILES_PKG.INSERTFILES(?,
?)}]; SQL state [99999]; error code [22922]; ORA-22922: nonexistent
LOB value ; nested exception is java.sql.SQLException: ORA-22922:
nonexistent LOB value ] with root cause
java.sql.SQLException: ORA-22922: nonexistent LOB value
The error message appears to indicate the Oracle JDBC driver doesn't know what to do with the javax.sql.rowset.serial.SerialBlob object you've passed to it.
Try creating the Blob objects using Connection.createBlob instead. In other words, try replacing the following
loop
for(int x = 0; x < byteFiles.size(); x++){
fileBlobs.add(new javax.sql.rowset.serial.SerialBlob(byteFiles.get(x)));
}
with
Connection conn = dataSource.getConnection();
for(int x = 0; x < byteFiles.size(); x++){
Blob blob = conn.createBlob();
blob.setBytes(1, byteFiles.get(x));
fileBlobs.add(blob);
}
Also, make sure that your parameter names are consistent between your SimpleJdbcCall and your stored function. Your SimpleJdbcCall declares the BLOB array parameter with name p_data but your stored function declaration uses p_files. If the parameter names are not consistent you are likely to get an Invalid column type error.
However, had I run the above test with a stored function of my own that actually did something with the BLOB values passed in, instead of just hard-coding a return value, I might have found that this approach didn't work. I'm not sure why, I'd probably have to spend some time digging around in the guts of Spring to find out.
I tried replacing the Blob values with Spring SqlLobValues, but that didn't work either. I guess Spring's SqlArrayValue<T> type doesn't handle Spring wrapper objects for various JDBC types.
So I gave up on a Spring approach and went back to plain JDBC:
import oracle.jdbc.OracleConnection;
// ...
OracleConnection conn = dataSource.getConnection().unwrap(OracleConnection.class);
List<Blob> fileBlobs = new ArrayList<>();
for(int x = 0; x < byteFiles.size(); x++){
Blob blob = conn.createBlob();
blob.setBytes(1, byteFiles.get(x));
fileBlobs.add(blob);
}
Array array = conn.createOracleArray("BLOB_ARRAY",
fileBlobs.toArray(new Blob[fileBlobs.size()]));
CallableStatement cstmt = conn.prepareCall("{? = call insertFiles(?, ?)}");
cstmt.registerOutParameter(1, Types.NUMERIC);
cstmt.setInt(2, userId);
cstmt.setArray(3, array);
cstmt.execute();
int result = cstmt.getInt(1);
I've tested this with the stored function you've now included in your question, and it is able to call this function and insert the BLOBs into the database.
I'll leave it up to you to do what you see fit with the variable result and to add any necessary cleanup or transaction control.
However, while this approach worked, it didn't feel right. It didn't fit the Spring way of doing things. It did at least prove that what you were asking for was possible, in that there wasn't some limitation in the JDBC driver that meant you couldn't use BLOB arrays. I felt that there ought to be some way to call your function using Spring JDBC.
I spent some time looking into the ORA-22922 error and concluded that the underlying problem was that the Blob objects were being created using a different Connection to what was being used to execute the statement. The question then becomes how to get hold of the Connection Spring uses.
After some further digging around in the source code to various Spring classes, I realised that a more Spring-like way of doing this is to replace the SqlArrayValue<T> class with a different one specialised for BLOB arrays. This is what I ended up with:
import java.sql.Array;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import oracle.jdbc.OracleConnection;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.support.AbstractSqlTypeValue;
public class SqlBlobArrayValue extends AbstractSqlTypeValue {
private List<byte[]> values;
private String defaultTypeName;
public SqlBlobArrayValue(List<byte[]> values) {
this.values = values;
}
public SqlBlobArrayValue(List<byte[]> values, String defaultTypeName) {
this.values = values;
this.defaultTypeName = defaultTypeName;
}
protected Object createTypeValue(Connection conn, int sqlType, String typeName)
throws SQLException {
if (typeName == null && defaultTypeName == null) {
throw new InvalidDataAccessApiUsageException(
"The typeName is null in this context. Consider setting the defaultTypeName.");
}
Blob[] blobs = new Blob[values.size()];
for (int i = 0; i < blobs.length; ++i) {
Blob blob = conn.createBlob();
blob.setBytes(1, values.get(i));
blobs[i] = blob;
}
Array array = conn.unwrap(OracleConnection.class).createOracleArray(typeName != null ? typeName : defaultTypeName, blobs);
return array;
}
}
This class is heavily based on SqlArrayValue<T>, which is licensed under Version 2 of the Apache License. For brevity, I've omitted comments and a package directive.
With the help of this class, it becomes a lot easier to call your function using Spring JDBC. In fact, you can replace everything after the call to uploadFiles.compile() with the following:
SqlParameterSource in = new MapSqlParameterSource()
.addValue("p_files", new SqlBlobArrayValue(byteFiles, "BLOB_ARRAY"))
.addValue("p_userId", userId);
Map<String, Object> results = uploadFiles.execute(in);
I have a problem executing stored function on MongoDB from my java code , i have a stored function like this.
db.system.js.save({
_id:"myFunction",
value:function(data){
//todo
return 1 + 1;
}
});
And in my java code :
mongoClient = new MongoClient(SERVER, PORT); // should use this always
db = mongoClient.getDB(DB);
DBObject datos = new BasicDBObject();
datos.put("val", "myvalue");
Then i am trying to execute the function:
try 1
db.doEval("myFunction", datos);
try 2
db.eval("myFunction", datos);
try 3
DBObject query = new BasicDBObject();
query.put("myFunction", datos);
db.command(query);
But the stored function does not execute, any ideas ?
I solved, it seems to pass values/objects to the stored function you have to serialize the object as JSON, so I did this :
String values = JSON.serialize(datos);
Then my java code looks like :
CommandResult er = db.doEval("myFunction("+values+")");
And the stored function gets executed correctly.
So this is the case: I have a program that takes two large csv-files, finds the diffs and then sends a array list to a method that is supposed to update the mongodb with the lines from the array. The problem is the updates are taking forever. A test case with 5000 updates takes 36 minutes. Is this normal?
the update(List<String> changes)-method something like this:
mongoClient = new MongoClient(ip);
db = mongoClient.getDB("foo");
collection = db.getCollection("bar");
//for each line of change
for (String s : changes) {
//splits the csv-lines on ;
String[] fields = s.split(";");
//identifies wich document in the database to be updated
long id = Long.parseLong(fields[0]);
BasicDBObject sq = new BasicDBObject().append("organizationNumber",id);
//creates a new unit-object, that is converted to JSON and then inserted into the database.
Unit u = new Unit(fields);
Gson gson = new Gson();
String jsonObj = gson.toJson(u);
DBObject objectToUpdate = collection.findOne(sq);
DBObject newObject = (DBObject) JSON.parse(jsonObj);
if(objectToUpdate != null){
objectToUpdate.putAll(newObject);
collection.save(objectToUpdate);
}
That's because you are taking extra steps to update.
You don't need to parse JSONs manually and you don't have to do the query-then-update when you can just do an update with a "where" clause in a single step.
Something like this:
BasicDBObject query= new BasicDBObject().append("organizationNumber",id);
Unit unit = new Unit(fields);
BasicDBObject unitDB= new BasicDBObject().append("someField",unit.getSomeField()).append("otherField",unit.getOtherField());
collection.update(query,unitDB);
Where query specifies the "where" clause and unitDB specifies the fields that need to be updated.