Saturday, May 2, 2026

Transaction Management in Java-JDBC

Efficient Transaction Management in Java is critical for building reliable, enterprise‑grade applications. JDBC provides powerful APIs to start, commit, roll back, and even set savepoints within transactions, ensuring your database always remains consistent.


What is a transaction

A transaction is a single logical unit of work. To complete a transaction all of the actions within that unit of work must complete. If any action with in the unit of work fails, all of the work done as part of the transaction has to be undone (entire transaction roll back), restoring the database to its previous state.

Example– A classic example for explaining transaction is to consider transferring money from one account to another. While transferring amount, actions are as follows-

  1. Amount is deducted from account A.
  2. Amount is added to account B.

As part of a transaction both of these actions should happen or none.

If first action happens and amount is deducted from account A, then some error happens and that amount is not added to the account B then your DB is left in an inconsistent state.

If both actions happen as a single unit of work in a transaction then any error after the first step will result in rollback of the entire action. Which means the action where amount was deducted from the account A is undone and DB returns to its state before the transaction begin.

Transaction Management in JDBC

By default, a JDBC connection operates in auto‑commit mode, meaning each SQL statement is treated as an independent transaction and committed immediately after it's execution completes.

If you want to run two or more statements grouped into a transaction in JDBC then you need to disable the auto-commit mode. You can do it using the setAutoCommit() method.

connection.setAutoCommit(false);

By setting the auto-commit as false you are ensuring that the statements are not automatically committed after execution. You can commit it once all the statements grouped into a transaction are completed without any error.

In order to commit the transaction you will have to explicitly call the commit method.

connection.commit();

The commit method call makes all changes, made as part of the transaction, permanent and releases any database locks held by this Connection object.

Transaction Rollback in JDBC

In robust Transaction Management in Java, handling failures gracefully is just as important as committing successful operations. If any action within a transaction fails, you must roll back the entire transaction to maintain database consistency. This is achieved using the rollback() method.

connection.rollback();

Calling rollback() will undo all changes made during the current transaction and releases any database locks currently held by this Connection object.

To sum it up following steps are to be followed for transaction management in JDBC-

  1. Disable Auto‑Commit- Set auto-commit mode to false for your connection object.
  2. Commit on Success- Once all the actions with in the transaction complete with out any error, make changes permanent by calling the commit method.
  3. Rollback on Failure- If any error occurs, rollback the transaction by calling the rollback() method.

JDBC transaction management example

Let’s see an example of transaction management in JDBC. We’ll use the same example of transferring amount from one account to another.

DB used is MySQL, schema is netjs and there is a table account which has fields acct_num and amount which will be used in SQL queries.

In the code there are two methods deductAmount() and addAmount() which are used to deduct amount form the account and add the same amount to another account respectively. These two methods are grouped into a transaction.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TransactionDemo {

 public static void main(String[] args) {
  TransactionDemo td = new TransactionDemo();
  td.transferAmount(1, 7, 100);
 }
 
 /**
  * 
  * @param fromAcct
  * @param toAcct
  * @param amount
  */
 public void transferAmount(int fromAcct, int toAcct, int amount){
  Connection connection = null;
  try {
   connection = getConnection();
   // Disabling auto commit
   connection.setAutoCommit(false);
   /** Transaction starts **/
   deductAmount(connection, fromAcct, amount);
   addAmount(connection, toAcct, amount);
   /** Transaction ends **/
   // Commit transaction
   connection.commit();
  }catch (SQLException e) {
     e.printStackTrace();
     if(connection != null){
       try {
         System.out.println("Rollingback the transaction");
         connection.rollback();
       } catch (SQLException e1) {
         // TODO Auto-generated catch block
         e1.printStackTrace();
       }
     }
      
  }finally{
       if(connection != null){
        //closing connection 
        try {
          connection.close();
        } catch (SQLException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
        }
      } // if condition
  }// finally
 }
 
 private void deductAmount(Connection connection, int acctNum, int amount) 
    throws SQLException{

  
  String updateSQL = "update account as t1 JOIN "
    + " (select acct_num, (balance - ?) as bal from account"
    + " where acct_num = ?) As acct "
    + " ON  t1.acct_num = acct.acct_num"
    + " set t1.balance = acct.bal"
    + " where t1.acct_num = ?";

  PreparedStatement prepStmt = null;
  try {
   prepStmt = connection.prepareStatement(updateSQL);
   prepStmt.setInt(1, amount);
   prepStmt.setInt(2, acctNum);
   prepStmt.setInt(3, acctNum);
   int count = prepStmt.executeUpdate();
   System.out.println("Count of rows updated " + count);
   if(count == 0){
    throw new SQLException("Account num not found " + acctNum);
   }
  }finally{
    if(prepStmt != null){
     prepStmt.close();
    }
  }
 }
 
 private void addAmount(Connection connection, int acctNum, int amount) throws SQLException{
  String updateSQL = "update account as t1 JOIN "
    + " (select acct_num, (balance + ?) as bal from account"
    + " where acct_num = ?) As acct "
    + " ON  t1.acct_num = acct.acct_num"
    + " set t1.balance = acct.bal"
    + " where t1.acct_num = ?";

  PreparedStatement prepStmt = null;
  try {
   prepStmt = connection.prepareStatement(updateSQL);
   prepStmt.setInt(1, amount);
   prepStmt.setInt(2, acctNum);
   prepStmt.setInt(3, acctNum);
   int count = prepStmt.executeUpdate();
   System.out.println("Count of rows updated " + count);
   if(count == 0){
    throw new SQLException("Account num not found " + acctNum);
   }
  }finally{
    if(prepStmt != null){
     prepStmt.close();
    }
  }
  
 }
 
 /**
  * Method for getting the connection
  * @return
  */
 public static Connection getConnection(){
  Connection connection = null;
   try {
    // Loading driver
    Class.forName("com.mysql.jdbc.Driver");
    
    // Creating connection
    connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/netjs", 
                 "root", "admin");
    
   } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
   } catch (SQLException e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
   }
  return connection;
 }

}

