Insert CLOB into Oracle database - java

My question is: How do you get around the ORA-01704: string literal too long error when inserting (or doing anything in queries) with CLOBs?
I want to have a query like this:
INSERT ALL
INTO mytable VALUES ('clob1')
INTO mytable VALUES ('clob2') --some of these clobs are more than 4000 characters...
INTO mytable VALUES ('clob3')
SELECT * FROM dual;
When I try it with actual values though I get ORA-01704: string literal too long back. This is pretty obvious, but how do I insert clobs (or execute any statement at all with a clob)?
I've tried looking at this question, but I don't think it has what I'm looking for. The clobs I have are in a List<String> and I iterate through them to make the statement. My code as it is follows:
private void insertQueries(String tempTableName) throws FileNotFoundException, DataException, SQLException, IOException {
String preQuery = " into " + tempTableName + " values ('";
String postQuery = "')" + StringHelper.newline;
StringBuilder inserts = new StringBuilder("insert all" + StringHelper.newline);
List<String> readQueries = getDomoQueries();
for (String query : readQueries) {
inserts.append(preQuery).append(query).append(postQuery);
}
inserts.append("select * from dual;");
DatabaseController.getInstance().executeQuery(databaseConnectionURL, inserts.toString());
}
public ResultSet executeQuery(String connection, String query) throws DataException, SQLException {
Connection conn = ConnectionPool.getInstance().get(connection);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);
conn.commit();
ConnectionPool.getInstance().release(conn);
return rs;
}

You are making it way to complicated.
Use a PreparedStatement and addBatch() for each clob in your list:
String sql = "insert into " + tempTableName + " values (?)";
PreparedStatement stmt = connection.prepareStatement(sql);
for (String query : readQueries) {
stmt.setCharacterStream(1, new StringReader(query), query.lenght());
stmt.addBatch();
}
stmt.exececuteBatch();
No messing around with escaping strings, no problem with the length of the literals, no need to create temporary clobs. And most probably just as fast as using a single INSERT ALL statement.
If you are using a current driver (> 10.2) then I think the setCharacterStream() call and the creation of the Reader is not necessary either. A simple setString(1, query) will most probably work as well.

You'll need to use bind variables rather than building a SQL statement using string concatenation. This will be beneficial from a security, performance, and robustness standpoint as well since it will reduce the risk of SQL injection attacks, decrease the amount of time Oracle has to spend doing hard parses of the SQL statement, and will eliminate the potential that there is a special character in the string that causes an invalid SQL statement to get generated (i.e. a single quote).
I would expect that you want something like
private void insertQueries(String tempTableName) throws FileNotFoundException, DataException, SQLException, IOException {
String preQuery = " into " + tempTableName + " values (?)" + StringHelper.newline;
StringBuilder inserts = new StringBuilder("insert all" + StringHelper.newline);
List<String> readQueries = getDomoQueries();
for (String query : readQueries) {
inserts.append(preQuery);
}
inserts.append("select * from dual");
Connection conn = ConnectionPool.getInstance().get(connection);
PreparedStatement pstmt = conn.prepareStatement(
inserts);
int i = 1;
for (String query : readQueries) {
Clob clob = CLOB.createTemporary(conn, false, oracle.sql.CLOB.DURATION_SESSION);
clob.setString(i, query);
pstmt.setClob(i, clob);
i = i + 1;
}
pstmt.executeUpdate();
}

