Альтернатива Connection.setAutoCommit(false), поскольку она не работает для JdbcTemplate

Я просматриваю несколько руководств, в которых говорится, что для начала транзакции вам нужно написать Connection.setAutoCommit(ложь) Но кажется, что это игнорируется JdbcTemplate, который я использую для вставки записей. Может ли кто-нибудь поделиться кодом транзакции, который работает с JdbcTemplate, но не включает @Transactional, потому что моя цель — попытаться смоделировать @Transactional, чтобы лучше понять, что он делает.

Итак, в приведенном ниже коде я получаю соединение от jdbcTeamplate и использую его для запуска транзакции, но оно игнорируется при вставке записей с помощью jdbcTeamplate внутри репозитория.

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() }
    );
  }

}
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
97
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

До сих пор мне удавалось заставить это работать вот так. Но это выглядит не слишком хорошо. Любые другие решения?

PersonRepository2.java

@Repository
public class PersonRepository2 {

  //=========================================================================================================
  // INSERT
  //=========================================================================================================
  public void save(Connection connection, Person person) throws SQLException {

    JdbcTemplate jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(connection, true));

    jdbcTemplate.update(
      " INSERT INTO PERSON(NAME, AGE) VALUES(?, ?)"
      , new Object[] { person.getName(), person.getAge() }
    );

  }

}

Мне также удалось заставить его работать с изменениями в DatabaseConfig, вернув SingleConnectionDataSource, но теперь это означает, что я не использую пул соединений?

База данныхConfig.java

@Configuration
@ComponentScan("com")
public class DatabaseConfig {

    //@Bean
    //public DataSource dataSource() {
    //    return new DriverManagerDataSource("jdbc:oracle:thin:@localhost:1522/orcl", "TEST", "LETMEIN");
    //}

    @Bean
    public DataSource dataSource() {
      SingleConnectionDataSource  dataSource = new SingleConnectionDataSource();
                                  dataSource.setUrl     ("jdbc:oracle:thin:@localhost:1522/orcl");
                                  dataSource.setUsername("TEST");
                                  dataSource.setPassword("LETMEIN");
                                //dataSource.setAutoCommit(false);
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }

}

Или создайте этот SingleConnectionDataSource внутри Service.java, а затем передайте его в репозиторий. Хорошо, но по сути все три решения — это всего лишь одно решение с использованием SingleConnectionDataSource. И поэтому JdbcTemplate по умолчанию не использует пул соединений, который, как я полагаю, используется по умолчанию.

PersonService3.java

@Service
public class PersonService3 {

  //PROPERTIES
  @Autowired private PersonRepository3 personRepository;
  @Autowired private DataSource        dataSource;

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

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

    //WE ARE CREATING IT BY PROVIDING THE SAME CONNECTION
    JdbcTemplate jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(connection, true));

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

Вы тут много чего смешиваете, поэтому непонятно, почему не работает и что происходит.

Прежде всего, если вы хотите попробовать такие низкоуровневые вещи, старайтесь избегать использования репозитория, созданного Spring. Внутри происходит много всего, и они будут вас сбивать с толку. Например, если вы посмотрите на Spring SimpleJpaRepository, вы увидите, что он использует @Transactional.

Чтобы выполнить вставку, вы можете использовать ReadedStatement.

Используете ли вы одно соединение или пул соединений, здесь роли не играет.

Ответ принят как подходящий

Я не думаю, что это можно сделать так, как вы хотите.

если вы посмотрите, как работает JdbcTemplate, обновление в конечном итоге попадает в метод выполнения, который получает соединение с базой данных с помощью DataSourceUtils, который использует TransactionSynchronizationManager. Проверьте это сами здесь:

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-L10 6С84

Если вы действительно хотите понять, что делает @Transactional — вы можете покопаться в github и следить за ним. Возможно, установите точки останова и пройдите через это. Я бы посоветовал прочитать несколько руководств и поверить, что фреймворк делает то, что заявляет.

Другие вопросы по теме