I use the following codes to update clob:
pStmt = conn.prepareStatement("SELECT DETAILS FROM PROGRAM_HISTORY WHERE id = 12");
rset = pStmt.executeQuery();
Clob detailsClob= rset.getClob(1);
System.out.println(detailsClob.length()); output: 100
Writer writer = ((oracle.sql.CLOB)detailsClob).getCharacterOutputStream();
writer.write("add more details");
writer.flush();
writer.close();
System.out.println(detailsClob.length()); output: 100
pStmt = conn.prepareStatement("UPDATE PROGRAM_HISTORY SET DETAILS = ? WHERE ID = 12");
pStmt.setClob(1, detailsClob);
pStmt.execute();
The problem is that clob attribute cannot be updated correctly, I want use the new string to replace the existing clob value in Oracle, but it was not replaced, the length which was printed out on two times are same.
So Ideally writer just appended the later value, whether the writer should be cleaned out empty? how I can do this?
Thanks in advance, can everybody give me some ideas?
Related
I am working on proof of concept to read a blob content from Oracle, manipulate it and then insert back as new record using Java. Currently, I am trying to just read and then write back blob content to Oracle but facing issues. I am able to write back but looks like the file is not getting inserted completely.
Error when trying to view/download Blob via SQL developer
Code used to read and write back
conn.setAutoCommit(false);
stmt = conn.createStatement();
sql = "SELECT DOC_ID, NAME, BLOB_CONTENT FROM DOCUMENTS WHERE DOC_ID = " + String.valueOf(docid);
ResultSet rs_docs = stmt.executeQuery(sql);
while (rs_docs.next()) {
Show_Message("Conversion sub process started ...");
doc_name = rs_docs.getString("name");
Blob ib = rs_docs.getBlob("blob_content");
Show_Message("Uploading converted pdf to database ... ");
InputStream input = ib.getBinaryStream();
String filename = doc_name;
CallableStatement callableStatement = conn.prepareCall("begin INSERT INTO DOCUMENTS(NAME, BLOB_CONTENT) VALUES(?,?) RETURNING DOC_ID INTO ?; end;");
callableStatement.setString(1, filename);
callableStatement.setBinaryStream(2, input, input.available());
callableStatement.registerOutParameter(3, Types.INTEGER);
callableStatement.executeQuery();
docid = callableStatement.getInt(3);
callableStatement.close();
Show_Message("New record created, doc # " + String.valueOf(docid));
Show_Message("Conversion Process completed!!!");
}
stmt.close();
conn.commit();
conn.close();
rs_docs.close();
Connection.prepareCall() is for creating a Statement that calls a stored procedure. If you want to do that then you should define an SP in the database, outside the scope of this method, and call it by name via your [Callable]Statement. But if the only point is to get the DOC_ID assigned to the new row, then there are other ways, such as Statement.getGeneratedKeys().
Do not use InputStream.available() to determine the size of the blob. "Available" means the number of bytes readable without blocking, right now, and it is allowed to be an arbitrarily inaccurate underestimate -- even zero. It is not a reliable measure of the total number of bytes that may eventually be readable from the stream. Instead, use the Blob's length() method.
I have an assignment where I need to update records using a PreparedStatement. Once the record have been updated as we know update query return count, i.e., number of row affected.
However, instead of the count I want the rows that were affected by update query in response, or at least a list of id values for the rows that were affected.
This my update query.
UPDATE User_Information uInfo SET address = uInfo.contact_number || uInfo.address where uInfo.user_id between ? AND ?;
Normally it will return count of row affected but in my case query should return the ids of row or all the row affected.
I have used the returning function of PostgreSQL it is working but is not useful for me in that case.
i have used returning function of PostgreSQL but is not useful for me
It should be. Perhaps you were just using it wrong. This code works for me:
sql = "UPDATE table1 SET customer = customer || 'X' WHERE customer LIKE 'ba%' RETURNING id";
try (PreparedStatement s = conn.prepareStatement(sql)) {
s.execute(); // perform the UPDATE
try (ResultSet rs = s.getResultSet()) {
// loop through rows from the RETURNING clause
while (rs.next()) {
System.out.println(rs.getInt("id")); // print the "id" value of the updated row
}
}
}
The documentation indicates that we can also use RETURNING * if we want the ResultSet to include the entire updated row.
Update:
As #CraigRinger suggests in his comment, the PostgreSQL JDBC driver does actually support .getGeneratedKeys() for UPDATE statements too, so this code worked for me as well:
sql = "UPDATE table1 SET customer = customer || 'X' WHERE customer LIKE 'ba%'";
try (PreparedStatement s = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
s.execute(); // perform the UPDATE
try (ResultSet rs = s.getGeneratedKeys()) {
while (rs.next()) {
System.out.println(rs.getInt(1)); // print the "id" value of the updated row
}
}
}
Thanks, Craig!
You might be able to use JDBC's support for getting generated keys. See the Connection.prepareStatement(String sql, int[] columnIndexes) API method, then use Statement.getGeneratedKeys() to access the results.
The spec says "the driver will ignore the array if the SQL statement is not an INSERT statement" but I think PostgreSQL's JDBC driver will actually honour your request with other statement types too.
e.g.
PreparedStatement s = conn.prepareStatement(sql, new String[] {'id'})
s.executeUpdate();
ResultSet rs = s.getGeneratedKeys();
Otherwise, use RETURNING, as Gord Thompson describes.
There are two way of doing it
1. by passing an array of column name or index of column prepareStatement
i.e conn.prepareStatement(sql, new String[] {'id','uname'})
and
2. by using Statement.RETURN_GENERATED_KEYS in prepareStatement.
My code is for this i.e as per my requirement i have developed my code you can have a look for better idea.
private static final String UPDATE_USER_QUERY= "UPDATE User_Information uInfo SET address = uInfo.contact_number || uInfo.address where uInfo.user_id between ? AND ?;";
//pst = connection.prepareStatement(UPDATE_USER_QUERY,columnNames);
pst = connection.prepareStatement(UPDATE_USER_QUERY,Statement.RETURN_GENERATED_KEYS);
ResultSet rst = pst.getGeneratedKeys();
List<UserInformation> userInformationList = new ArrayList<UserInformation>();
UserInformation userInformation;
while (rst.next()){
userInformation = new UserInformation();
userInformation.setUserId(rst.getLong("user_id"));
userInformation.setUserName(rst.getString("user_name"));
userInformation.setUserLName(rst.getString("user_lName"));
userInformation.setAddress(rst.getString("address"));
userInformation.setContactNumber(rst.getLong("contact_number"));
userInformationList.add(userInformation);
}
That think i need to achieve in this case.
Hope so this will help you a lot.
Here are the info:
I have a String
I want to insert a record in a table with the String in a column whose
datatype is CLOB.
I would like to use setClob() method of the preparedstatement.
So my question is how to create a Clob object from this String so that I
can use setClob() method.
Thanks in advance,
Naveen
If you want to write a String to CLOB column just use PreparedStatement.setString.
If you want to know how to create a CLOB from String this is it
Clob clob = connection.createClob();
clob.setString(1, str);
You may create the clob from a connection object as follows
Connection con = null;// write code to make a connection object
Clob clob = con.createClob();
String str = "this is a stirng";
clob.setString(1, str );
PreparedStatement ps = null;// write code to create a prepared statement
ps.setClob(4, clob);
Or you may try the alternative code as follows :
//alternative way
String str = "this is a stirng";
ByteArrayInputStream inputStream = new ByteArrayInputStream(str.getBytes());
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
int parameterIndex = 1;
PreparedStatement ps = null;// write code to create a prepared statement
ps.setClob(parameterIndex, inputStreamReader);
For CLOB it is of String already. So, just use .setString() and that should work. One thing about ORACLE jdbc if you are using it, it like the CLOB INPUT parameter to be the last one in your statement especially with a large data.
Example:
INSERT INTO MY_TABL (NUM_COL, VARC_COL, VARC_COL, TS_COL, CLOB_COL)
VALUES(?,?,?,?,?);
As you can see, the CLOB_COL is of type CLOB and should be last so that when
you do .setString(5) and 5 is the last index.
I had a specific variation of this issue which required to insert a clob into an Oracle database from java code running on that db. None of the answers here quite worked for me.
I eventually found solution, the trick being to use oracle.sql.CLOB
This the approach I discovered:
create table test_clob (
c clob
);
create or replace and compile java source named java_clob_insert as
import java.sql.Connection;
import java.sql.PreparedStatement;
import oracle.sql.CLOB;
import java.io.Writer;
public class JavaClobInsert {
public static void doInsert () {
try {
//create the connection and statement
Connection oracleConn =
(new oracle.jdbc.OracleDriver()).defaultConnection();
String stmt = "INSERT INTO test_clob values (?)";
PreparedStatement oraclePstmt = oracleConn.prepareStatement(stmt);
//Imagine we have a mysql longtext or some very long string
String s = "";
for (int i = 0; i < 32768; i++) {
s += i % 10;
}
//Initialise the Oracle CLOB
CLOB clob;
clob = CLOB.createTemporary(oracleConn, true, CLOB.DURATION_CALL);
//Good idea to check the string is not null before writing to clob
if (s != null) {
Writer w = clob.setCharacterStream( 1L );
w.write(s);
w.close();
oraclePstmt.setClob(1, clob);
} else {
oraclePstmt.setString(1, "");
}
//clean up
oraclePstmt.executeUpdate();
oracleConn.commit();
oraclePstmt.close();
oracleConn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/
create or replace procedure clob_insert as language java name
'JavaClobInsert.doInsert()';
/
begin
clob_insert;
end;
/
select *
from test_clob;
Today i had an issue with a Clob field because i was using "setString" to set the parameter, but then i had this error while testing with a very long string: "setString can handle only Strings with less than 32766 characters"
I used connection.createClob but it gave me this exception:
java.lang.AbstractMethodError: org.apache.tomcat.dbcp.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.createClob()Ljava/sql/Clob;
So looking for this exception i found this
using CLOB in java throwing exception and the accepted answer (using setCharacterStream instead of setClob) worked for me
Copy/Pasted from the accepted answer (so all credits are for a_horse_with_no_name )
StringReader reader = new StringReader(userAbout);
PreparedStatement insertClob = dbCon.prepareStatement("UPDATE user_data SET user_about=? WHERE user_id=?");
insertClob.setCharacterStream(1, reader, userAbout.length());
insertClob.setInt(2,userId);
My answer is slightly different than others...
I had a PreparedStatement, stmt, and was using stmt.setString(colIndex, value) for updates to my database that had a CLOB column.
This worked without fail for me when inserting and updating rows in the database table.
When others tested this code though they would occasionally see an exception occur:
ORA-22275: invalid LOB locator
It only seemed to happen on updates, not inserts - not sure why on that, when value was null. And I only ever had this occur with Oracle databases, not MSSQL or DB2.
Anyway to fix it I changed the logic to test for a null value
if (value == null) {
stmt.setNull(colIndex, java.sql.Types.CLOB);
}
else {
stmt.setString(colIndex, value);
}
This worked without fail for me and others!
My source code has the following structure:
SourceFolder
AddProduct.jsp
Source Packages
-Controller(Servlets)
SaveProduct.java
-Model(Db Operations)
ProductDbOperations.java
I am inserting a new product into the product table and at the same time I am inserting an entry into product_collection table (product_id | collection_id).
To insert an entry into the product_collection table i need to get generated id from product table. After that a new entry is inserted into the product_collection table.
Also, I am not using any Framework and am using Netbeans 7.3.
Problem:
A new entry is inserted into the product table with this piece of code
IN: ProductDbOperations.java
try
{
this.initConnection(); // Db connection
pst = cn.prepareStatement("INSERT INTO product values('"+name+"', "+quantity+", "+price+")");
rs = pst.executeUpdate();
}
catch(SQLException ex)
{
}
I Also used the solution at following link which doesn't works for me.
I didn't got any SQL exception
How to get the insert ID in JDBC?
so help me find out why this code not working for me .
Thanks a million.
Not all drivers support the version of getGeneratedKeys() as shown in the linked answer. But when preparing the statement, you can also pass the list of columns that should be returned instead of the "flag" Statement.RETURN_GENERATED_KEYS (and passing the column names works more reliably in my experience)
Additionally: as javaBeginner pointed out correctly, your usage of a prepared statement is wrong. The way you do it, will still leave you wide open to SQL injection.
// run the INSERT
String sql = "INSERT INTO product values(?,?,?)";
pst = cn.prepareStatement(sql, new String[] {"PRODUCT_ID"} );
pst.setString(1, name);
pst.setInt(2, quantity);
pst.setInt(3, price);
pst.executeUpdate();
// now get the ID:
ResultSet rs = pst.getGeneratedKeys();
if (rs.next()) {
long productId = rs.getLong(1);
}
Note that the column name passed to the call is case-sensitive. For Oracle the column names are usually uppercase. If you are using e.g. Postgres you would most probably need to pass new String[] {"product_id"}
The way you are using is not the proper way of using preparedstatement
use the following way
pst = cn.prepareStatement("INSERT INTO product values(?,?,?)");
pst.setString(1,name);
pst.setInt(2,quantity);
pst.setInt(3,price);
pst.executeUpdate();
Yes there is a way to retrieve the key inserted by SQL. You can do it by:
Using Statement.RETURN_GENERATED_KEYS in your previous insert and get the key which can be used in further insert
e.g:
String query = "INSERT INTO Table (Col2, Col3) VALUES ('S', 50)";
Statement stmt = con.createStatement();
int count = stmt.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
I know it works by using SQL
update activity set REFERENCE = EMPTY_CLOB() where id = ?
But I cannot do like this, I cannot hard coded 'EMPTY_CLOB()' in SQL.
I used the way like the following:
String empty_string = "";
conn = getConnection();
pStmt = conn.prepareStatement("SELECT REFERENCE FROM activity WHERE ID = ? FOR UPDATE");
pStmt.setLong(1, 1);
rset = pStmt.executeQuery();
Clob clob = null;
while (rset.next()) {
clob = rset.getClob(1);
Writer writer = adapter.getCharacterOutputStream(clob);
writer.write(empty_string);
writer.flush();
writer.close();
}
pStmt = conn.prepareStatement("update activity set REFERENCE = ? WHERE ID = ?");
pStmt.setClob(1, clob);
pStmt.setLong(2, 1);
pStmt.executeUpdate();
But It didn't work. the clob didn't be updated to empty string, it still stored as previous value.
Any body can help me on this?
As I have already mentionued in your other question: in my experience getClob() and setClob() don't work properly.
Use setCharacterStream() instead:
StringReader clob = new StringReader("");
pStmt = conn.prepareStatement("update activity set REFERENCE = ? WHERE ID = ?");
pStmt.setCharacterStream(1, clob, 0);
pStmt.setLong(2, 1);
pStmt.executeUpdate();
That way you can also remove the unnecessary SELECT before updating, which will improve performance as well.
Another option would be to simply set that column to NULL
Edit:
With newer drivers (11.x) you might also want to try to use setString() and getString() on the CLOB column.
The locking of the row should only be necessary when you use a LOB locator that you intend to keep during a transaction that spans more than one statement (at least that's my understanding of the linked reference to the manual).