Postgresql transaction handling with java - java

I have two blocks of queries with preparedStatement.
This is the first:
String sql = "update cikan_malzeme set miktar = ? where proje_id = ? and malzeme_id = ?";
PreparedStatement prep = dbConnect.connection.prepareStatement(sql);
prep.setFloat(1, toplam);
prep.setInt(2, pid);
prep.setInt(3, mid);
prep.executeUpdate();
And this is the second:
String sql2 = "update malzemeler set miktar = ? where malz_adi = ?";
PreparedStatement prep2 = dbConnect.connection.prepareStatement(sql2);
prep2.setFloat(1, fark);
prep2.setString(2, malzemeadi);
prep2.executeUpdate();
Now I want to execute them with the transaction BEGIN; and COMMIT;
How can I handle transaction with preparedStatement?

You should use Connection.setAutoCommit(false) to disable auto-commit and Connection.commit() and Connection.rollback().
When auto-commit is disabled, a transaction will be started automatically the first time you execute a command or query that requires a transaction.
You should not be using the database specific transaction control commands, as the driver will most likely do additional cleanup of resources when a commit() or rollback() is issued.

Set auto commit to false.
Put your PreparedStatements in a try block. Commit at the end; rollback in the catch block.
That's how it's usually done in bare bones JDBC.
http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html
If you use EJB3 or Spring you can add a transaction manager and specify them declaratively. That's more sophisticated and flexible.

Related

JDBC Transaction control in Sybase

Need help in JDBC transaction control mechanism in JAVA.
Issue:
There are certain stored procedures in our Sybase DB that needs to be run on Unchained mode. Since we are updating our data on two different databases (unfortunately, both Sybase) we need to be able to rollback all our previous transactions, if there is any failure.
But running with Unchained Mode (Auto commit - on) is not helping us with the rollbacks as some of the SPs have already committed the transactions.
Connection connection = getConnection();
PreparedStatement ps = null;
try{
String sql = getQuery(); // SQL Chained Mode
ps = connection.prepareStatement(sql);
ps.executeUpdate(); //Step 1
.
.
sql = getTransctionQuery(); // SQL Unchained Mode
connection.setAutoCommit(true); //Step 2
ps = connection.prepareStatement(sql);
ps.executeUpdate();
connection.setAutoCommit(false);
.
.
sql = getQuery(); // SQL Chained Mode
ps = connection.prepareStatement(sql);
ps.executeUpdate(); //Step 3 This step fails.
connection.commit();
}catch(){
connection.rollback(); //Doesn’t rollback step 1 and understandably step 2.
}
finally{
connection.close(); //cleanup code
}
We would ideally like to rollback both step 1 and step 2 effectively if 3 fails.
Current Solution:
Our idea is to reinvent the wheel and write our own version of rollback (by deleting inserted records and reverting the updated values, from Java).
Need effective solution
Since this solution is effort intensive and not fool proof we would like to know if there are any other better solutions.
Thanks
You need to perform an explicit BEGIN TRANSACTION statement. Otherwise, every DML is a transaction by itself which you cannot control. Obviously autocommit must be off as well.

neo4j insert using jdbc but cannot query immediately within the same connection

question background:
1.database is neo4j 2.3.1, driver using jdbc;
2.db connection initialized as a class member, default is auto-commit(not changed);
To avoid insert duplicates, i query before insert. after program stopped, found duplicates. why?
code:
String query = "CREATE (n:LABEL {name:'jack'})";
System.out.println(query);
Statement stmt = dbConnection.createStatement();
stmt.executeUpdate(query);
stmt.close();
Use MERGE + unique constraints instead
How do you "check"
You would have to check in the same tx and also take a write lock
after debugging i found that for neo4j-jdbc(v2.1.4), the default db connection transaction level is TRANSACTION_NONE, then i set it to TRANSACTION_READ_COMMITTED, above issue disappeared. so i think that TRANSACTION_READ_COMMITTED will force the previous insert committed, though this is not the recommended way. for isolation level refer to:Difference between read commit and repeatable read

do I need a connection.commit() after executeBatch()?

