use*_*710 5 java spring spring-transactions jooq
我有一个如下所示的数据源配置类,DataSource使用 JOOQ使用单独的bean 进行测试和非测试环境。在我的代码中,我不使用DSLContext.transaction(ctx -> {...}而是将方法标记为事务性,以便 JOOQ 遵循 Spring 的事务性声明性事务。我正在使用Spring 4.3.7.RELEASE。
我有以下问题:
@Transactional按预期工作。无论我使用DSLContext'sstore()方法多少次,单个方法都是事务性的,并且 aRuntimeException会触发整个事务的回滚。@Transactional完全忽略。一个方法不再是事务性的,并且TransactionSynchronizationManager.getResourceMap()拥有两个单独的值:一个显示到我的连接池(不是事务性的),一个显示TransactionAwareDataSourceProxy)。

在这种情况下,我希望只有一个类型的资源TransactionAwareDataSourceProxy可以包装我的 DB CP。
@Transactional即使在运行时也能按预期正常工作,但 TransactionSynchronizationManager.getResourceMap()具有以下值:

在这种情况下,我DataSourceTransactionManager似乎甚至不知道TransactionAwareDataSourceProxy(很可能是因为我将它传递给 simple DataSource,而不是代理对象),这似乎完全“跳过”了代理。
我的问题是:我似乎正确的初始配置,但没有工作。提议的“修复”有效,但 IMO 根本不应该起作用(因为事务管理器似乎不知道TransactionAwareDataSourceProxy)。
这里发生了什么?有没有更干净的方法来解决这个问题?
@Configuration
@EnableTransactionManagement
@RefreshScope
@Slf4j
public class DataSourceConfig {
@Bean
@Primary
public DSLContext dslContext(org.jooq.Configuration configuration) throws SQLException {
return new DefaultDSLContext(configuration);
}
@Bean
@Primary
public org.jooq.Configuration defaultConfiguration(DataSourceConnectionProvider dataSourceConnectionProvider) {
org.jooq.Configuration configuration = new DefaultConfiguration()
.derive(dataSourceConnectionProvider)
.derive(SQLDialect.POSTGRES_9_5);
configuration.set(new DeleteOrUpdateWithoutWhereListener());
return configuration;
}
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public DataSourceConnectionProvider dataSourceConnectionProvider(DataSource dataSource) {
return new DataSourceConnectionProvider(dataSource);
}
@Configuration
@ConditionalOnClass(EmbeddedPostgres.class)
static class EmbeddedDataSourceConfig {
@Value("${spring.jdbc.port}")
private int dbPort;
@Bean(destroyMethod = "close")
public EmbeddedPostgres embeddedPostgres() throws Exception {
EmbeddedPostgres embeddedPostgres = EmbeddedPostgresHelper.startDatabase(dbPort);
return embeddedPostgres;
}
@Bean
@Primary
public DataSource dataSource(EmbeddedPostgres embeddedPostgres) throws Exception {
DataSource dataSource = embeddedPostgres.getPostgresDatabase();
return new TransactionAwareDataSourceProxy(dataSource);
}
}
@Configuration
@ConditionalOnMissingClass("com.opentable.db.postgres.embedded.EmbeddedPostgres")
@RefreshScope
static class DefaultDataSourceConfig {
@Value("${spring.jdbc.url}")
private String url;
@Value("${spring.jdbc.username}")
private String username;
@Value("${spring.jdbc.password}")
private String password;
@Value("${spring.jdbc.driverClass}")
private String driverClass;
@Value("${spring.jdbc.MaximumPoolSize}")
private Integer maxPoolSize;
@Bean
@Primary
@RefreshScope
public DataSource dataSource() {
log.debug("Connecting to datasource: {}", url);
HikariConfig hikariConfig = buildPool();
DataSource dataSource = new HikariDataSource(hikariConfig);
return new TransactionAwareDataSourceProxy(dataSource);
}
private HikariConfig buildPool() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
config.setDriverClassName(driverClass);
config.setConnectionTestQuery("SELECT 1");
config.setMaximumPoolSize(maxPoolSize);
return config;
}
}
Run Code Online (Sandbox Code Playgroud)
@Configuration
@EnableTransactionManagement
@RefreshScope
@Slf4j
public class DataSourceConfig {
@Bean
public DataSourceConnectionProvider dataSourceConnectionProvider(TransactionAwareDataSourceProxy dataSourceProxy) {
return new DataSourceConnectionProvider(dataSourceProxy);
}
@Bean
public TransactionAwareDataSourceProxy transactionAwareDataSourceProxy(DataSource dataSource) {
return new TransactionAwareDataSourceProxy(dataSource);
}
@Configuration
@ConditionalOnMissingClass("com.opentable.db.postgres.embedded.EmbeddedPostgres")
@RefreshScope
static class DefaultDataSourceConfig {
@Value("${spring.jdbc.url}")
private String url;
@Value("${spring.jdbc.username}")
private String username;
@Value("${spring.jdbc.password}")
private String password;
@Value("${spring.jdbc.driverClass}")
private String driverClass;
@Value("${spring.jdbc.MaximumPoolSize}")
private Integer maxPoolSize;
@Bean
@Primary
@RefreshScope
public DataSource dataSource() {
log.debug("Connecting to datasource: {}", url);
HikariConfig hikariConfig = buildPoolConfig();
DataSource dataSource = new HikariDataSource(hikariConfig);
return dataSource; // not returning the proxy here
}
}
}
Run Code Online (Sandbox Code Playgroud)
我会将我的评论变成答案。
事务管理器不应该知道代理。从文档中:
请注意,事务管理器(例如 DataSourceTransactionManager)仍然需要使用底层 DataSource,而不是使用此代理。
该类TransactionAwareDataSourceProxy是一个特殊用途的类,大多数情况下不需要。任何通过 Spring 框架基础设施与数据源交互的东西都不应该在其访问链中包含代理。该代理适用于无法与 Spring 基础设施交互的代码。例如,已经设置为与 JDBC 一起使用的第三方库,但不接受任何 Spring 的 JDBC 模板。这在与上面相同的文档中进行了说明:
该代理允许数据访问代码使用普通的 JDBC API,并且仍然参与 Spring 管理的事务,类似于 J2EE/JTA 环境中的 JDBC 代码。但是,如果可能的话,即使没有目标数据源的代理,也可以使用 Spring 的 DataSourceUtils、JdbcTemplate 或 JDBC 操作对象来获取事务参与,从而避免首先定义此类代理。
如果您没有任何需要绕过 Spring 框架的代码,那么TransactionAwareDataSourceProxy根本不要使用。如果您确实有这样的旧代码,那么您将需要执行在第二个设置中已配置的操作。您将需要创建两个 bean,一个是数据源,另一个是代理。然后,您应该为所有 Spring 托管类型提供数据源,并为遗留类型提供代理。
| 归档时间: |
|
| 查看次数: |
911 次 |
| 最近记录: |