Ibatis TypeHandler and empty Varchar parameters in types in stored procedure - oracle

In a mybatis, spring application I have a TypeHandler which fills data for an ARRAY of STRUCTS required to call Oracle's stored procedure. A blob entry is properly filled and visible in the stored procedure; String entries are not, no string data is sent. No error or warning printed in logs. Data is not null and valid in application. The data simply disappears between application and oracle.
My handler's setParameter implementation looks like this:
public void setParameter(PreparedStatement ps, int i, List<MailAttachment> parameter,
JdbcType jdbcType) throws SQLException
{
List<MailAttachment> attachmentList = parameter;
OracleConnection oracleConnection = ps.getConnection().unwrap(OracleConnection.class);
StructDescriptor structDescriptor = StructDescriptor.createDescriptor(ATTACHMENT, oracleConnection);
Object[] structs = null;
structs = new Object[attachmentList == null ? 0 :attachmentList.size()];
if (attachmentList != null) {
//CharacterSet chs = CharacterSet.make(CharacterSet.UTF8_CHARSET);
for (int index = 0; index < attachmentList.size(); index++) {
MailAttachment mailAttachment = attachmentList.get(index);
BLOB blob = null;
if (mailAttachment.getData() != null){
blob = BLOB.createTemporary(oracleConnection,false,BLOB.DURATION_SESSION);
// filling blob works
}
CHAR attachName = new CHAR(mailAttachment.getFilename(), CharacterSet.make(CharacterSet.UTF8_CHARSET) );
CHAR contentType = new CHAR(mailAttachment.getContentType(), CharacterSet.make(CharacterSet.UTF8_CHARSET) );
STRUCT struct = new STRUCT(structDescriptor, oracleConnection,
new Object[] {blob, attachName, contentType, null}
);
structs[index] = struct;
}
}
ArrayDescriptor arrayDesc = ArrayDescriptor.createDescriptor(ATTACHMENT_LIST, oracleConnection);
ARRAY oracleArray = new ARRAY(arrayDesc, oracleConnection, structs);
ps.setObject(i, oracleArray);
}

This issue is connected with Oracle JDBC driver and it's support for internationalization.
Remember to include orai18n.jar in classpath/pom file in correct version for your ojdbc jar file.
If orai18n.jar is missing:
setParameters: varchar2 parameters in Oracle type will be set to null
getResult/getNonNullParameter: varchar2 parameters will be loaded to java class as "???" string.

Related

how to get alias name in Spring boot

how to get alias name using spring-data-jpa in Spring boot?
I want alias name for stored procedure
StoredProcedureQuery storedProcedure = entitymanager.createStoredProcedureQuery(Proc);
if (params != null && params.length > 0) { // If no parameters supplied then no need to add it
int paramCount = 0;
for (String param : params) {
paramCount++;
storedProcedure.registerStoredProcedureParameter(paramCount, String.class, ParameterMode.IN);
storedProcedure.setParameter(paramCount, param);
// System.out.println("param" +param);
}
}
List<Object[]> storedProcedureResults = storedProcedure.getResultList();
this will return me only result but i also want column name which i have given in stored procedure

how not to insert more than two sequential nextval value?

