【发布时间】:2018-08-15 08:20:07
【问题描述】:
我有一个如下所示的数据源配置类,带有单独的 DataSource bean,用于使用 JOOQ 的测试和非测试环境。在我的代码中,我不使用DSLContext.transaction(ctx -> {...},而是将该方法标记为事务性的,以便JOOQ 遵循Spring 的事务性声明性事务。我正在使用 Spring 4.3.7.RELEASE。
我有以下问题:
- 在测试 (JUnit) 期间,
@Transactional按预期工作。无论我使用多少次DSLContext的store()方法,单个方法都是事务性的,而RuntimeException会触发整个事务的回滚。 - 在实际生产运行时,
@Transactional被完全忽略。方法不再是事务性的,TransactionSynchronizationManager.getResourceMap()拥有两个单独的值:一个显示给我的连接池(不是事务性的),一个显示TransactionAwareDataSourceProxy)。
在这种情况下,我希望只有一个 TransactionAwareDataSourceProxy 类型的资源来包装我的 DB CP。
- 在使用我所做的第二组配置更改(下面用“AFTER”注明)进行多次试验和错误之后,
@Transactional即使在运行时也可以正常工作,尽管TransactionSynchronizationManager.getResourceMap()具有以下值:
在这种情况下,我的DataSourceTransactionManager 似乎甚至不知道TransactionAwareDataSourceProxy(很可能是因为我传递了简单的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;
}
}
AFTER(运行时的事务性,正如预期的那样,所有未列出的 bean 与上述相同)
@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
}
}
}
【问题讨论】:
-
作为记录,这个问题似乎也被问到了 spring boot 问题跟踪器:github.com/spring-projects/spring-boot/issues/12377
-
嗨@LukasEder,是的,我已经单独发布了这个问题,尽管它似乎放错了地方。我把它移到了SO。对于没有在此处提及上述内容而造成的混乱,我深表歉意。感谢您更新此问题和跟踪器。
-
您的课程是否直接使用
DataSource(例如JDBC)?如果没有,我认为您根本不应该使用TransactionAwareDataSourceProxy。正如the docs 中所述,“请注意,事务管理器,例如 DataSourceTransactionManager,仍然需要使用底层 DataSource,而不是使用此代理。” -
如果您的代码确实使用了 JDBC 以及 Spring 的抽象,那么您将需要创建两个 bean。首先是普通数据源,您将其传递给 Spring 的抽象(例如
DataSourceTransactionManager)和第二个 bean,即代理,它包装第一个 bean 并将其传递给您的类。
标签: java spring spring-transactions jooq