Spring 中绑定线程连接的事务
Transaction with binded thread connection in Spring
我想将一个连接绑定到一个线程,并将该连接用于任何 JdbcTemplate 调用,以最终提交更改或执行回滚。
我正在声明 Groovy 脚本中的所有句子,所以我无法控制将调用多少 SQL 查询,这就是为什么我必须使用此方法而不是 TransactionalTemplate或类似的东西。这个脚本将调用一个助手class,它将使用该连接和JdbcTemplate,我们称之为class SqlHelper.
现在我的非按需工作解决方案是从 groovy 脚本调用 SqlHelper 来初始化事务:
initTransaction(ecommerce)
调用
public void initTransaction(DataSource dataSource) {
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setReadOnly(false);
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
// dataSourceTransactionManager.setDataSource(dataSource);
// dataSourceTransactionManager.setRollbackOnCommitFailure(true);
// dataSourceTransactionManager.setTransactionSynchronization(TransactionSynchronization.STATUS_COMMITTED);
// dataSourceTransactionManager.setDataSource(dataSource);
try {
Connection connection = DataSourceUtils.getConnection(dataSource);
connection.setAutoCommit(false);
DataSourceUtils.prepareConnectionForTransaction(connection, transactionDefinition);
} catch (CannotGetJdbcConnectionException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
之后脚本将调用一些 SQL 操作,例如
sqlUpdate(ecommerce, insertSentence, insertParams)
调用
public Integer update(DataSource dataSource, String sql, Map<String, Object> paramMap) {
return new NamedParameterJdbcTemplate(dataSource).update(sql, paramMap);
}
最后我想使用
完成提交更改的事务
commitTransaction(dataSource)
调用
public void commitTransaction(DataSource dataSource) {
Connection connection = DataSourceUtils.getConnection(dataSource);
try {
connection.commit();
} catch (Exception e) {
rollbackTransaction(dataSource);
}
// DataSourceUtils.resetConnectionAfterTransaction(connection, TransactionDefinition.ISOLATION_DEFAULT);
// SimpleTransactionStatus transactionStatus = new SimpleTransactionStatus(false);
// try {
// dataSourceTransactionManager.commit(transactionStatus);
// jta.commit(transactionStatus);
// } catch (TransactionException e) {
// dataSourceTransactionManager.rollback(transactionStatus);
// throw new RuntimeException(e);
// }
}
private void rollbackTransaction(DataSource dataSource) {
Connection connection = DataSourceUtils.getConnection(dataSource);
try {
connection.rollback();
} catch (SQLException e) {
throw new RuntimeException(e);
}
DataSourceUtils.resetConnectionAfterTransaction(connection, TransactionDefinition.ISOLATION_DEFAULT);
}
我留下了一些测试的注释块,以向您展示我尝试过的方法。我不太清楚 Spring 事务是如何工作的,所以我只是尝试不同的事情并尝试了解所有这些东西是如何工作的......如果你愿意,我会为你提供更多信息;)
提前致谢。
更新
正如 M. Denium 所建议的,这就是我现在所拥有的:
我声明了变量,使用 TransactionStatus
作为 ThreadSafe,最后解决为:
private DataSourceTransactionManager dataSourceTransactionManager = null;
private DefaultTransactionDefinition transactionDefinition = null;
private static final ThreadLocal<TransactionStatus> transactionStatus = new ThreadLocal<TransactionStatus>() {
@Override
protected TransactionStatus initialValue() {
return null;
}
};
然后使用来自 Groovy 脚本的相同调用,使用辅助方法:
public void initTransaction(DataSource dataSource) {
dataSourceTransactionManager = new DataSourceTransactionManager();
transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setReadOnly(false);
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
dataSourceTransactionManager.setRollbackOnCommitFailure(true);
dataSourceTransactionManager.setTransactionSynchronization(TransactionSynchronization.STATUS_UNKNOWN);
dataSourceTransactionManager.setDataSource(dataSource);
transactionStatus.set(dataSourceTransactionManager.getTransaction(null));
}
public void commitTransaction() {
try {
LOG.info("Finishing transaction...");
dataSourceTransactionManager.commit(transactionStatus.get());
dataSourceTransactionManager.getDataSource().getConnection().close();
LOG.info("Done.");
} catch (Throwable e) {
dataSourceTransactionManager.rollback(transactionStatus.get());
throw new RuntimeException("An exception during transaction. Rolling back.");
}
}
您正在尝试重新实现 Spring 的事务抽象已经实现的东西。只需使用正确的 PlatformTransactionManager
(您可能可以从 ApplicationContext
中获取它)保留对 TransactionStatus
而不是 DataSource
的引用并将其用于 commit/rollback .
public TransactionStatus initTransaction() {
return transactionManager.getTransaction(null);
}
public void commit(TransactionStatus status) {
transactionManager.commit(status);
}
除了传递 TransactionStatus
之外,您还可以将其存储在 ThreadLocal
中并在 commit
方法中检索它。这样可以减轻疼痛。
另一个提示,您不应该创建 JdbcTemplate
s 和 NamedParameterJdbcTemplate
s,它们是要创建的重物。在构建时,他们咨询连接以确定异常转换需要哪个数据库和版本。所以在性能方面这不是一件明智的事情。创建单个实例并重复使用,模板是线程安全的,因此您只需要一个实例。
但是我强烈认为您实际上应该使用 Groovy 而不是尝试绕过它。 Groovy有Sql
class可以帮到你。您已经可以访问 DataSource
,因此只需执行类似的操作即可。
def sql = new Sql(dataSource);
sql.withTransaction {
sql.execute "INSERT INTO city (name, state, founded_year) VALUES ('Minneapolis', 'Minnesota', 1867)"
sql.execute "INSERT INTO city (name, state, founded_year) VALUES ('Orlando', 'Florida', 1875)"
sql.execute "INSERT INTO city (name, state, founded_year) VALUES ('Gulfport', 'Mississippi', 1887)"
}
这很简单 Groovy,不需要开发额外的 classes 或编写大量文档来让它工作。只是 Groovy...
我想将一个连接绑定到一个线程,并将该连接用于任何 JdbcTemplate 调用,以最终提交更改或执行回滚。
我正在声明 Groovy 脚本中的所有句子,所以我无法控制将调用多少 SQL 查询,这就是为什么我必须使用此方法而不是 TransactionalTemplate或类似的东西。这个脚本将调用一个助手class,它将使用该连接和JdbcTemplate,我们称之为class SqlHelper.
现在我的非按需工作解决方案是从 groovy 脚本调用 SqlHelper 来初始化事务:
initTransaction(ecommerce)
调用
public void initTransaction(DataSource dataSource) {
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setReadOnly(false);
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
// dataSourceTransactionManager.setDataSource(dataSource);
// dataSourceTransactionManager.setRollbackOnCommitFailure(true);
// dataSourceTransactionManager.setTransactionSynchronization(TransactionSynchronization.STATUS_COMMITTED);
// dataSourceTransactionManager.setDataSource(dataSource);
try {
Connection connection = DataSourceUtils.getConnection(dataSource);
connection.setAutoCommit(false);
DataSourceUtils.prepareConnectionForTransaction(connection, transactionDefinition);
} catch (CannotGetJdbcConnectionException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
之后脚本将调用一些 SQL 操作,例如
sqlUpdate(ecommerce, insertSentence, insertParams)
调用
public Integer update(DataSource dataSource, String sql, Map<String, Object> paramMap) {
return new NamedParameterJdbcTemplate(dataSource).update(sql, paramMap);
}
最后我想使用
完成提交更改的事务commitTransaction(dataSource)
调用
public void commitTransaction(DataSource dataSource) {
Connection connection = DataSourceUtils.getConnection(dataSource);
try {
connection.commit();
} catch (Exception e) {
rollbackTransaction(dataSource);
}
// DataSourceUtils.resetConnectionAfterTransaction(connection, TransactionDefinition.ISOLATION_DEFAULT);
// SimpleTransactionStatus transactionStatus = new SimpleTransactionStatus(false);
// try {
// dataSourceTransactionManager.commit(transactionStatus);
// jta.commit(transactionStatus);
// } catch (TransactionException e) {
// dataSourceTransactionManager.rollback(transactionStatus);
// throw new RuntimeException(e);
// }
}
private void rollbackTransaction(DataSource dataSource) {
Connection connection = DataSourceUtils.getConnection(dataSource);
try {
connection.rollback();
} catch (SQLException e) {
throw new RuntimeException(e);
}
DataSourceUtils.resetConnectionAfterTransaction(connection, TransactionDefinition.ISOLATION_DEFAULT);
}
我留下了一些测试的注释块,以向您展示我尝试过的方法。我不太清楚 Spring 事务是如何工作的,所以我只是尝试不同的事情并尝试了解所有这些东西是如何工作的......如果你愿意,我会为你提供更多信息;)
提前致谢。
更新
正如 M. Denium 所建议的,这就是我现在所拥有的:
我声明了变量,使用 TransactionStatus
作为 ThreadSafe,最后解决为:
private DataSourceTransactionManager dataSourceTransactionManager = null;
private DefaultTransactionDefinition transactionDefinition = null;
private static final ThreadLocal<TransactionStatus> transactionStatus = new ThreadLocal<TransactionStatus>() {
@Override
protected TransactionStatus initialValue() {
return null;
}
};
然后使用来自 Groovy 脚本的相同调用,使用辅助方法:
public void initTransaction(DataSource dataSource) {
dataSourceTransactionManager = new DataSourceTransactionManager();
transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setReadOnly(false);
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
dataSourceTransactionManager.setRollbackOnCommitFailure(true);
dataSourceTransactionManager.setTransactionSynchronization(TransactionSynchronization.STATUS_UNKNOWN);
dataSourceTransactionManager.setDataSource(dataSource);
transactionStatus.set(dataSourceTransactionManager.getTransaction(null));
}
public void commitTransaction() {
try {
LOG.info("Finishing transaction...");
dataSourceTransactionManager.commit(transactionStatus.get());
dataSourceTransactionManager.getDataSource().getConnection().close();
LOG.info("Done.");
} catch (Throwable e) {
dataSourceTransactionManager.rollback(transactionStatus.get());
throw new RuntimeException("An exception during transaction. Rolling back.");
}
}
您正在尝试重新实现 Spring 的事务抽象已经实现的东西。只需使用正确的 PlatformTransactionManager
(您可能可以从 ApplicationContext
中获取它)保留对 TransactionStatus
而不是 DataSource
的引用并将其用于 commit/rollback .
public TransactionStatus initTransaction() {
return transactionManager.getTransaction(null);
}
public void commit(TransactionStatus status) {
transactionManager.commit(status);
}
除了传递 TransactionStatus
之外,您还可以将其存储在 ThreadLocal
中并在 commit
方法中检索它。这样可以减轻疼痛。
另一个提示,您不应该创建 JdbcTemplate
s 和 NamedParameterJdbcTemplate
s,它们是要创建的重物。在构建时,他们咨询连接以确定异常转换需要哪个数据库和版本。所以在性能方面这不是一件明智的事情。创建单个实例并重复使用,模板是线程安全的,因此您只需要一个实例。
但是我强烈认为您实际上应该使用 Groovy 而不是尝试绕过它。 Groovy有Sql
class可以帮到你。您已经可以访问 DataSource
,因此只需执行类似的操作即可。
def sql = new Sql(dataSource);
sql.withTransaction {
sql.execute "INSERT INTO city (name, state, founded_year) VALUES ('Minneapolis', 'Minnesota', 1867)"
sql.execute "INSERT INTO city (name, state, founded_year) VALUES ('Orlando', 'Florida', 1875)"
sql.execute "INSERT INTO city (name, state, founded_year) VALUES ('Gulfport', 'Mississippi', 1887)"
}
这很简单 Groovy,不需要开发额外的 classes 或编写大量文档来让它工作。只是 Groovy...