Я просматриваю несколько руководств, в которых говорится, что для начала транзакции вам нужно написать 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() }
);
}
}
До сих пор мне удавалось заставить это работать вот так. Но это выглядит не слишком хорошо. Любые другие решения?
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. Проверьте это сами здесь:
Если вы действительно хотите понять, что делает @Transactional — вы можете покопаться в github и следить за ним. Возможно, установите точки останова и пройдите через это. Я бы посоветовал прочитать несколько руководств и поверить, что фреймворк делает то, что заявляет.