Transaction Isolation Levels in JDBC

Connection interface provides a set of fields for specifying transaction isolation level-
  • TRANSACTION_NONE- A constant indicating that transactions are not supported.
  • TRANSACTION_READ_COMMITTED- A constant indicating that dirty reads are prevented; non-repeatable reads and phantom reads can occur.
  • TRANSACTION_READ_UNCOMMITTED- A constant indicating that dirty reads, non-repeatable reads and phantom reads can occur.
  • TRANSACTION_REPEATABLE_READ- A constant indicating that dirty reads and non-repeatable reads are prevented; phantom reads can occur.
  • TRANSACTION_SERIALIZABLE- A constant indicating that dirty reads, non-repeatable reads and phantom reads are prevented.

Connection interface also provides a method setTransactionIsolation(int level) for setting the transaction isolation level.

As example – If you want to set the transaction isolation level to read committed.

connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

Setting Savepoint for transaction rollback

You can also set a save point in your transaction and rollback to that savepoint rather than rolling back the whole transaction. There is an overloaded variant of rollback method that takes a Savepoint argument.

Connection interface provides two overloaded methods for setting savepoint -

  • setSavepoint()- Creates an unnamed savepoint in the current transaction and returns the created Savepoint object.
  • setSavepoint(String name)- Creates a savepoint with the given name in the current transaction and returns the created Savepoint object.

If you want to release the created savepoint you can do it using the releaseSavepoint() method. As example to release the created Savepoint sp1.

connection.releaseSavepoint(sp1)

JDBC transaction - Setting savepoint example

If we take the same example as used above for transaction and add one more functionality to add the transaction information to a new table transdata (From Account, To Account, Amount).

A savepoint is created after the transfer is done and then transaction information is inserted in the table transdata. In case there is some error while inserting record in transdata you just want to log that information and rollback to the savepoint created after the transfer is done rather than rollingback the whole transaction.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;

public class TransactionDemo {
  public static void main(String[] args) {
    TransactionDemo td = new TransactionDemo();
    td.transferAmount(2, 3, 100);
  }
 
