我有一个相当具体的场景,遇到了问题:
我有一个骆驼路线,它试图从任意数量的数据源中获取数据并将其插入中央数据库。如果目标表在中央数据库中不存在,我将尝试从源数据库获取DDL。在中央数据库中创建丢失的表非常有用,但是当Camel路由下次按其计划运行时,即使我在数据库工具中可以看到它也找不到该表。如果我只是重新启动spring boot实例,它将找到该表并且可以正常工作。
为使您更好地了解其工作原理,以下是错误之处的一小部分:
from("direct:ensureTargetTableExists")
.routeId("ensureTargetTableExists")
.doTry()
.toD(String.format("sql:select * from ${in.header.%s} where false?dataSource=%s&outputHeader=%s", QueryHandler.Properties.TARGET_TABLE, DataSourceHandler.Properties.TARGET_DATASOURCE_NAME, "tableExists"))
.to("direct:handleResultSet")
.doCatch(BadSqlGrammarException.class)
.log("entered catch")
.log(LoggingLevel.INFO, String.format("Could not find table ${in.header.%s} at %s, will attempt to create it by fetching DDL.", QueryHandler.Properties.TARGET_TABLE, DataSourceHandler.Properties.TARGET_DATASOURCE_NAME))
.to("direct:fetchTableDdlFromSource")
.endDoTry();
from("direct:fetchTableDdlFromSource")
.routeId("fetchTableDdlFromSource")
.streamCaching()
.bean(DumpHandler.class, "generateDumpCommand")
.toD(String.format("ssh:{{sync.dump.ssh.user}}@${in.header.%s}?password={{sync.dump.ssh.password}}", DataSourceHandler.Properties.FETCHED_FROM_HOST))
.to("direct:applyDdlToTargetDatabase");
from("direct:applyDdlToTargetDatabase")
.routeId("applyDdlToTargetDatabase")
.transacted("targetTransactionPolicy")
.toD(String.format("sql:?dataSource=%s&useMessageBodyForSql=true", DataSourceHandler.Properties.TARGET_DATASOURCE_NAME))
.bean(FileUtil.class, "saveDdl")
// Nothing else is called after this, since I want the transaction to be completed and commited
.log(LoggingLevel.INFO, "Table will be populated next scheduled run.");
我让它运行两次迭代,这就是发生的情况:
entered catch
Could not find table article_flags at target, will attempt to create it by fetching DDL.
Saving DDL to config/sql/ddl/article_flags-20181206_162310.sql
Successfully created table article_flags
Table will be populated next scheduled run.
entered catch
Could not find table article_flags at target, will attempt to create it by fetching DDL.
Failed delivery for (MessageId: ID-overlord-1544109787810-0-12 on ExchangeId: ID-overlord-1544109787810-0-9). Exhausted after delivery attempt: 1 caught: org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar []; nested exception is org.postgresql.util.PSQLException: ERROR: relation "article_flags" already exists
我尝试使用事务管理器和事务策略来解决此问题,这些事务管理器和事务策略是在应用程序启动时添加的,但无济于事。如您所见,我在上面的骆驼路线中使用交易策略targetTransactionPolicy
。
public class ContextConfiguration implements BeanDefinitionRegistryPostProcessor {
private static final Logger LOG = LoggerFactory.getLogger(ContextConfiguration.class);
private Map<String, BasicDataSource> datasources;
public ContextConfiguration(Map<String, BasicDataSource> datasources) {
this.datasources = datasources;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
for (Entry<String, BasicDataSource> source : datasources.entrySet()) {
LOG.info("Adding datasource {} targeting {} to Spring registry", source.getKey(), source.getValue().getUrl());
PlatformTransactionManager transactionManager = new DataSourceTransactionManager(source.getValue());
SpringTransactionPolicy transactionPolicy = new SpringTransactionPolicy(transactionManager);
registry.registerBeanDefinition(source.getKey() + "TransactionPolicy", BeanDefinitionBuilder.genericBeanDefinition(SpringTransactionPolicy.class, () -> transactionPolicy).getBeanDefinition());
registry.registerBeanDefinition(source.getKey() + "TransactionManager", BeanDefinitionBuilder.genericBeanDefinition(PlatformTransactionManager.class, () -> transactionManager).getBeanDefinition());
registry.registerBeanDefinition(source.getKey(), BeanDefinitionBuilder.genericBeanDefinition(BasicDataSource.class, () -> source.getValue()).getBeanDefinition());
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { }
}
在过去的几天里,我一直在听不见声音,而我似乎无法超越。任何帮助或见解将不胜感激。我最好的猜测是它与事务相关,或者与我创建事务管理器,策略和数据源的方式有关。
其他信息:
答案 0 :(得分:0)
答案 1 :(得分:0)
我想我以令人满意的方式解决了它。我怀疑这是交易。
我向目标数据源添加了交易策略:
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
registerTargetTransactionPolicy(datasources.get("target"), registry);
for (Entry<String, BasicDataSource> source : datasources.entrySet()) {
LOG.info("Adding datasource {} targeting {} to Spring registry", source.getKey(), source.getValue().getUrl());
registry.registerBeanDefinition(source.getKey(), BeanDefinitionBuilder.genericBeanDefinition(BasicDataSource.class, () -> source.getValue()).getBeanDefinition());
}
}
private void registerTargetTransactionPolicy(BasicDataSource datasource, BeanDefinitionRegistry registry) {
PlatformTransactionManager transactionManager = new DataSourceTransactionManager(datasource);
SpringTransactionPolicy transactionPolicy = new SpringTransactionPolicy(transactionManager);
transactionPolicy.setPropagationBehaviorName("PROPAGATION_REQUIRES_NEW");
registry.registerBeanDefinition(TARGET_REQUIRES_NEW_POLICY, BeanDefinitionBuilder.genericBeanDefinition(SpringTransactionPolicy.class, () -> transactionPolicy).getBeanDefinition());
}
然后我使用.policy(ContextConfiguration.TARGET_REQUIRES_NEW_POLICY)
在我的骆驼路线中使用它,它可以正常工作。