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

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

Related

Why connection still commits automatically after putting setAutoCommit(false)?

I'm using a MySQL database.
I have a code like this :
public class Test {
public static void main(String[] args) throws SQLException {
String url = "....";
String username = "...";
String password = "...";
DriverManager.registerDriver(new Driver());
Connection connection = DriverManager.getConnection(url, username, password);
connection.setAutoCommit(false);
Statement stmt = connection.createStatement();
stmt.execute("create table if not exists customer1\r\n" + "(\r\n"
+ "customer_name varchar(20) not null primary key,\r\n" + "customer_street varchar(20),\r\n"
+ "customer_city varchar(10)\r\n" + ")");
// connection.commit();
connection.close();
}
}
Problem: When I execute this, it creates the table and commits it automatically but it should not.
I did connection.setAutoCommit(false) and commented out connection.commit() for testing, then why it is committing ?
This question (jdbc autocommit(false) doesnt work) didn't help.
Problem: When I execute this, it creates the table and commits it
automatically but it should not.
Right, any DDL will always be committed regardless of autocommit setting.
This behavior is not specific to MySQL, see Autocommit.
Most DBMS (e.g. MariaDB) force autocommit for every DDL statement,
even in non-autocommit mode. In this case, before each DDL statement,
previous DML statements in transaction are autocommitted. Each DDL
statement is executed in its own new autocommit transaction.
Maybe TEMPORARY TABLE might help you.
You can use the TEMPORARY keyword when creating a table. A TEMPORARY
table is visible only within the current session, and is dropped
automatically when the session is closed. This means that two
different sessions can use the same temporary table name without
conflicting with each other or with an existing non-TEMPORARY table of
the same name. (The existing table is hidden until the temporary table
is dropped.)
MySQL DDL, i.e. creating table isn't transactional
ref transactions that cause implicit commit
Data Manipulation statements are transactional, not Data definition statements.
so create/alter table or drop table are still committed.

Statement - Connection.setAutoCommit(false);

If I have a Connection con open and a Statment stat what does the stat.executeBatch()?
I think it does nothing because I have set autocommit to false.
Here an example:
stat.addBatch("update bankaccount set balance = balance + 100 where customer = 'Bill'");
stat.addBatch("update bankaccount set balance = balance - 100 where customer = 'Joe'");
stat.executeBatch();
con.commit();
Batch Processing allows you to group related SQL statements into a batch and submit them with one call to the database.
When you send several SQL statements to the database at once, you reduce the amount of communication overhead, thereby improving performance.
The addBatch() method of Statement, PreparedStatement, and CallableStatement is used to add individual statements to the batch.
The executeBatch() is used to start the execution of all the statements grouped together.
Also,
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.

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

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,

Postgresql transaction handling with 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.

Categories