33

Currently I'm setting autocommit to false in spring through adding a property to a datasource bean id like below :

   <property name="defaultAutoCommit" value="false" /> 

But i need to add it specifically in a single java method before executing my procedure. I used the below code snippet.

  getJdbcTemplate().getDataSource().getConnection().setAutoCommit(false);

But the above line was not setting autocommit to false?
Am i missing anything ?
or any alternative to set autocommit in a specific java method by spring

Thanks

bad programmer
  • 331
  • 1
  • 3
  • 4
  • possible duplicate of (http://stackoverflow.com/questions/10746377/spring-3-1-jdbctemplate-auto-commit-to-false) – sTg Aug 08 '14 at 07:17
  • I know that it can be set autocommit to false by the property tag to the datasource bean id.. But i dont want to set it in that way. I want to set autocommit false in a java database business logic method – bad programmer Aug 08 '14 at 07:34
  • @shirish : the link provided by you is telling what i already did..! – bad programmer Aug 08 '14 at 07:35
  • Why? If you use a transaction manager auto commit is disabled by default. Your approach won't work as it will give you a unmanaged connection (if you run this x times where x is the number of connections in your pool your pool will be depleted). – M. Deinum Aug 08 '14 at 08:15
  • @M.Denium. Could you help me in how can i do that and what are the settings to use a transaction manager – bad programmer Aug 08 '14 at 11:40

8 Answers8

13

The problem is that you are setting autocommit on a Connection, but JdbcTemplate doesn't remember that Connection; instead, it gets a new Connection for each operation, and that might or might not be the same Connection instance, depending on your DataSource implementation. Since defaultAutoCommit is not a property on DataSource, you have two options:

  1. Assuming your concrete datasource has a setter for defaultAutoCommit (for example, org.apache.commons.dbcp.BasicDataSource), cast the DataSource to your concrete implementation. Of course this means that you can no longer change your DataSource in your Spring configuration, which defeats the purpose of dependency injection.

((BasicDataSource)getJdbcTemplate().getDataSource()).setDefaultAutoCommit(false);

  1. Set the DataSource to a wrapper implementation that sets AutoCommit to false each time you fetch a connection.

    final DataSource ds = getJdbcTemplate().getDataSource();
    getJdbcTemplate().setDataSource(new DataSource(){
      // You'll need to implement all the methods, simply delegating to ds
    
      @Override
      public Connection getConnection() throws SQLException {
        Connection c = ds.getConnection();
        c.setAutoCommit(false);
        return c;
      }
    });
    
Yosef Weiner
  • 5,432
  • 1
  • 24
  • 37
10

I'm posting this because I was looking for it everywhere: I used configuration property in Spring boot to achieve setting the default autocommit mode with:

spring.datasource.hikari.auto-commit: false

Spring Boot 2.4.x Doc for Hikari

milchreis
  • 79
  • 7
moffeltje
  • 4,521
  • 4
  • 33
  • 57
8

You need to get the current connection. e.g.

Connection conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
    try {
        conn.setAutoCommit(false);

        /**
         * Your Code
         */
        conn.commit();
    } catch (SQLException e) {
        conn.rollback();
        e.printStackTrace();
    }
Sujan
  • 81
  • 1
  • 1
8

after 5 years still a valid question, i resolved my issue in this way :

  1. set a connection with connection.setAutoCommit(false);
  2. create a jbc template with that connection;
  3. do your work and commit.
    Connection connection = dataSource.getConnection();
    connection.setAutoCommit(false);
    JdbcTemplate jdbcTemplate = 
    new  JdbcTemplate(newSingleConnectionDataSource(connection, true));
    // ignore case in mapping result
    jdbcTemplate.setResultsMapCaseInsensitive(true);
    // do your stuff
    connection.commit();
solocoding
  • 81
  • 1
  • 3
6

You will have to do for each statement that the jdbcTemplate executes. Because for each jdbcTemplate.execute() etc it gets a new connection from the Datasource's connection pool. So you will have to set it for the connection that the connection the jdbcTemplate uses for that query. So you will have to do something like

 jdbcTemplate.execute("<your sql query", new PreparedStatementCallback<Integer>(){

        @Override
        public  Integer doInPreparedStatement(PreparedStatement stmt) throws SQLException, DataAccessException 
        {
            Connection cxn = stmt.getConnection();
            // set autocommit for that cxn object to false
            cxn.setAutoCommit(false);
            // set parameters etc in the stmt
            ....
            ....
            cxn.commit();
            // restore autocommit to true for that cxn object. because if the same object is obtained from the CxnPool later, autocommit will be false
            cxn.setAutoCommit(true);
            return 0;

        }
    });

Hope this helps

Prabhu R
  • 13,836
  • 21
  • 78
  • 112
  • what if `commit()` throws an exception and `cxn.setAutoCommit(true)` doesn't execute? – broc.seib May 07 '18 at 18:39
  • 1
    @broc.seib fair point, the cxn.setAutoCommit(true) should have been in the finally block. But I hope the answer illustrates how autocommit can be set to false in the JDBCTemplate – Prabhu R May 31 '18 at 11:18
4

I just came across this and thought the solution would help someone even if it's too late.

As Yosef said, the connection that you get by calling getJdbcTemplate().getDataSource().getConnection() method may or may not be the one used for the communication with database for your operation.

Instead, if your requirement is to just test your script, not to commit the data, you can have a Apache Commons DBCP datasource with auto commit set to fault. The bean definition is given below:

/**
 * A datasource with auto commit set to false.
 */
@Bean
public DataSource dbcpDataSource() throws Exception {
    BasicDataSource ds = new BasicDataSource();
    ds.setUrl(url);
    ds.setUsername(username);
    ds.setPassword(password);
    ds.setDefaultAutoCommit(false);
    ds.setEnableAutoCommitOnReturn(false);
    return ds;
}

// Create either JdbcTemplate or NamedParameterJdbcTemplate as per your needs
@Bean
public NamedParameterJdbcTemplate dbcpNamedParameterJdbcTemplate() throws Exception {
    return new NamedParameterJdbcTemplate(dbcpDataSource());
}

And use this datasource for any such operations.

If you wish to commit your transactions, I suggest you to have one more bean of the datasource with auto commit set to true which is the default behavior.

Hope it helps someone!

Nagendra Varma
  • 2,215
  • 3
  • 17
  • 26
2

I needed it to do some unit testing In fact Spring already provides the SingleConnectionDataSource implementation with the setAutoCommit method

// import org.springframework.jdbc.datasource.SingleConnectionDataSource;
SingleConnectionDataSource dataSource = new SingleConnectionDataSource();
dataSourceRX71.setAutoCommit(false);
dataSourceRX71.setDriverClassName("xxx");
dataSourceRX71.setUrl("xxx");
dataSourceRX71.setUsername("xxx");
dataSourceRX71.setPassword("xxx");
Dharman
  • 30,962
  • 25
  • 85
  • 135
ulk200
  • 369
  • 3
  • 5
0

In some case you could just add @Transactional in the method, e.g. After some batch insert, execute commit at last.

zhuguowei
  • 8,401
  • 16
  • 70
  • 106