Use Oracle createTemporary to update Clob - java

I used the following codes to update Oracle Clob:
CLOB tempClob = null;
try {
Connection conn = getConnection();
PreparedStatement = = conn.prepareStatement("UPDATE PROGRAM_HISTORY SET DETAILS = ? WHERE ID = 12");
tempClob = CLOB.createTemporary(conn, true, CLOB.DURATION_SESSION);
tempClob.open(CLOB.MODE_READWRITE);
Writer tempClobWriter = tempClob.getCharacterOutputStream();
tempClobWriter.write(clobData);
tempClobWriter.flush();
tempClobWriter.close();
tempClob.close();
pStmt.setClob(1, tempClob);
pStmt.execute();
} catch (Exception ex) { // Trap errors
System.out.println(" Error Inserting Clob : "+ex.toString());
ex.printStackTrace();
}finally{
if(tempClob != null ) tempClob.freeTemporary();
opstmt.close();
conn.close();
}
As you can see, after creating temporary clob, I used tempClob.open(CLOB.MODE_READWRITE);
to open and use tempClob.close() to colse later; so My question is that is this necessary? if yes why? because some example codes I searched from google don't have this procedure.
My second question is that is this required to tempClob.close() in finally statement; we must close temporary clob just like connection after used out? or don't need do this it will be automatically released?

The Oracle session object will keep a reference to the CLOB, so the garbage collector won't touch it. It will be freed automaticly when the session is closed.
Note that the actual temp CLOB memory will not exist somewhere in the Java VM, but either in the Oracle server process (PGA) or in the temp tablespace (disk), depending on your database configuration and the current workload.

Related

H2 Database result set is readonly