I have to check the code of a fellow coworker and I stumble on this piece of code:
private void pdate(JdbcTemplate jdbcTemplate, List<Long> saisineIdsToUpdate,Connection connection) throws SQLException {
String sqlUpdate = "UPDATE SAISINES SAI WHERE SAI.IDSAISINE = ?"; //request simplified
PreparedStatement psUpdate = connection.prepareStatement(sqlUpdate);
for (Long saisineId : saisineIdsToUpdate) {
psUpdate.setLong(1, saisineId );
psUpdate.addBatch();
}
psUpdate.executeBatch();
psUpdate.close();
The code works, the updates are done correctly, but I cannot find the trace of a connection.commit();
I wonder how it can work without the commit - could someone explain why ?
As explained here, JDBC-drivers commonly use autocommit, you can enable database-traces via DBMS-driver specific settings like showSQL or generateDDL in JPA.
To enable manual- transaction support instead of the auto-commit mode
that the JDBC driver uses by default, use the Connection object's
setAutoCommit() method. If you pass a boolean false to setAutoCommit(
), you turn off auto-commit. You can pass a boolean true to turn it
back on again.
if you set auto-commit on your connection object to false then we have to commit the transaction manually
connection.setAutoCommit(false);
// your code goes here
connection.commit();
if you don't set auto-commit then default its value is true and it will commit each record

JDBC and Concurrency issues

I want some advice on some concurrency issues regarding jdbc, i basically need to update a value and then retrieve that value using a update then a select, I'm assuming by turning auto commit off no other transaction can access this table, hence other transactions won't be able to perform update and select queries until this has been committed.
Below is some example code. Do you think this will work and does any one else have a better solution to implementing this?
int newVal=-1;
con.setAutoCommit(false);
PreparedStatement statement = con.prepareStatement("UPDATE atable SET val=val+1 WHERE id=?");
statement.setInt(1, id);
int result = statement.executeUpdate();
if (result != 1) {
throw new SQLException("Nothing updated");
} else {
statement = con.prepareStatement("SELECT val FROM atable WHERE id=?");
statement.setInt(1, id);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
newVal = resultSet.getInt("val");
}
}
statement.close();
con.commit();
con.setAutoCommit(true);
Thanks.
Assuming you use some form of data source, you may configure there if you want transactionality and the isolation level. But to be explicit:
try(Connection con = ds.getConnection()){
con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
con.setAutoCommit(false);
//...
} catch(SQLException sqle) {
throw new MyModelException(e)
}
Now, you could trigger pesimistic locking by updating a version (or timestamp) field in your table. This will trigger a lock in the database (most likely at the record level):
try(PreparedStatement pStm = con.prepareStatement("update atable set version=version+1")){
pStm.executeUpdate();
}
At this point, if another user is trying to update the same record simultaneously, this connection will either wait or timeout, so you must be ready for both things. The record will not be unlocked until your transaction ends (commit or rollback).
Then, you can safely select and update whatever you want and be sure that nobody else is touching your record as you process your data. If anybody else tries they will be put on wait until you finish (or they will timeout depending on connection configuration).
Alternatively you could use optimistic locking. In this case you read your record, do modifications to it, but in the update you make sure nobody else has changed it since you read it by checking that the version/timestamp field is the same as the one you orginally read. In this case you must be prepared to retry a transaction (or abort it alltogether) if you realize you have stale/outdated data.
i.e. update atable set afield=? where id=? and version=1
If the number of rows affected is 0, then you know that is probable that the record was updated between your read and your update and the record is no longer in version 1.
Setting autocommit=false on your connection will not prevent other connections/threads from changing the row in the database! It will only disable automatic commits after each JDBC operation on that specific connection.
You will need to lock the row, eg. with select ... for update to prevent other transactions against the row, and also you will need to do your selects and updates within a single transaction.
Cheers,

JPA and MySQL transaction isolation level

I have a native query that does a batch insert into a MySQL database:
String sql = "insert into t1 (a, b) select x, y from t2 where x = 'foo'";
EntityTransaction tx = entityManager.getTransaction();
try {
tx.begin();
int rowCount = entityManager.createNativeQuery(sql).executeUpdate();
tx.commit();
return rowCount;
}
catch(Exception ex) {
tx.rollback();
log.error(...);
}
This query causes a deadlock: while it reads from t2 with insert .. select, another process tries to insert a row into t2.
I don't care about the consistency of reads from t2 when doing an insert .. select and want to set the transaction isolation level to READ_UNCOMMITTED.
How do I go about setting it in JPA?
Update
So I ended up creating a regular SQL connection for this case as it seemed to me the simplest option. Thanks everyone!
You need to set it at the connection level, get the session from the entitymanager and do this:
org.hibernate.Session session = (Session)entityManager.getDelegate();
Connection connection = session.connection();
connection.setTransactionIsolation(Connection.READ_UNCOMMITTED);
In JPA you don't. JDO is the only standard that supports setting txn isolation. Obviously going for particular implementations methods can allow it, but then you become non-portable
Since you are using BMT, you can do the following using a datasource to get the connection.
and set the iso. level.
DataSource source = (javax.sql.DataSource) jndiCntxt.lookup("java:comp/env/jdbc/myds");
Connection con = source.getConnection( );
con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

Categories