javajdbctransactions

Alternative to connection.setAutoCommit(false) since it doesn't work for JdbcTemplate


I am going through some tutorials that says to start transaction you have to write connection.setAutoCommit(false) But it seems that this is ignored by JdbcTemplate that I am using to actually insert records. Can some share a Transaction code that works with JdbcTemplate but that doesn't include @Transactional because my goal is to try to simulate @Transactional to better understand what it does.

So in the below Code I get connection from jdbcTeamplate and use it to start Transaction but it is ignored when inserting Records with jdbcTeamplate inside Repository.

PersonService.java

@Service
public class PersonService {

  //PROPERTIES
  @Autowired private PersonRepository personRepository;
  @Autowired private JdbcTemplate     jdbcTemplate;

  //=========================================================================================================
  // INSERT RECORDS
  //=========================================================================================================
  public void insertRecords() throws SQLException {

    //GET DB CONNECTION
    Connection connection = jdbcTemplate.getDataSource().getConnection();

    //FIRST RECORD IS ALWAYS INSERTED (It is outside the Transaction)
    Person person0 = new Person(0, "Person Outside Transaction", 100);
    personRepository.save(person0);

    //TRANSACTION
    try { //It doesn't work with try(connection) => throws java.sql.SQLRecoverableException

      //START TRANSACTION
      connection.setAutoCommit(false);

      //EXECUTE SQL STATEMENTS
      for (int i = 1; i <= 2; i++) {
        if(i==2) { throw new Exception("Exception"); }
        Person person = new Person(0, "Person " + i, 10 * i);
        personRepository.save(person);
      }

      //COMMIT TRANSACTION
      connection.commit();

    } catch (Exception e) {
      //ROLLBACK TRANSACTION
      connection.rollback();   //throws java.sql.SQLRecoverableException
    }
    finally {
      connection.close();
    }

  }

}

PersonRepository.java

@Repository
public class PersonRepository {

  //PROPERTIES
  @Autowired private JdbcTemplate jdbcTemplate;

  //=========================================================================================================
  // INSERT
  //=========================================================================================================
  public void save(Person person) throws SQLException {
    jdbcTemplate.update(
      " INSERT INTO PERSON(NAME, AGE) VALUES(?, ?)"
      , new Object[] { person.getName(), person.getAge() }
    );
  }

}

Solution

  • I don't think this is something that can be done in the way that you want.

    if you look at how the JdbcTemplate works, the update eventually lands in to the execute method which obtains the database connection using the DataSourceUtils - which uses the TransactionSynchronizationManager. Check it out yourself here:

    https://github.com/spring-projects/spring-framework/blob/775a23b39d6332c7e85a840e3452b6d86d0ad5c0/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java#L1161

    https://github.com/spring-projects/spring-framework/blob/775a23b39d6332c7e85a840e3452b6d86d0ad5c0/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DataSourceUtils.java#L106C51-L106C84

    If you really want to understand what @Transactional is doing - you can dig into the github and follow it. Maybe setup breakpoints and step through it. My suggestion would be to read some tutorials and trust that the framework is doing what it claims.