【发布时间】:2015-05-31 15:07:40
【问题描述】:
我遇到了一些我无法轻易解释的奇怪行为。以下代码运行良好:
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
statement.executeUpdate("DELETE FROM product");
} catch (Exception ex) {
throw new RuntimeException(ex);
}
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
statement.executeUpdate("INSERT INTO product ...");
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
但是这段代码会导致死锁:
jdbcTemplate.update("DELETE FROM product");
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
statement.executeUpdate("INSERT INTO product ...");
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
例外是
java.sql.SQLException: 超过锁定等待超时;尝试重启事务
jdbcTemplate 和 dataSource 都是由 Spring boot 创建并自动装配的
@Autowired
private DataSource dataSource;
@Autowired
private JdbcTemplate jdbcTemplate;
语句构成服务中方法的一部分(带有@Transactional 注释)
谁能解释为什么会这样?
【问题讨论】:
-
你为什么要这样?只需使用
JdbcTemplate进行所有 JDBC 访问。问题是每次您执行getConnection时,您都在使用与数据库的新连接,如果它们在同一个连接上操作,可能会导致死锁。要修复它,请不要这样做,而是使用JdbcTemplate进行所有访问。 -
我不能对所有事情都使用 JdbcTemplate - 我们执行 mysql upserts“在重复密钥更新时插入..”并且 KeyHolder 在生成的 id 上阻塞,而 vanilla JDBC 可以适应这一点。我仍然不明白如果我有单线程顺序调用来获取和释放连接,我将如何陷入死锁?
-
你能显示使用事务边界(@transaction),你使用哪个存储引擎?
-
默认情况下,每次调用 getConnection 都会创建一个新连接,除非您将数据源包装在
TransactionAwareDataSourceProxy中,但我怀疑您是这样的。您仍然可以(并且应该)使用JdbcTemplate,为此使用ConnectionCallback,这样您就可以确定您使用相同的连接进行所有访问。 -
非常感谢,这是有道理的。
标签: spring spring-boot spring-data spring-jdbc