BLOB (Binary Large Objects ) and CLOB(Character large objects) are special datatypes and can hold the large chunks of data in form of objects or text. Blob and Clob objects persist the data of the objects into the database as a stream.
An example piece of code:
public class TestDB {
public static void main(String[] args) {
try {
/** Loading the driver */
Class.forName("com.oracle.jdbc.Driver");
/** Getting Connection */
Connection con = DriverManager.getConnection("Driver URL","test","test");
PreparedStatement pstmt = con.prepareStatement("insert into Emp(id,name,description)values(?,?,?)");
pstmt.setInt(1,5);
pstmt.setString(2,"Das");
// Create a big CLOB value...AND inserting as a CLOB
StringBuffer sb = new StringBuffer(400000);
sb.append("This is the Example of CLOB ..");
String clobValue = sb.toString();
pstmt.setString(3, clobValue);
int i = pstmt.executeUpdate();
System.out.println("Done Inserted");
pstmt.close();
con.close();
// Retrive CLOB values
Connection con = DriverManager.getConnection("Driver URL","test","test");
PreparedStatement pstmt = con.prepareStatement("select * from Emp where id=5");
ResultSet rs = pstmt.executeQuery();
Reader instream = null;
int chunkSize;
if (rs.next()) {
String name = rs.getString("name");
java.sql.Clob clob = result.getClob("description")
StringBuffer sb1 = new StringBuffer();
chunkSize = ((oracle.sql.CLOB)clob).getChunkSize();
instream = clob.getCharacterStream();
BufferedReader in = new BufferedReader(instream);
String line = null;
while ((line = in.readLine()) != null) {
sb1.append(line);
}
if (in != null) {
in.close();
}
// this is the clob data converted into string
String clobdata = sb1.toString();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

From Oracle document
You must bear in mind the following automatic switching of the input mode for large data.
There are three input modes as follows: Direct binding, Stream binding, and LOB binding.
For PL/SQL statements
The setBytes and setBinary stream methods use direct binding for data less than 32767 bytes.
The setBytes and setBinaryStream methods use LOB binding for data larger than 32766 bytes.
The setString, setCharacterStream, and setAsciiStream methods use direct binding for data smaller than 32767 bytes in the database character set.
The setString, setCharacterStream, and setAsciiStream methods use LOB binding for data larger than 32766 bytes in the database character set.
The setBytesForBlob and setStringForClob methods, present in the oracle.jdbc.OraclePreparedStatement interface, use LOB binding for any data size.
Follow is a example for put a file content into a input CLOB parameter of a PLSQL procedure:
public int fileToClob( FileItem uploadFileItem ) throws SQLException, IOException
{
//for using stmt.setStringForClob method, turn the file to a big String
FileItem item = uploadFileItem;
InputStream inputStream = item.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader( inputStream );
BufferedReader bufferedReader = new BufferedReader( inputStreamReader );
StringBuffer stringBuffer = new StringBuffer();
String line = null;
while((line = bufferedReader.readLine()) != null) { //Read till end
stringBuffer.append(line);
stringBuffer.append("\n");
}
String fileString = stringBuffer.toString();
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
item.delete();
OracleCallableStatement stmt;
String strFunction = "{ call p_file_to_clob( p_in_clob => ? )}";
stmt= (OracleCallableStatement)conn.prepareCall(strFunction);
try{
SasUtility servletUtility = sas.SasUtility.getInstance();
stmt.setStringForClob(1, fileString );
stmt.execute();
} finally {
stmt.close();
}
}

Me, I like to use the classes from java.sql.* package, not oracle.* stuff. For me the simple approach
Connection con = ...;
try (PreparedStatement pst = con.prepareStatement(
"insert into tbl (other_fld, clob_fld) values (?,?)", new String[]{"tbl_id"});
) {
Clob clob = con.createClob();
readIntoClob(clob, inputStream);
pst.setString(1, "other");
pst.setClob(2, clob);
pst.executeUpdate();
try (ResultSet rst = pst.getGeneratedKeys()) {
if (rst == null || !rst.next()) {
throw new Exception("error with getting auto-generated key");
}
id = rst.getBigDecimal(1);
}
stopped working when testing (current tomcat, jdbc) moved into production (stuck in Tomcat6 for stupid reasons). con.createClob() returns null for reasons unknown in that version, so I had to do this double-take (It took me ages to figure out so I'm sharing here...)
try (PreparedStatement pst = con.prepareStatement(
"insert into tbl (other_fld) values (?)", new String[]{"tbl_id"});
PreparedStatement getClob= con.prepareStatement(
"select clob_fld from tbl where tbl_id = ? for update");
) {
Clob clob = con.createClob();
readIntoClob(clob, inputStream);
pst.setString(1, "other");
pst.executeUpdate();
try (ResultSet rst = pst.getGeneratedKeys()) {
if (rst == null || !rst.next()) {
throw new Exception("error with getting auto-generated key");
}
id = rst.getBigDecimal(1);
}
// fetch back fresh record, with the Clob
getClob.setBigDecimal(1, id);
getClob.execute();
try (ResultSet rst = getClob.getResultSet()) {
if (rst == null || !rst.next()) {
throw new Exception("error with fetching back clob");
}
Clob c = rst.getClob(1);
// Fill in data
readIntoClob(c, stream);
// that's all
}
} catch (SQLException) {
...
}
for completeness here's
// Read data from an input stream and insert it in to the clob column
private static void readIntoClob(Clob clob, InputStream stream) {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream))) {
char[] buffer = new char[CHUNK_BUFFER_SIZE];
int charsRead;
try (Writer wr = clob.setCharacterStream(1L)) {
// Loop for reading of chunk of data and then write into the clob.
while ((charsRead = bufferedReader.read(buffer)) != -1) {
wr.write(buffer, 0, charsRead);
}
} catch (SQLException | IOException ex) {
...
}
}
}
which is from elsewhere on SO, thanks.

Check out some CLOB related samples on github.

Related

java.sql.SQLException: Column index out range, (int) < 1 [duplicate]

I want to INSERT a record in a database (which is Microsoft SQL Server in my case) using JDBC in Java. At the same time, I want to obtain the insert ID. How can I achieve this using JDBC API?
If it is an auto generated key, then you can use Statement#getGeneratedKeys() for this. You need to call it on the same Statement as the one being used for the INSERT. You first need to create the statement using Statement.RETURN_GENERATED_KEYS to notify the JDBC driver to return the keys.
Here's a basic example:
public void create(User user) throws SQLException {
try (
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(SQL_INSERT,
Statement.RETURN_GENERATED_KEYS);
) {
statement.setString(1, user.getName());
statement.setString(2, user.getPassword());
statement.setString(3, user.getEmail());
// ...
int affectedRows = statement.executeUpdate();
if (affectedRows == 0) {
throw new SQLException("Creating user failed, no rows affected.");
}
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
if (generatedKeys.next()) {
user.setId(generatedKeys.getLong(1));
}
else {
throw new SQLException("Creating user failed, no ID obtained.");
}
}
}
}
Note that you're dependent on the JDBC driver as to whether it works. Currently, most of the last versions will work, but if I am correct, Oracle JDBC driver is still somewhat troublesome with this. MySQL and DB2 already supported it for ages. PostgreSQL started to support it not long ago. I can't comment about MSSQL as I've never used it.
For Oracle, you can invoke a CallableStatement with a RETURNING clause or a SELECT CURRVAL(sequencename) (or whatever DB-specific syntax to do so) directly after the INSERT in the same transaction to obtain the last generated key. See also this answer.
Create Generated Column
String generatedColumns[] = { "ID" };
Pass this geneated Column to your statement
PreparedStatement stmtInsert = conn.prepareStatement(insertSQL, generatedColumns);
Use ResultSet object to fetch the GeneratedKeys on Statement
ResultSet rs = stmtInsert.getGeneratedKeys();
if (rs.next()) {
long id = rs.getLong(1);
System.out.println("Inserted ID -" + id); // display inserted record
}
When encountering an 'Unsupported feature' error while using Statement.RETURN_GENERATED_KEYS, try this:
String[] returnId = { "BATCHID" };
String sql = "INSERT INTO BATCH (BATCHNAME) VALUES ('aaaaaaa')";
PreparedStatement statement = connection.prepareStatement(sql, returnId);
int affectedRows = statement.executeUpdate();
if (affectedRows == 0) {
throw new SQLException("Creating user failed, no rows affected.");
}
try (ResultSet rs = statement.getGeneratedKeys()) {
if (rs.next()) {
System.out.println(rs.getInt(1));
}
rs.close();
}
Where BATCHID is the auto generated id.
I'm hitting Microsoft SQL Server 2008 R2 from a single-threaded JDBC-based application and pulling back the last ID without using the RETURN_GENERATED_KEYS property or any PreparedStatement. Looks something like this:
private int insertQueryReturnInt(String SQLQy) {
ResultSet generatedKeys = null;
int generatedKey = -1;
try {
Statement statement = conn.createStatement();
statement.execute(SQLQy);
} catch (Exception e) {
errorDescription = "Failed to insert SQL query: " + SQLQy + "( " + e.toString() + ")";
return -1;
}
try {
generatedKey = Integer.parseInt(readOneValue("SELECT ##IDENTITY"));
} catch (Exception e) {
errorDescription = "Failed to get ID of just-inserted SQL query: " + SQLQy + "( " + e.toString() + ")";
return -1;
}
return generatedKey;
}
This blog post nicely isolates three main SQL Server "last ID" options:
http://msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the-sql-server/ - haven't needed the other two yet.
Instead of a comment, I just want to answer post.
Interface java.sql.PreparedStatement
columnIndexes « You can use prepareStatement function that accepts columnIndexes and SQL statement.
Where columnIndexes allowed constant flags are Statement.RETURN_GENERATED_KEYS1 or Statement.NO_GENERATED_KEYS[2], SQL statement that may contain one or more '?' IN parameter placeholders.
SYNTAX «
Connection.prepareStatement(String sql, int autoGeneratedKeys)
Connection.prepareStatement(String sql, int[] columnIndexes)
Example:
PreparedStatement pstmt =
conn.prepareStatement( insertSQL, Statement.RETURN_GENERATED_KEYS );
columnNames « List out the columnNames like 'id', 'uniqueID', .... in the target table that contain the auto-generated keys that should be returned. The driver will ignore them if the SQL statement is not an INSERT statement.
SYNTAX «
Connection.prepareStatement(String sql, String[] columnNames)
Example:
String columnNames[] = new String[] { "id" };
PreparedStatement pstmt = conn.prepareStatement( insertSQL, columnNames );
Full Example:
public static void insertAutoIncrement_SQL(String UserName, String Language, String Message) {
String DB_URL = "jdbc:mysql://localhost:3306/test", DB_User = "root", DB_Password = "";
String insertSQL = "INSERT INTO `unicodeinfo`( `UserName`, `Language`, `Message`) VALUES (?,?,?)";
//"INSERT INTO `unicodeinfo`(`id`, `UserName`, `Language`, `Message`) VALUES (?,?,?,?)";
int primkey = 0 ;
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
Connection conn = DriverManager.getConnection(DB_URL, DB_User, DB_Password);
String columnNames[] = new String[] { "id" };
PreparedStatement pstmt = conn.prepareStatement( insertSQL, columnNames );
pstmt.setString(1, UserName );
pstmt.setString(2, Language );
pstmt.setString(3, Message );
if (pstmt.executeUpdate() > 0) {
// Retrieves any auto-generated keys created as a result of executing this Statement object
java.sql.ResultSet generatedKeys = pstmt.getGeneratedKeys();
if ( generatedKeys.next() ) {
primkey = generatedKeys.getInt(1);
}
}
System.out.println("Record updated with id = "+primkey);
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
I'm using SQLServer 2008, but I have a development limitation: I cannot use a new driver for it, I have to use "com.microsoft.jdbc.sqlserver.SQLServerDriver" (I cannot use "com.microsoft.sqlserver.jdbc.SQLServerDriver").
That's why the solution conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) threw a java.lang.AbstractMethodError for me.
In this situation, a possible solution I found is the old one suggested by Microsoft:
How To Retrieve ##IDENTITY Value Using JDBC
import java.sql.*;
import java.io.*;
public class IdentitySample
{
public static void main(String args[])
{
try
{
String URL = "jdbc:microsoft:sqlserver://yourServer:1433;databasename=pubs";
String userName = "yourUser";
String password = "yourPassword";
System.out.println( "Trying to connect to: " + URL);
//Register JDBC Driver
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();
//Connect to SQL Server
Connection con = null;
con = DriverManager.getConnection(URL,userName,password);
System.out.println("Successfully connected to server");
//Create statement and Execute using either a stored procecure or batch statement
CallableStatement callstmt = null;
callstmt = con.prepareCall("INSERT INTO myIdentTable (col2) VALUES (?);SELECT ##IDENTITY");
callstmt.setString(1, "testInputBatch");
System.out.println("Batch statement successfully executed");
callstmt.execute();
int iUpdCount = callstmt.getUpdateCount();
boolean bMoreResults = true;
ResultSet rs = null;
int myIdentVal = -1; //to store the ##IDENTITY
//While there are still more results or update counts
//available, continue processing resultsets
while (bMoreResults || iUpdCount!=-1)
{
//NOTE: in order for output parameters to be available,
//all resultsets must be processed
rs = callstmt.getResultSet();
//if rs is not null, we know we can get the results from the SELECT ##IDENTITY
if (rs != null)
{
rs.next();
myIdentVal = rs.getInt(1);
}
//Do something with the results here (not shown)
//get the next resultset, if there is one
//this call also implicitly closes the previously obtained ResultSet
bMoreResults = callstmt.getMoreResults();
iUpdCount = callstmt.getUpdateCount();
}
System.out.println( "##IDENTITY is: " + myIdentVal);
//Close statement and connection
callstmt.close();
con.close();
}
catch (Exception ex)
{
ex.printStackTrace();
}
try
{
System.out.println("Press any key to quit...");
System.in.read();
}
catch (Exception e)
{
}
}
}
This solution worked for me!
I hope this helps!
You can use following java code to get new inserted id.
ps = con.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, quizid);
ps.setInt(2, userid);
ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
lastInsertId = rs.getInt(1);
}
It is possible to use it with normal Statement's as well (not just PreparedStatement)
Statement statement = conn.createStatement();
int updateCount = statement.executeUpdate("insert into x...)", Statement.RETURN_GENERATED_KEYS);
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
if (generatedKeys.next()) {
return generatedKeys.getLong(1);
}
else {
throw new SQLException("Creating failed, no ID obtained.");
}
}
Most others have suggested to use JDBC API for this, but personally, I find it quite painful to do with most drivers. When in fact, you can just use a native T-SQL feature, the OUTPUT clause:
try (
Statement s = c.createStatement();
ResultSet rs = s.executeQuery(
"""
INSERT INTO t (a, b)
OUTPUT id
VALUES (1, 2)
"""
);
) {
while (rs.next())
System.out.println("ID = " + rs.getLong(1));
}
This is the simplest solution for SQL Server as well as a few other SQL dialects (e.g. Firebird, MariaDB, PostgreSQL, where you'd use RETURNING instead of OUTPUT).
I've blogged about this topic more in detail here.
With Hibernate's NativeQuery, you need to return a ResultList instead of a SingleResult, because Hibernate modifies a native query
INSERT INTO bla (a,b) VALUES (2,3) RETURNING id
like
INSERT INTO bla (a,b) VALUES (2,3) RETURNING id LIMIT 1
if you try to get a single result, which causes most databases (at least PostgreSQL) to throw a syntax error. Afterwards, you may fetch the resulting id from the list (which usually contains exactly one item).
In my case ->
ConnectionClass objConnectionClass=new ConnectionClass();
con=objConnectionClass.getDataBaseConnection();
pstmtGetAdd=con.prepareStatement(SQL_INSERT_ADDRESS_QUERY,Statement.RETURN_GENERATED_KEYS);
pstmtGetAdd.setString(1, objRegisterVO.getAddress());
pstmtGetAdd.setInt(2, Integer.parseInt(objRegisterVO.getCityId()));
int addId=pstmtGetAdd.executeUpdate();
if(addId>0)
{
ResultSet rsVal=pstmtGetAdd.getGeneratedKeys();
rsVal.next();
addId=rsVal.getInt(1);
}
If you are using Spring JDBC, you can use Spring's GeneratedKeyHolder class to get the inserted ID.
See this answer...
How to get inserted id using Spring Jdbctemplate.update(String sql, obj...args)
If you are using JDBC (tested with MySQL) and you just want the last inserted ID, there is an easy way to get it. The method I'm using is the following:
public static Integer insert(ConnectionImpl connection, String insertQuery){
Integer lastInsertId = -1;
try{
final PreparedStatement ps = connection.prepareStatement(insertQuery);
ps.executeUpdate(insertQuery);
final com.mysql.jdbc.PreparedStatement psFinal = (com.mysql.jdbc.PreparedStatement) ps;
lastInsertId = (int) psFinal.getLastInsertID();
connection.close();
} catch(SQLException ex){
System.err.println("Error: "+ex);
}
return lastInsertId;
}
Also, (and just in case) the method to get the ConnectionImpl is the following:
public static ConnectionImpl getConnectionImpl(){
ConnectionImpl conexion = null;
final String dbName = "database_name";
final String dbPort = "3306";
final String dbIPAddress = "127.0.0.1";
final String connectionPath = "jdbc:mysql://"+dbIPAddress+":"+dbPort+"/"+dbName+"?autoReconnect=true&useSSL=false";
final String dbUser = "database_user";
final String dbPassword = "database_password";
try{
conexion = (ConnectionImpl) DriverManager.getConnection(connectionPath, dbUser, dbPassword);
}catch(SQLException e){
System.err.println(e);
}
return conexion;
}
Remember to add the connector/J to the project referenced libraries.
In my case, the connector/J version is the 5.1.42. Maybe you will have to apply some changes to the connectionPath if you want to use a more modern version of the connector/J such as with the version 8.0.28.
In the file, remember to import the following resources:
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.mysql.jdbc.ConnectionImpl;
Hope this will be helpful.
Connection cn = DriverManager.getConnection("Host","user","pass");
Statement st = cn.createStatement("Ur Requet Sql");
int ret = st.execute();

How to pass an array of BLOB to a stored oracle procedure?

I am allowing user to upload multiple files to my database. These file contents must be stored in my oracle database as BLOB.
How can i write a oracle procedure to do this ? (I have a little knowledge of Oracle stored procedures) ?
Once this is done how can i use the stored procedure in java using jdbc's CallableStatement ?
Please help.
First of all, you have to create the type that will contain the table of BLOB:
CREATE OR REPLACE TYPE tab_blobs AS TABLE OF BLOB;
In Java, you have to rely on the STRUCT type provided by Oracle sql.
You will create a STRUCT that will contain the array of BLOB to store into the DB.
The code would look like the following:
import java.sql.Connection;
import java.sql.SQLException;
import oracle.jdbc.driver.OracleDriver;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;
public class ArrayDemo
{
public static void passArray()
throws SQLException
{
Connection conn = new OracleDriver().defaultConnection();
byte[] fileInByteArray = "value".getBytes();
StructDescriptor itemDescriptor = StructDescriptor.createDescriptor("BLOB", conn);
Object[] itemAtributes = new Object[] {};
STRUCT itemObject1 = new STRUCT(itemDescriptor, conn, itemAtributes);
itemAtributes = new Object[] {};
STRUCT itemObject2 = new STRUCT(itemDescriptor, conn, itemAtributes);
STRUCT[] idsArray = { itemObject1, itemObject2 };
ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor("IDS_TABLE", conn);
ARRAY array_to_pass = new ARRAY(descriptor, conn, idsArray);
OraclePreparedStatement ps = (OraclePreparedStatement) conn.prepareStatement("begin getInfo(:x); end;");
ps.setARRAY(1, array_to_pass);
ps.execute();
}
}
But why don't you simplify the handling by iterating on the files, inserting them one after the other:
public static void insererBlob(String name, String path) {
File file = new File(path);
try{
//link to DB
Connection connection = DriverManager.getConnection("url","user","password");
//link to file
FileInputStream stream = new FileInputStream(file);
//prepare the SQL instruction
String sql = "INSERT INTO file_table VALUES (?, ?)";
PreparedStatement statement = connection.prepareStatement(sql);
//blob insertion
statement.setString(1, name);
statement.setBinaryStream(2, stream, (int)file.length());
statement.executeUpdate();
}catch(Exception e){
//ERROR SQL, IO, etc .
}finally {
//close connection ?
}
}
Here is another attempty to help you.
You can find more info from oracle here:
http://docs.oracle.com/cd/B28359_01/java.111/e10788/connect.htm#CHDGHFCG
http://docs.oracle.com/javase/tutorial/jdbc/basics/
Also, example taken from (website of great help at the time i had to do it):
http://docs.oracle.com/javase/tutorial/jdbc/basics/blob.html
public void addRowToCoffeeDescriptions(
String coffeeName, String fileName)
throws SQLException {
PreparedStatement pstmt = null;
try {
Clob myClob = this.con.createClob();
Writer clobWriter = myClob.setCharacterStream(1);
String str = this.readFile(fileName, clobWriter);
System.out.println("Wrote the following: " +
clobWriter.toString());
if (this.settings.dbms.equals("mysql")) {
System.out.println(
"MySQL, setting String in Clob " +
"object with setString method");
myClob.setString(1, str);
}
System.out.println("Length of Clob: " + myClob.length());
String sql = "INSERT INTO COFFEE_DESCRIPTIONS " +
"VALUES(?,?)";
pstmt = this.con.prepareStatement(sql);
pstmt.setString(1, coffeeName);
pstmt.setClob(2, myClob);
pstmt.executeUpdate();
} catch (SQLException sqlex) {
JDBCTutorialUtilities.printSQLException(sqlex);
} catch (Exception ex) {
System.out.println("Unexpected exception: " + ex.toString());
} finally {
if (pstmt != null)pstmt.close();
}
}
Below is my code hope you got your answer:
from java code:
try {Class.forName("oracle.jdbc.driver.OracleDriver");
String url = "jdbc:oracle:thin:#localhost:1521:orcl";
Connection con = DriverManager.getConnection(url, db_user, password);
System.out.println("Connected to database");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
Date now = new java.sql.Date(simpleDateFormat.parse("12/02/2001").getTime());
String command2 = "{call USER1(?,?,?,?)}";
String path;
File[] roots = File.listRoots();
path=roots[0].getPath()+"dfg.jpg";
System.out.println("path: " + path);
//shows drives in you computer
for(int i = 0; i < roots.length ; i++){
System.out.println("drive: " + roots[i].getPath());
}
CallableStatement insertStatment = con.prepareCall(command2);
insertStatment.setInt(1, 18);
insertStatment.setString(2, "ankssaait");
insertStatment.setDate(3, now);
File file = new File(path);
//link to file
FileInputStream stream = new FileInputStream(file);
insertStatment.setBinaryStream(4, stream,(int)file.length());;
System.out.println("onExecute: "+ insertStatment.executeUpdate());
insertStatment.close();
System.out.println("done");
} catch (Exception e) {
e.printStackTrace();
}
my tablei is:
CREATE TABLE "ANKIT"."O_USER"
( "NAME" VARCHAR2(20 BYTE),
"GENDER" CHAR(1 BYTE),
"DESIGNTION" VARCHAR2(20 BYTE),
"YEAR" INTERVAL YEAR (2) TO MONTH,
"ID" NUMBER NOT NULL ENABLE,
"DOB" DATE,
"PROOF" CLOB,
"ADDRESS" VARCHAR2(100 BYTE),
"TELEPHONE" NUMBER,
"RAW1" RAW(20),
"IMAGE" BLOB,
CONSTRAINT "O_USER_PK" PRIMARY KEY ("ID"));
my procedure is:
CREATE OR REPLACE PROCEDURE USER1 (U_ID IN o_user.id%TYPE, U_NAME in o_user.name%TYPE, u_DOB in o_user.dob%TYPE, u_image in o_user.image%TYPE) AS BEGIN insert into o_user(id,name,dob,image) values(u_id,u_name,u_dob,u_image); END USER1;

Multiple queries on database using jdbc

I have a text file from where i read values
FileInputStream file = new FileInputStream("C:/workspace/table_export.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(file));
String line = null;
while( (line = br.readLine())!= null )
{
String [] tokens = line.split("\\s+");
String var_1 = tokens[0];
System.out.println(var_1);
getstaffinfo(var_1,connection);
}
The values read from text file is passed to getstaffinfo method to query the db
public static String getstaffinfo(String var_1, Connection connection) throws SQLException, Exception
// Create a statement
{
StringBuffer query = new StringBuffer();
ResultSet rs = null;
String record = null;
Statement stmt = connection.createStatement();
query.delete(0, query.length());
query.append("select firstname, middlename, lastname from users where employeeid = '"+var_1+"'");
rs = stmt.executeQuery(query.toString());
while(rs.next())
{
record = rs.getString(1) + " " +
rs.getString(2) + " " +
rs.getString(3);
System.out.println(record);
}
return record;
}
I get almost 14000 values read from text file which is passed to getstaffinfo method, all database activities such has loading driver, establishing connectivity all works fine. But while printing
it throws error
java.sql.SQLException: ORA-00604: error occurred at recursive SQL level 1
ORA-01000: maximum open cursors exceeded
ORA-01000: maximum open cursors exceeded
Although i understand that this error is to do with database configuration, Is there an efficent way of making one db call and exceute the query for multiple values read from text file.
Any inputs would be of great use.
Many Thanks in advance!!
Close ResultSet rs.close(); and Statement stmt.close(); after your while loop in getstaffinfo(), preferably inside a finally{}
You need to close resultSet via rs.close(); and Statement via stmt.close();
while(rs.next()){
record = rs.getString(1) + " " +
rs.getString(2) + " " +
rs.getString(3);
System.out.println(record);
}
rs.close();
stmt.close();
The issue is you are not closeing ResultSet and Statement (though it is good that your are working on one Connection), try closing resources, the error should not happen.
Bigger issue is that if you do close, you are hitting DB n number of times where n is number of filtering criteria. One solution to this could be make in clause instead of = selection.
e.g:
Say total lines = N, divide into x chunks hence make N/x select statements
For example is N=20000, x=1000; you need to fire 20 selects instead of 20000.
close resultset and statement
like this way
rs.close(); stmt.close();
Best way is to use IN clause in the query and call the method only once.
FileInputStream file = new FileInputStream("C:/workspace/table_export.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(file));
String line = null;
String var_1 = "";
while( (line = br.readLine())!= null )
{
String [] tokens = line.split("\\s+");
var_1 = var_1+tokens[0]+",";
System.out.println(var_1);
}
var_1 = var_1.subString(0,var_1.lastIndexOf(","));
getstaffinfo(var_1,connection);
change getstaffinfo() like this
public static List<String> getstaffinfo(String var_1, Connection connection) throws SQLException, Exception
// Create a statement
{
StringBuffer query = new StringBuffer();
ResultSet rs = null;
String record = null;
List<String> list = new ArrayList<String>();
Statement stmt = connection.createStatement();
query.delete(0, query.length());
query.append("select firstname, middlename, lastname from users where employeeid IN ("+var_1+")");
try{
rs = stmt.executeQuery(query.toString());
while(rs.next())
{
record = rs.getString(1) + " " +rs.getString(2) + " " +rs.getString(3);
System.out.println(record);
list.add(record);
}
}finally{
stmt.close();
rs.close();
}
return list;
}
Note : Cant we put more than 1000 values in 'in clause'.
Related links.
1.https://forums.oracle.com/thread/235143?start=0&tstart=0
2.java.sql.SQLException: - ORA-01000: maximum open cursors exceeded

How to write / update Oracle blob in a reliable way?

I'm trying to write and update a pdf document in a blob column but I'm just able to update the blob only writing more data than the previous stored data.
If I try to update the blob column with a smaller document data I get only a corrupted pdf.
First the blob column has been initialized using empty_blob() function. I wrote the sample Java class below to test this behaviour. I run it the first time with 'true' as first parameter of the main method so in the first row there's stored a document of about 31kB and in the second row there's a document of 278kB.
Then I run it with 'false' as parameter, in this way the two rows should be updated swapping the documents. The result is that I get a correct result only when I write more data than the existing one.
How is it possible to write a method that writes and updates a blob in a reliable way without worring about binary data's size?
import static org.apache.commons.io.IOUtils.copy;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import oracle.jdbc.OracleDriver;
import oracle.jdbc.OracleResultSet;
import oracle.sql.BLOB;
import org.apache.commons.lang.ArrayUtils;
/**
* Prerequisites:
* 1) a table named 'x' must exists [create table x (i number, j blob);]
* 2) that table should have two columns [insert into x (i, j) values (1, empty_blob()); insert into x (i, j) values (2, empty_blob()); commit;]
* 3) download lsp.pdf from http://www.objectmentor.com/resources/articles/lsp.pdf
* 4) download dotguide.pdf from http://www.graphviz.org/Documentation/dotguide.pdf
*/
public class UpdateBlob {
public static void main(String[] args) throws Exception {
processFiles(new String[]{"lsp.pdf", "dotguide.pdf"}, Boolean.valueOf(args[0]));
}
public static void processFiles(String [] fileNames, boolean forward) throws Exception {
if(!forward){
ArrayUtils.reverse(a);
}
int idx = 1;
for(String fname : fileNames){
insert(idx++, fname);
}
}
private static void insert(int idx, String fname) throws Exception{
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
DriverManager.registerDriver(new OracleDriver());
conn = DriverManager.getConnection("jdbc:oracle:thin:#"+db+":"+port+":"+sid, user, pwd);
ps = conn.prepareStatement("select j from x where i = ? for update");
ps.setLong(1, idx);
rs = ps.executeQuery();
if (rs.next()) {
FileInputStream instream = new FileInputStream(fname);
BLOB blob = ((OracleResultSet)rs).getBLOB(1);
OutputStream outstream = blob.setBinaryStream(1L);
copy(instream, outstream);
instream.close();
outstream.close();
}
rs.close();
ps.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new Exception(e);
}
}
}
Oracle version: 11.1.0.7.0 - 64bit
I even tried the standard JDBC API without using Oracle's specific one (like in the example above) without any success.
It's a lot easier:
PreparedStatement pstmt =
conn.prepareStatement("update blob_table set blob = ? where id = ?");
File blob = new File("/path/to/picture.png");
FileInputStream in = new FileInputStream(blob);
// the cast to int is necessary because with JDBC 4 there is
// also a version of this method with a (int, long)
// but that is not implemented by Oracle
pstmt.setBinaryStream(1, in, (int)blob.length());
pstmt.setInt(2, 42); // set the PK value
pstmt.executeUpdate();
conn.commit();
pstmt.close();
It works the same when using an INSERT statement. No need for empty_blob() and a second update statement.
In addition to a_horse_with_no_name's answer (which relies on PreparedStatement.setBinaryStream(...) API), there're at least two more options for BLOBs, and 3 more for CLOBs and NCLOBs:
Explicitly create a LOB, write to it, and use PreparedStatement.setBlob(int, Blob):
int insertBlobViaSetBlob(final Connection conn, final String tableName, final int id, final byte value[])
throws SQLException, IOException {
try (final PreparedStatement pstmt = conn.prepareStatement(String.format("INSERT INTO %s (ID, VALUE) VALUES (?, ?)", tableName))) {
final Blob blob = conn.createBlob();
try (final OutputStream out = new BufferedOutputStream(blob.setBinaryStream(1L))) {
out.write(value);
}
pstmt.setInt(1, id);
pstmt.setBlob(2, blob);
return pstmt.executeUpdate();
}
}
Update an empty LOB (inserted via DBMS_LOB.EMPTY_BLOB() or DBMS_LOB.EMPTY_CLOB()) via SELECT ... FOR UPDATE. This is Oracle-specific and requires two statements executed instead of one. Additionally, this is what you were trying to accomplish in the first place:
void insertBlobViaSelectForUpdate(final Connection conn, final String tableName, final int id, final byte value[])
throws SQLException, IOException {
try (final PreparedStatement pstmt = conn.prepareStatement(String.format("INSERT INTO %s (ID, VALUE) VALUES (?, EMPTY_BLOB())", tableName))) {
pstmt.setInt(1, id);
pstmt.executeUpdate();
}
try (final PreparedStatement pstmt = conn.prepareStatement(String.format("SELECT VALUE FROM %s WHERE ID = ? FOR UPDATE", tableName))) {
pstmt.setInt(1, id);
try (final ResultSet rset = pstmt.executeQuery()) {
while (rset.next()) {
final Blob blob = rset.getBlob(1);
try (final OutputStream out = new BufferedOutputStream(blob.setBinaryStream(1L))) {
out.write(value);
}
}
}
}
}
For CLOBs and NCLOBs, you can additionally use PreparedStatement.setString() and setNString(), respectively.
FWIW, for something that fits in memory, I found I could simply pass in a byte array as the prepared statement parameter, rather than going through the "stream" rigor morale (or worse Oracle specific/suggested things)
Using a Spring "JDBC template" wrapper (org.springframework.jdbc.core.JdbcTemplate) to put the contents of a "large" (or not) string into a BLOB column, the code is something like the following:
jdbc.update( "insert into a_table ( clob_col ) values ( ? )", largeStr.getBytes() );
There is no step 2.

Java: How to insert CLOB into oracle database

I need to write an XML file content into oracle database where the column is of CLOB datatype.
How will I do that?
The easiest way is to simply use the
stmt.setString(position, xml);
methods (for "small" strings which can be easily kept in Java memory), or
try {
java.sql.Clob clob =
oracle.sql.CLOB.createTemporary(
connection, false, oracle.sql.CLOB.DURATION_SESSION);
clob.setString(1, xml);
stmt.setClob(position, clob);
stmt.execute();
}
// Important!
finally {
clob.free();
}
OUTDATED See Lukas Eder's answer below.
With about 100 lines of code ;-) Here is an example.
The main point: Unlike with other JDBC drivers, the one from Oracle doesn't support using Reader and InputStream as parameters of an INSERT. Instead, you must SELECT the CLOB column FOR UPDATE and then write into the ResultSet
I suggest that you move this code into a helper method/class. Otherwise, it will pollute the rest of your code.
passing the xml content as string.
table1
ID int
XML CLOB
import oracle.jdbc.OraclePreparedStatement;
/*
Your Code
*/
void insert(int id, String xml){
try {
String sql = "INSERT INTO table1(ID,XML) VALUES ("
+ id
+ "', ? )";
PreparedStatement ps = conn.prepareStatement(sql);
((OraclePreparedStatement) ps).setStringForClob(1, xml);
ps.execute();
result = true;
} catch (Exception e) {
e.printStackTrace();
}
}
This code worked for me. I use ojdbc6-11.2.0.2.jar.
java.sql.Connection con;
javax.xml.bind.Marshaller marshaller;
Clob xmlClob = con.createClob();
try {
try (Writer xmlClobWriter = xmlClob.setCharacterStream(1)) {
m.marshal(jaxbObject, xmlClobWriter);
} // xmlClobWriter.close();
try (PreparedStatement stmt = con.prepareStatement("INSERT INTO table (xml) values(?)")) {
stmt.setClob(1, xmlClob);
stmt.executeUpdate();
}
} finally {
xmlClob.free();
}
Converting clob to string:
Clob clob=rs.getClob(2);
String str=(String)clob.getSubString(1,(int)clob.length());
System.out.println("Clob Data is : "+str);
For this purpose you need to make the connection result set
ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE
Connection con=null;
//initialize connection variable to connect to your database...
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String query="Select MYCLOB from TABLE_NAME for update";
con.setAutoCommit(false);
ResultSet resultset=stmt.executeQuery(query);
if(resultset.next()){
oracle.sql.CLOB clobnew = ((OracleResultSet) rss).getCLOB("MYCLOB");
PrintWriter pw = new PrintWriter(clobnew.getCharacterOutputStream() );
BufferedReader br = new BufferedReader( new FileReader( new File("filename.xml") ) );
String lineIn = null;
while( ( lineIn = br.readLine() ) != null )
pw.println( lineIn );
pw.close();
br.close();
}
con.setAutoCommit(true);
con.commit();
}
Note: its important that you add the phrase for update at the end of the query that is written to select the row...
Follow the above code to insert the XML file
You can very well do it with below code, i am giving you just the code to insert xml hope u are done with rest of other things..
import oracle.xdb.XMLType;
//now inside the class......
// this will be to convert xml into string
File file = new File(your file path);
FileReader fileR = new FileReader(file);
fileR.read(data);
String str = new String(data);
// now to enter it into db
conn = DriverManager.getConnection(serverName, userId, password);
XMLType objXml = XMLType.createXML(conn, str);
// inside the query statement put this code
objPreparedstatmnt.setObject(your value index, objXml);
I have done like this and it is working fine.
I had similar issue. Changed one of my table column from varchar2 to CLOB.
I didn't needed to change any java code. I kept it as setString(..) only so no need to change set method as setClob() etch if you are using following versions ATLEAST of Oracle and jdbc driver.
I tried in In Oracle 11g and driver ojdbc6-11.2.0.4.jar
Try this , there is no need to set its a CLOB
public static void main(String[] args)
{
try{
System.out.println("Opening db");
Class.forName("oracle.jdbc.driver.OracleDriver");
if(con==null)
con=DriverManager.getConnection("jdbc:oracle:thin:#192.9.200.103:1521: orcl","sas","sas");
if(stmt==null)
stmt=con.createStatement();
int res=9;
String usersSql = "{call Esme_Insertsmscdata(?,?,?,?,?)}";
CallableStatement stmt = con.prepareCall(usersSql);
// THIS THE CLOB DATA
stmt.setString(1,"SS¶5268771¶00058711¶04192018¶SS¶5268771¶00058712¶04192018¶SS¶5268772¶00058713¶04192018¶SS¶5268772¶00058714¶04192018¶SS¶5268773¶00058715¶04192018¶SS¶5268773¶00058716¶04192018¶SS¶5268774¶00058717¶04192018¶SS¶5268774¶00058718¶04192018¶SS¶5268775¶00058719¶04192018¶SS¶5268775¶00058720¶04192018¶");
stmt.setString(2, "bcvbcvb");
stmt.setString(3, String.valueOf("4522"));
stmt.setString(4, "42.25.632.25");
stmt.registerOutParameter(5,OracleTypes.NUMBER);
stmt.execute();
res=stmt.getInt(5);
stmt.close();
System.out.println(res);
}
catch(Exception e)
{
try
{
con.close();
} catch (SQLException e1) {
}
}
}
}
Take a look at the LobBasicSample for an example to use CLOB, BLOB, NLOB datatypes.

Categories