  /**
  * @param fromAcct
  * @param toAcct
  * @param amount
  */
  public void transferAmount(int fromAcct, int toAcct, int amount){
    Connection connection = null;
    Savepoint sp1 = null;
    try {
      connection = getConnection();
      // Disabling auto commit
     connection.setAutoCommit(false);
     /** Transaction starts **/
     deductAmount(connection, fromAcct, amount);
     addAmount(connection, toAcct, amount);
     /** Transaction ends **/
     // setting save point here
     sp1 = connection.setSavepoint("TrasferDoneSavePoint");
     // inserting transaction data
     insertTransaction(connection, fromAcct, toAcct, amount);
     // Commit transaction
     connection.commit();      
    }catch (SQLException e) {
      e.printStackTrace();
      if(connection != null){
        try {
          // if savepoint is not reached, rollback the transaction
          if(sp1 == null){
            System.out.println("Rollingback the transaction");
            connection.rollback();
          }else{
            System.out.println("Rollingback to savepoint");
            // rollback to savepoint
            connection.rollback(sp1);
            // Commit till the savepoint
            connection.commit();
          }
        } catch (SQLException e1) {
          // TODO Auto-generated catch block
          e1.printStackTrace();
        }
      }  
    }finally{
      if(connection != null){
        //closing connection 
        try {
          connection.close();
        } catch (SQLException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      } // if condition
    }// finally
  }
 
  /**
  * @param connection
  * @param acctNum
  * @param amount
  * @throws SQLException
  */
  private void deductAmount(Connection connection, int acctNum, int amount) throws SQLException{
    String updateSQL = "update account as t1 JOIN "
      + " (select acct_num, (balance - ?) as bal from account"
      + " where acct_num = ?) As acct "
      + " ON  t1.acct_num = acct.acct_num"
      + " set t1.balance = acct.bal"
      + " where t1.acct_num = ?";

    PreparedStatement prepStmt = null;
    try {
      prepStmt = connection.prepareStatement(updateSQL);
      prepStmt.setInt(1, amount);
      prepStmt.setInt(2, acctNum);
      prepStmt.setInt(3, acctNum);
      int count = prepStmt.executeUpdate();
      System.out.println("Count of rows updated " + count);
      if(count == 0){
        throw new SQLException("Account num not found " + acctNum);
      }
    }finally{
      if(prepStmt != null){
        prepStmt.close();
      }
    }
  }
 
 /**
  * 
  * @param connection
  * @param acctNum
  * @param amount
  * @throws SQLException
  */
 private void addAmount(Connection connection, int acctNum, int amount) throws SQLException{
  String updateSQL = "update account as t1 JOIN "
    + " (select acct_num, (balance + ?) as bal from account"
    + " where acct_num = ?) As acct "
    + " ON  t1.acct_num = acct.acct_num"
    + " set t1.balance = acct.bal"
    + " where t1.acct_num = ?";

  PreparedStatement prepStmt = null;
  try {
   prepStmt = connection.prepareStatement(updateSQL);
   connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
   prepStmt.setInt(1, amount);
   prepStmt.setInt(2, acctNum);
   prepStmt.setInt(3, acctNum);
   int count = prepStmt.executeUpdate();
   System.out.println("Count of rows updated " + count);
   if(count == 0){
    throw new SQLException("Account num not found " + acctNum);
   }
  }finally{
    if(prepStmt != null){
     prepStmt.close();
    }
  }  
 }
 
 /**
  * 
  * @param connection
  * @param fromAcct
  * @param toAcct
  * @param amount
  * @throws SQLException
  */
 private void insertTransaction(Connection connection, int fromAcct, int toAcct, int amount) throws SQLException{
  String insertSQL = "Insert into transdata (from_acct, to_acct, amount) values (?, ?, ?)";
  PreparedStatement prepStmt = null;
  try {
   prepStmt = connection.prepareStatement(insertSQL);
   prepStmt.setInt(1, fromAcct);
   //prepStmt.setInt(2, toAcct);
   //prepStmt.setInt(3, amount);
   int count = prepStmt.executeUpdate();
   System.out.println("Count of rows inserted " + count);
   if(count == 0){
    throw new SQLException("Problem in inserting - " + " From Account: " + fromAcct + " To Account: "  +  toAcct) ;
   }
  }finally{
    if(prepStmt != null){
     prepStmt.close();
    }
  }
 }
 
  /**
  * Method for getting the connection
  * @return
  */
  public static Connection getConnection(){
    Connection connection = null;
    try {
      // Loading driver
      Class.forName("com.mysql.jdbc.Driver");
    
      // Creating connection
      connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/netjs", "root", "admin");    
    } catch (ClassNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (SQLException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return connection;
  }
}

That's all for this topic Transaction Management in Java-JDBC. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. CallableStatement Interface in Java-JDBC
  2. DataSource in Java-JDBC
  3. ResultSet Interface in Java-JDBC
  4. Java Program to Get All DB Schemas
  5. Data access in Spring framework

You may also like-

  1. How HashMap Internally Works in Java
  2. Difference Between Comparable and Comparator in Java
  3. AtomicInteger in Java With Examples
  4. Bounded type parameter in Java generics
  5. Functional Interfaces in Java
  6. Nested class and Inner class in Java
  7. Java Multithreading Interview Questions And Answers
  8. Java split() Method - Splitting a String