我有一个数据源配置类,如下所示,使用JOOQ测试和非测试环境的单独DataSource
bean。在我的代码中,我不使用DSLContext.transaction(ctx -> {...}
,而是将该方法标记为事务性,以便JOOQ遵循Spring的事务性声明式事务。我正在使用 Spring 4.3.7.RELEASE 。
我有以下问题:
@Transactional
按预期工作。无论我使用DSLContext
store()
方法多少次,单个方法都是事务性的,而RuntimeException
会触发整个事务的回滚。@Transactional
完全被忽略。方法不再是事务性的,TransactionSynchronizationManager.getResourceMap()
包含两个单独的值:一个显示到我的连接池(不事务性),另一个显示TransactionAwareDataSourceProxy
)。
在这种情况下,我原本只期望一个TransactionAwareDataSourceProxy
类型的资源包装我的数据库CP。
@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;
}
}
@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
}
}
}
答案 0 :(得分:1)
我会将我的评论转化为答案。
事务管理器不应该知道代理。来自documentation:
请注意事务管理器 DataSourceTransactionManager仍然需要使用底层 DataSource,不是这个代理。
类TransactionAwareDataSourceProxy
是一个特殊目的类,在大多数情况下是不需要的。通过Spring框架基础结构与数据源连接的任何东西都不应该在其访问链中具有代理。代理适用于无法与Spring基础结构连接的代码。例如,第三方库已经设置为使用JDBC并且不接受任何Spring的JDBC模板。这与上述相同的文档中说明:
此代理允许数据访问代码与纯JDBC API一起使用 仍然参与Spring管理的事务,类似于JDBC代码 在J2EE / JTA环境中。但是,如果可能的话,请使用Spring 获取DataSourceUtils,JdbcTemplate或JDBC操作对象 即使没有代表目标,也可以参与交易 DataSource,无需在第一个中定义这样的代理 的地方。
如果您没有任何代码需要绕过Spring框架,那么根本不要使用TransactionAwareDataSourceProxy
。如果您确实有这样的遗留代码,那么您将需要执行第二次设置中已配置的代码。您将需要创建两个bean,一个是数据源,另一个是代理。然后,您应该将数据源提供给所有Spring托管类型以及遗留类型的代理。