I'm using spring framework and oracle DB for the Web solution system.
The problem is when I call the web page related on oracle sequence,
sometimes more than two rows are inserted on the DB table.
That rows has not duplicated values but increased values from sequence.
Also I already checked the java code,
but I didn't use the loop or for sentences or call twice insert sentences.
Is that occurred often?
and how can I solve the problem?
Do I have to add the code for checking value or make the oracle trigger on the table?
This is the code.
public void insertDefaultLParameter(HttpServletRequest request, String workflowId) throws Exception{
String newLParaId = mapper.getNewLParaId();
HashMap<String, String> condition = new HashMap<String, String>();
condition.put("newLParaId", newLParaId);
condition.put("paraValue", "2013-05");
condition.put("workflowId", workflowId);
mapper.insertLParameter(condition);
mapper.insertLParameterMapping(condition);
}
"getNewLParaId()" called the sequence "MT_L_PARA_MAPPING_SEQ" like the below sql.
SELECT 'LPARA' || LPAD(MT_L_PARA_MAPPING_SEQ.nextval,10,0) FROM DUAL
After getting the value, the value is inserted into two tables through "insertLParameter" and "insertLParameterMapping" mapping id.
And the below is the code which call "insertDefaultLParameter" class.
See the bottom of the code.
You can find "rfmService.insertDefaultLParameter(request, praWfId);".
#RequestMapping(value="/insertWorkflowInfo", method=RequestMethod.POST, headers="Accept=application/json")
public ModelAndView insertWorkflowDetail(Locale locae, Model model, HttpServletRequest request) throws Exception{
HttpSession session = request.getSession(false);
User user = (User)session.getAttribute("user");
String userId = user.getUserId();
String workflowNm = "";
String alsPpsCd = "";
String workflowDesc = "";
String pid = "";
String plevel = "";
if(request.getParameter("workflowNm") != null || !request.getParameter("workflowNm").equals("")) workflowNm = request.getParameter("workflowNm");
if(request.getParameter("alsPpsCd") != null || !request.getParameter("alsPpsCd").equals("")) alsPpsCd = request.getParameter("alsPpsCd");
if(request.getParameter("workflowDesc") != null || !request.getParameter("workflowDesc").equals("")) workflowDesc = request.getParameter("workflowDesc");
if(request.getParameter("pid") != null || !request.getParameter("pid").equals("")) pid = request.getParameter("pid");
if(request.getParameter("plevel") != null || !request.getParameter("plevel").equals("")) plevel = request.getParameter("plevel");
//0.
//HashMap<String,String> treeMgtInfo = new HashMap<String,String>();
//treeMgtInfo.put("treeId",pid);
//
HashMap<String,String> input = new HashMap<String,String>();
//2.
String praWfId = wfService.selectPraWfid();
input.put("praWfId", praWfId);
input.put("wfNm",workflowNm);
input.put("wfDesc", SecurityUtil.removeXSS(workflowDesc));
input.put("alsPpsCd",alsPpsCd);
input.put("usrId",userId);
input.put("rgUsrId",userId);
// 1.
wfService.insertWorkflowDetail(input);
input.put("treeNm",workflowNm);
//
int treeLevCd = new Integer(plevel) +1 ;
input.put("treeLevCd",""+treeLevCd);
input.put("treeBjCd",Constant.TREE_BJ_CD_WORKFLOW);
input.put("treeCd",Constant.TREE_CD_WORKFLOW);
//
input.put("treeLrkRufId",praWfId);
input.put("upTreeId",pid);
//
int sceXrsSeqVl = treeService.selectSceXrsSeqId(input);
input.put("sceXrsSeqVl",""+sceXrsSeqVl);
String treeId = treeService.selectTreeIdInfo();
input.put("treeId",treeId);
// 2.
rfmService.insertDefaultLParameter(request, praWfId);
// 3.
treeService.insertTreeMgtInfoWithId(input);
ModelAndView modelAndView=new ModelAndView("defaultViews");
modelAndView.addObject("treeId",treeId);
modelAndView.addObject("praWfId",praWfId);
return modelAndView;
}
This is all of code related on the sequence.

Using JDBC Driver for Informix + SQLException being thrown when NULL Value is encountered

I'm using the JDBC driver for Informix. I connect to my host just fine, but when the query is executed a null value is returned for one of the fields specified in my select. Instead of just retrieving that value, and SQLException gets thrown:
Column (colname) not found in any table in the query (or SLV is undefined).
I'm using the driver this way:
try{
PreparedStatement pstmtDist = conn.prepareStatement(query2);
ResultSet rsDist = pstmtDist.executeQuery();
while(rsDist.next()){
int distCaseId = 0;
String distCaseIdStr = new String();
int distCaseDefNum = 0;
String distCaseDefNumStr = new String();
distCaseIdStr = rsDist.getObject("colname").toString();
distCaseId = Integer.parseInt(distCaseIdStr.trim());
distCaseDefNumStr = rsDist.getObject("colname2").toString();
distCaseDefNum = Integer.parseInt(distCaseDefNumStr.trim());
//System.out.println(String.format("distCaseId == %d distCaseDefNum == %d\n",distCaseId,distCaseDefNum));
}// end while district cases
rsDist.close();
pstmtDist.close();
connDist.close();
}
catch (SQLException e){
System.out.println("EXCEPTION: "+e.getMessage());
}
Any tips are welcomed!
-TU
I think problem is in line:
distCaseDefNumStr = rsDist.getObject("colname2").toString();
and with similar lines where getObject() can return null.
If your colname2 has null value then getObject() returns null and then Java tries to run toString() method on null object.
Yes, the message about column not found in result is strange, but I think that you really observe null pointer exception. Use getString() or getInt() methods of ResultSet.

How to Pass Java List of Objects to Oracle Stored Procedure Using MyBatis?