I'm getting the SQLNonTransientException error when trying to update one of my rows in a H2 database.
public static void setNewServiceInformationsToShown() {
try (Connection conn = DriverManager.getConnection("jdbc:h2:" + Main.config_db_location,
Main.config_db_username, Main.config_db_password)) {
//read data from database
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM BCSTASKS_SERVICE");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
if(rs.getString("Status").equals("Neu") && rs.getBoolean("wasShown") == false) {
rs.updateBoolean("WASSHOWN", true);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
The error message already suggests that I should use conn.createStatement and set the ResultSet to CONCUR_UPDATABLE. The error occurs at the line with rs.updateBoolean(...);
Error Message:
The result set is readonly. You may need to use conn.createStatement(.., ResultSet.CONCUR_UPDATABLE). [90140-210]
The problem is I don't know where and how I should use this method. In the same function or at the start of the program?
Most DB code I see doesn't attempt to use the fact that resultsets are updatable, and will instead fire off an additional UPDATE query, which works fine.
However, sure, H2 supports updateable resultsets too. However, some of the features that ResultSets have actually have quite a cost; the DB engine needs to do a boatload of additional bookkeeping to enable such features which have a performance cost. Lots of database queries are extremely performance sensitive, so by default you do not get the bookkeeping and therefore these features do not work. You need to enable them explicitly, that's what the error is telling you.
You're currently calling the 'wrong' preparedStatement method. You want the more extended one, where you pick and choose which additional bookkeeping you want H2 to do for you, in order to enable these things. You want this one.
conn.prepareStatement(
"SELECT * FROM BCSTASKS_SERVICE",
ResultSet.TYPE_SCROLL_INSENSITIVE, // [edited]
ResultSet.CONCUR_UPDATABLE);
That CONCUR_UPDATABLE thing is just a flag you pass to say: Please do the bookkeeping so that I can call .update.
[edited] This used to read 0 before, but as #MarkRotteveel pointed out, that's not valid according to the documentation.
You have to put update query for update data in database but you are going with select query that is the problem.
Select query is used if you have to fetch data from database.
Update query is used for update data in database where data already stored in database but you just overwrite data.
Here down is modified code:
public static void setNewServiceInformationsToShown() {
try (Connection conn = DriverManager.getConnection("jdbc:h2:" + Main.config_db_location,
Main.config_db_username, Main.config_db_password)) {
PreparedStatement stmt = conn.prepareStatement("UPDATE BCSTASKS_SERVICE SET wasShown = ? WHERE status = ? AND wasShown = ?");
stmt.setBoolean(1, true);
stmt.setString(2, "Neu");
stmt.setBoolean(3, false);
stmt.executeUpdate();
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
You need to create a separate query/prepareStatement for an update. In your case as far as I can see you need only one update query:
conn.prepareStatement("UPDATE BCSTASKS_SERVICE SET WASSHOWN=true where
Status = 'Neu' and wasShown = false "

Append data to a DB2 blob

In my DB2 database, I have a table with a Blob:
CREATE TABLE FILE_STORAGE (
FILE_STORAGE_ID integer,
DATA blob(2147483647),
CONSTRAINT PK_FILE_STORAGE PRIMARY KEY (FILE_STORAGE_ID));
Using the db2jcc JDBC driver (db2jcc4-9.7.jar), I can read and write data in this table without any problems.
Now I need to be able to append data to existing rows, but DB2 gives the cryptic error
Invalid operation: setBinaryStream is not allowed on a locator or a reference. ERRORCODE=-4474, SQLSTATE=null
I use the following code to append my data:
String selectQuery = "SELECT DATA FROM FILE_STORAGE WHERE FILE_STORAGE_ID = ?";
try (PreparedStatement ps = conn.prepareStatement(selectQuery, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)) {
ps.setInt(1, fileStorageID);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
Blob existing = rs.getBlob(1);
try {
// The following line throws the exception:
try (OutputStream output = existing.setBinaryStream(existing.length() + 1)) {
// append the new data to the output:
writeData(output);
} catch (IOException e) {
throw new IllegalStateException("Error writing output stream to blob", e);
}
rs.updateBlob(1, existing);
rs.updateRow();
} finally {
existing.free();
}
} else {
throw new IllegalStateException("No row found for file storage ID: " + fileStorageID);
}
}
}
My code is using the methods as suggested in OutputStream to the BLOB column of a DB2 database table. There also seem to be other people who have the same problem: Update lob columns using lob locator.
As a workaround, I currently read all the existing data into memory, append the new data in memory, and then write the complete data back into the blob. This works, but it's very slow and obviously it will take longer if there's more data in the blob, getting slower with each update.
I do need to use Java to update the data, but apart from switching away from the JVM, I am happy to try any possible alternatives at all, I just need to append the data somehow.
Thanks in advance for any ideas!
If you only need to append data to the end of a BLOB column and don't want to read the entire value into your program, a simple UPDATE statement will be faster and more straightforward.
Your Java program could run something like this via executeUpdate():
UPDATE file_storage SET data = data || BLOB(?) WHERE file_storage_id = ?
The parameter markers for this would be populated by setBlob(1, dataToAppend) and setInt(2, fileStorageID).

Oracle Open cursors ora-1000 errors, "Maximum open cursors exceeded." [duplicate]

This question already has answers here:
java.sql.SQLException: - ORA-01000: maximum open cursors exceeded
(14 answers)
Closed 9 years ago.
I've done connection to Oracle Database.
Now I'm facing
ORA-01000: maximum open cursors exceeded
I used code to insert data:
public static void OracleJDBC(String Serial_Number, String Charged_MDN) {
String dataInsertQuery = "insert into CDR_Huawei (Serial_Number, Charged_MDN) values ('" + Serial_Number + "', '" + Charged_MDN + "')";
String dataSelectQuery = "select * from CDR_Huawei";
Statement statement = null;
try {
statement = connection.createStatement();
statement.execute(dataInsertQuery);
//System.out.println("Data Inserted Successfully");
} catch (SQLException e) {
e.printStackTrace();
}
}
It works only for first 500 records, then I have the error Ora-1000.
I have about 6000 records in total.
I found some topics saying that configuration should be changed, but I can't change configuration.
Is there another way to solve this error?
Close your statement in a finally block.
try {
statement = connection.createStatement();
statement.execute(dataInsertQuery);
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (statement != null) statement.close();
}
Every time new statement object is generated while you write
statement = connection.createStatement()
It is good practice to close the statement after using it...
statement.close(); after `statement.execute(dataInsertQuery);`
will solve your problem.
An extra answer to draw attention to ppeterka's comment:
You really should be using PreparedStatements here. The reason is that you are currently sending 6000 unique SQL insert statements to the database. The statements are unique because the values of the insert statements are glued in your statement text. The database will have to parse each unique statement and place it in its shared pool for reuse. But it won't reuse, since they are unique.
Using PreparedStatements where you bind in the values, you'll create only one unique SQL insert statement, which only needs to parse once and won't clutter the shared pool. Your database administrator will thank you for it.

Java and MySQL Transactions

I have a java app that runs on multiple computers and they all connect to the same MySQL database. I need transactions to make sure database is updated correctly.
I have a problem with releasing locks. Here is the code:
public boolean bookSeats(int numeroSala, Calendar dataOra, List<Posto> posti) {
JDBConnection connection = JDBConnection.getDbConnection();
Connection conn = connection.conn;
java.sql.Date javaSqlDate = new java.sql.Date(dataOra.getTime().getTime());
java.sql.Time javaSqlTime = new java.sql.Time(dataOra.getTime().getTime());
try {
conn.setAutoCommit(false);
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
} catch (SQLException e) {
e.printStackTrace();
}
try
{
Posto p = null;
ListIterator<Posto> itr = posti.listIterator();
PreparedStatement stmt = null;
stmt = conn.prepareStatement(
"INSERT INTO sala?_tickets " +
"(giornoSpettacolo, orarioSpettacolo, fila, posto) " +
"VALUES (?, ?, ?, ?)");
stmt.setInt(1, numeroSala);
while(itr.hasNext())
{
p = itr.next();
stmt.setString(2, javaSqlDate.toString()); // giornoSpettacolo
stmt.setString(3, javaSqlTime.toString()); // orarioSpettacolo
stmt.setInt(4, p.getFila()); // fila
stmt.setInt(5, p.getNumero()); // posto
stmt.addBatch();
}
stmt.executeBatch();
conn.commit();
return true;
}
catch (SQLException e) {
e.printStackTrace();
}
return false;
}
This method works fine but when I try to execute the this on another computer at the same time, I get a DEADLOCK error even though the conn.commit() was completed.
As soon as I close the first application, the other application can run the method without Deadlock error. It seems like the COMMIT doesn't release the lock?
Per MySQL documentation, about SERIALIZABLE:
This level is like REPEATABLE READ, but InnoDB implicitly converts all
plain SELECT statements to SELECT ... LOCK IN SHARE MODE if autocommit
is disabled.
You might want to try re-enabled auto-commit after your commit, or try Connection. REPEATABLE_READ instead.
You can refer to this documentantion on how to do transactions with bare JDBC. Hope it helps.
You should always do a conn.close() in a finally block to ensure that any resources used by your method are released.
Yes, close the connection as the first answer says. However, in addition, if you can put the above code into an "syncronized" method, it will make sure only one thread can access it at a time.
Apart from that, just have a look at the following link, which discuss about deadlock handling in JDBC
Deadlock detection in Java

How to catch constraint violation inside the resultset loop?

I was working on a servlet that will generate a unique code and update that in a mySQL database.
Now, in that, I want to catch any exception thrown in case that unique code already exists in the mySQL table and generate a new code and try updating the database. The problem is I want to do this WITHIN the for loop itself. The code is as follows:
try
{
connection = datasource.getConnection();
SQLUpdate = "INSERT INTO Voucher_dump VALUES( '"+unique_code+"','08-10-2011 04:48:48','0')";
PreparedStatement ps1 = connection.prepareStatement(SQLUpdate);
ps1.executeUpdate();
ResultSet r = ps1.getResultSet(); // this is where I'm checking if it's a duplicate
if(r==null)
out.println("This is a duplicate");
else out.println("Updated");
trial12= "08-10-2011 04:48:480.03999855056924717a";
SQLUpdate = "INSERT INTO Voucher_dump VALUES( '"+trial12+"','08-10-2011 04:48:48','0')";
ps1 = connection.prepareStatement(SQLUpdate);
ps1.executeUpdate();
r = ps1.getResultSet();
if(r==null)
out.println("This is a duplicate");
else out.println("Updated");
}
catch (SQLException sqle)
{
sqle.printStackTrace();
}
I don't want to wait till the end of the entire loop to catch the SQLException (I have already defined this key in mySQL as primary). The moment, the result comes back as a duplicate entry, I want to re-generate this key and attempt the update again.My output for this particular code is coming blank on my output page (all other parameters are showing correctly). Neither is "This is a duplicate" displayed nor is "Updated". Maybe, ResultSet is not the best way to do it. Could you guys give me some advice on what would be the best way forward ?
Some advice in no particular order:
Close the connection in a finally block.
Close statements individually if you'll be creating many of them before closing the connection. ("Many" is defined by your DBAs.)
Format your code.
Don't use stdout and/or stderr from real code. Pick a logging framework.
Consider using some helper classes to simplify (and correct) your database access, like Spring's JdbcTemplate.
Make sure to include relevant context when you post example code.
Due to #6, I don't know what out is, but I suspect the reason you're not seeing anything is that you're inserting a duplicate value with the first statement, which will cause a SQLException from that line, not at getResultSet(), where you seem to expect it. Since the error is written to stdout, it'll show up in your server logs somewhere, but nothing will be written to out. I'm not sure why you think getResultSet() will return null or not null depending on whether there was a constraint violation. Take a look at the javadoc for that method.
Update: 7. As BalusC points out, never, ever concatenate a string directly into a JDBC Statment. Use PreparedStatment's placeholders and set* methods. For info on SQL injection, see Wikipedia and XKCD.
How about this code?
try {
Class.forName(driver).newInstance();
conn = DriverManager.getConnection(url + dbName);
System.out.println("Connected to the database");
int i = 1; //get the unique code
boolean isInserted = false;
while (!isInserted) {
try {
PreparedStatement preparedStatement = conn.prepareStatement("INSERT INTO test values (?)");
preparedStatement.setInt(1, i);
preparedStatement.executeUpdate();
isInserted = true;
} catch (com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException e) { //Catch the particular exception which throws error on unique constraint. This may depend on Java/MySQL your version
i++; //get the next unique code
}
}
System.out.println("Disconnected from database");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
conn.close();
} catch (Exception e) {
}
}

Categories