java - 使用面临 IllegalStateException 的多个数据源时的 Spring 批处理问题:无法打开 JPA EntityManager 进行事务处理:已经值
问题描述
我正在尝试在春季批处理中使用多个数据源,其中第一步连接到一个数据库(sybase
),第二步连接到不同的数据源(posgres
),但是当我尝试运行相同的数据时,我遇到了以下错误:
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder@713a4f8d] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@25a51f53] bound to thread [http-nio-9093-exec-1]
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:448)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.startTransaction(AbstractPlatformTransactionManager.java:400)
..
..
Caused by: java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder@713a4f8d] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@25a51f53] bound to thread [http-nio-9093-exec-1]
at org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(TransactionSynchronizationManager.java:193)
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:423)
@Configuration
@EnableJpaRepositories(
basePackages = "com.**.dao.posgres",
entityManagerFactoryRef = "positionEntityManager",
transactionManagerRef = "positionTransactionManager"
)
@EnableTransactionManagement
@EnableAutoConfiguration
public class PosgresdbConfig {
@Autowired
private Environment env;
@Bean
@Primary
@Qualifier("positionEntityManager")
public LocalContainerEntityManagerFactoryBean positionEntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(positionDataSource());
em.setPackagesToScan(
new String[] { "com.**.dao.posgres" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
@Bean
@Primary
@Qualifier("positionDataSource")
public DataSource positionDataSource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("datasource.posgres.driver-class-name"));
dataSource.setUrl(env.getProperty("datasource.posgres.url"));
dataSource.setUsername(env.getProperty("datasource.posgres.username"));
dataSource.setPassword(env.getProperty("datasource.posgres.password"));
return dataSource;
}
@Bean
@Primary
@Qualifier("positionTransactionManager")
public PlatformTransactionManager positionTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
positionEntityManager().getObject());
return transactionManager;
}
}
@Configuration
@EnableJpaRepositories(
basePackages = "com.**.dao.Sybase",
entityManagerFactoryRef = "SybaseEntityManager",
transactionManagerRef = "SybaseTransactionManager"
)
public class SybasedbConfig {
@Autowired
private Environment env;
@Bean
public LocalContainerEntityManagerFactoryBean SybaseEntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(sybaseDataSource());
em.setPackagesToScan(
new String[] { "com.**.Sybase" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
@Bean
public DataSource sybaseDataSource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("datasource.posgres.driver-class-name"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
@Bean
public PlatformTransactionManager SybaseTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
SybaseEntityManager().getObject());
return transactionManager;
}
}
批量配置
@Bean
public Step loadPositionData(final ItemReader<PositionDataResult> positionDataReader,
final ItemProcessor<Object, PositionDataResult> positionRecordProcessor,
final ItemWriter<Object> positionFileWriter) {
return stepBuilderFactory.get("position-load-account")
.<LoadPositionForRiskSP, LoadPositionForRiskSP>chunk(2000)
.reader(positionDataReader)
.processor(positionRecordProcessor)
.writer(positionFileWriter)
.listener(new NoDataFoundListener())
.transactionManager(positionTransactionManager)
.build();
}
在第一步阅读器中使用第一个数据源可以正常工作:
@Transactional(value="SybaseTransactionManager", propagation= Propagation.NOT_SUPPORTED, isolation= Isolation.READ_UNCOMMITTED,readOnly = true)
public PositionDataResult loadDataFromSP() {}
现在在第二步中使用辅助数据源会出错:
@Transactional(transactionManager="positionTransactionManager", readOnly = true, propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
public void write(List<? extends Object> list) throws Exception {}
解决方案
该问题与在此处使ItemWriter#write
方法具有事务性有关:
@Transactional(transactionManager="positionTransactionManager", readOnly = true, propagation = Propagation.REQUIRED, noRollbackFor = Exception.class)
public void write(List<? extends Object> list) throws Exception {}
项目编写器将在 Spring Batch 驱动的事务中调用,因此不需要此注释,应将其删除。对读者来说也是如此。事务管理器和步骤的事务属性应使用StepBuilder
.
如果该步骤涉及多个事务资源,JtaTransactionManager
则应使用 a 来协调这些资源之间的事务。
推荐阅读
- java - 如何修复“javax.net.ssl.SSLHandshakeException:TLS 服务器证书在 2019 年 4 月 16 日之后颁发并由不受信任的旧版赛门铁克根 CA 锚定”
- c++ - 具有扩展方法的自定义类
- python - Pycharm 缩进样式,如 vs 代码中的彩虹缩进扩展
- java - 自定义注释处理器未定位带注释的类
- go - 如何在 KrakenD 中使用带有 TLS 的客户端?
- python - 执行 20 多个 SQL 文件的最有效方法?
- python - 有没有办法可以拆分这个列表?
- php - 在 Shopware 5 中停用某些邮件的发送
- c++ - 为什么 Xcode 找不到标准库?
- java - 使用 throws 关键字并在 main 中处理它,而不是在它发生的方法中处理它