I have been googling this for a while and cannot seem to find any real answers.
I have an Oracle stored procedure that has a number of in parameters that have a type that is table of the table rowtype. So for example:
Declared in the pacakge:
TYPE param1_type_t IS TABLE OF table1%ROWTYPE;
TYPE param2_type_t IS TABLE OF table2%ROWTYPE;
TYPE param3_type_t IS TABLE OF table3%ROWTYPE;
Oracle Procedure:
PROCEDURE my_proc
(
parameter1 IN param1_type_t,
parameter2 IN param2_type_t,
parameter3 IN param3_type_t
)
On the java side, I have 3 corresponding Lists of objects representing each of the parameters that are populated in Java. Is it possible to call the Oracle procedure using MyBatis in this scenario?
<update id="callOracleSP" statementType="CALLABLE">
{CALL my_proc( #{param1, mode=IN},
#{param2, mode=IN},
#{param3, mode=IN}
)
}
</update>
The objects themselves are simple VOs with String and Integer properties and their respective getters and setters.
I am not really sure how to proceed. Do I need to somehow map the Java object lists to the Oracle types?
I can't tell if you do already or not, but you'll need Oracle objects defined.
CREATE OR REPLACE TYPE SCHEMA."YOUR_OBJECT" AS OBJECT
(
field_one varchar2(50),
field_two varchar2(100)
);
/
CREATE OR REPLACE TYPE SCHEMA."YOUR_OBJECT_ARRAY" AS TABLE OF YOUR_OBJECT;
/
Then you can write type handlers to map the Java objects to the Oracle objects.
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;
....
public class YourTypeHandler implements TypeHandler
{
....
public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException
{
List<YourObject> objects = (List<YourObject>) parameter;
StructDescriptor structDescriptor = StructDescriptor.createDescriptor("YOUR_OBJECT", ps.getConnection());
STRUCT[] structs = new STRUCT[objects.size()];
for (int index = 0; index < objects.size(); index++)
{
YourObject pack = packs.get(index);
Object[] params = new Object[2];
params[0] = pack.getFieldOne();
params[1] = pack.getFieldTwo();
STRUCT struct = new STRUCT(structDescriptor, ps.getConnection(), params);
structs[index] = struct;
}
ArrayDescriptor desc = ArrayDescriptor.createDescriptor("YOUR_OBJECT_ARRAY", ps.getConnection());
ARRAY oracleArray = new ARRAY(desc, ps.getConnection(), structs);
ps.setArray(i, oracleArray);
}
}
Then invoke the procedure,
call your_proc
(
#{yourObjects, javaType=Object, jdbcType=ARRAY, jdbcTypeName=YOUR_OBJECT_ARRAY, mode=IN, typeHandler=YourObjectArrayTypeHandler}
)
Andy Pryor's answer is very good I tested it and it really works. But it has an error at typeHandler:
call your_proc
(
#{yourObjects, javaType=Object, jdbcType=ARRAY, jdbcTypeName=YOUR_OBJECT_ARRAY, mode=IN, typeHandler=YourObjectArrayTypeHandler}
)
should be:
call your_proc
(
#{yourObjects, javaType=Object, jdbcType=ARRAY, jdbcTypeName=YOUR_OBJECT_ARRAY, mode=IN, typeHandler=YourTypeHandler}
)
The TypeHandler has an error as well: (there is no "packs" and there is some difference in the method parameters in my version)
#Override
public void setParameter(PreparedStatement ps, int i, Object parameter, String arg3) throws SQLException {
List<YourObject> objects = (List<YourObject>) parameter;
StructDescriptor structDescriptor = StructDescriptor.createDescriptor("YOUR_OBJECT", ps.getConnection());
STRUCT[] structs = new STRUCT[objects.size()];
for (int index = 0; index < objects.size(); index++)
{
YourObject pack = objects.get(index);
Object[] params = new Object[2];
params[0] = pack.getFieldOne();
params[1] = pack.getFieldTwo();
STRUCT struct = new STRUCT(structDescriptor, ps.getConnection(), params);
structs[index] = struct;
}
ArrayDescriptor desc = ArrayDescriptor.createDescriptor("YOUR_OBJECT_ARRAY", ps.getConnection());
ARRAY oracleArray = new ARRAY(desc, ps.getConnection(), structs);
ps.setArray(i, oracleArray);
}
And here is an example for xml mapping:
<parameterMap id="updateHierPersonAssignMap" class="java.util.Map" >
<parameter property="p_array" jdbcType="ARRAY" javaType="Object" mode="IN" typeHandler="com.aamtech.ria.model.domain.typehandler.YourTypeHandler"/>
</parameterMap>
<procedure id="updateHierPersonAssign" parameterMap="updateHierPersonAssignMap" >
<![CDATA[
{ call ria_am_util_pkg.j_update_hier_person_assign( ? ) }
]]>
</procedure>
And here is how you can call it from the DAO:
public void update(List array) {
Map<String, Object> queryParams = new HashMap<String, Object>();
queryParams.put("p_array", array);
try {
client.update("HashMapResult.updateHierPersonAssign", queryParams);
} catch (SQLException e) {
}
}
And my procedure looks like this (it just inserts a row into a test table):
Procedure j_update_hier_person_assign (p_array IN YOUR_OBJECT_ARRAY) is
begin
FOR i IN 1..p_array.count LOOP
--dbms_output.put_line();
insert into test (a) values (p_array(i).field_one);
END LOOP;
end;

ResultSet.getBlob() Exception

The Code:
ResultSet rs = null;
try {
conn = getConnection();
stmt = conn.prepareStatement(sql);
rs = stmt.executeQuery();
while (rs.next()) {
Blob blob = rs.getBlob("text");
byte[] blobbytes = blob.getBytes(1, (int) blob.length());
String text = new String(blobbytes);
The result:
java.sql.SQLException: Invalid column type: getBLOB not implemented for class oracle.jdbc.driver.T4CClobAccessor
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:111)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:145)
at oracle.jdbc.driver.Accessor.unimpl(Accessor.java:357)
at oracle.jdbc.driver.Accessor.getBLOB(Accessor.java:1299)
at oracle.jdbc.driver.OracleResultSetImpl.getBLOB(OracleResultSetImpl.java:1280)
at oracle.jdbc.driver.OracleResultSetImpl.getBlob(OracleResultSetImpl.java:1466)
at oracle.jdbc.driver.OracleResultSet.getBlob(OracleResultSet.java:1978)
I have class12_10g.zip in my class path. I've googled and have found essentially only one site on this particular problem, and it wasn't helpful at.
Does anyone have any ideas on this?
A little background:
We were converting one of our databases from MySQL to Oracle. Within the MySQL DB, one of the fields is a longtext which is treated as a BLOB in the code. The SQL developer workbench by default converts longtext to CLOB (make sense to me) but the code was expecting Blob. I guess the error wasn't that nice: oracle.jdbc.driver.T4CClobAccessor (though it does mention Clob).
When I tried the following:
rs = stmt.executeQuery();
while (rs.next()) {
byte[] blobbytes = rs.getBytes("text");
String text = new String(blobbytes);
}
it threw an unsupported exception - all I had to do in the first place was compare the types in the newly created Oracle DB with what the code was expecting (unfortunately I just assumed they would match).
Sorry guys! Not that I've put much thought into it, now I have to figure out why the original developers used BLOB types for longtext
Not sure about making the Blob object work -- I typically skip the Blob step:
rs = stmt.executeQuery();
while (rs.next()) {
byte[] blobbytes = rs.getBytes("text");
String text = new String(blobbytes);
}
try to use the latest version of the drivers (10.2.0.4). Try also the drivers for JDK 1.4/1.5 since classes12 are for JDK 1.2/1.3.
Try...
PreparedStatement stmt = connection.prepareStatement(query);
ResultSet rs = stmt.executeQuery();
rs.next();
InputStream is = rs.getBlob(columnIndex).getBinaryStream();
...instead?
I have a utility method in a DAO superclass of all my DAOs:
protected byte[] readBlob(oracle.sql.BLOB blob) throws SQLException {
if (blob != null) {
byte[] buffer = new byte[(int) blob.length()];
int bufsz = blob.getBufferSize();
InputStream is = blob.getBinaryStream();
int len = -1, off = 0;
try {
while ((len = is.read(buffer, off, bufsz)) != -1) {
off += len;
}
} catch (IOException ioe) {
logger.debug("IOException when reading blob", ioe);
}
return buffer;
} else {
return null;
}
}
// to get the oracle BLOB object from the result set:
oracle.sql.BLOB blob= (oracle.sql.BLOB) ((OracleResultSet) rs).getBlob("blobd");
Someone will now say "why didn't you just do XYZ", but there was some issue at the time that made the above more reliable.
When JDBC returns a ResultSet from an Oracle database it always returns an OracleResultSet. If you are typing it as a ResultSet, java upcasts it to the standard SQL ResultSet.
OracleResultSet overrides most of the data type methods, because Oracle datatypes are not standard SQL types.
In other words, that worked because you cast the rs as an OracleResultSet, and used it's getBlob method.

Resources