问题
My current configuration of file:inbound-channel-adapter is working fine. I'm running the application in multiple servers and only one is actually processing the file. Database table INT_METADATA_STORE also updated successfully. Issue I'm facing is that one of the servers are still trying to insert the record and below exception is raised:
org.springframework.messaging.MessagingException: nested exception is
org.springframework.dao.DuplicateKeyException: PreparedStatementCallback;
SQL [INSERT INTO INT_METADATA_STORE(METADATA_KEY, METADATA_VALUE, REGION) SELECT ?, ?, ? FROM INT_METADATA_STORE WHERE METADATA_KEY=? AND REGION=? HAVING COUNT(*)=0];
ORA-00001: unique constraint (SMS_OWNER.INT_METADATA_STORE_PK) violated;
nested exception is java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (SMS_OWNER.INT_METADATA_STORE_PK) violated
I've tried different flavors of isolation and no luck. Is this something related to the transaction manager used since I'm using XML with Spring Integration + Java? See below some configurations:
<int-file:inbound-channel-adapter directory="file:/tmp/input/" prevent-duplicates="true" filter="compositeFileListFilter">
<int:poller max-messages-per-poll="1" cron="*/10 * * * * *">
<int:transactional transaction-manager="transactionManager" isolation="READ_COMMITTED" timeout="5" />
</int:poller>
</int-file:inbound-channel-adapter>
and
@Configuration
@EnableTransactionManagement
public class MetadataStoreConfiguration {
@Value("${input.file.pattern:(DUMMY)(_).*\\.(xml)}")
private String pattern;
@Bean
@Qualifier("fileSystemPersistentAcceptOnceFileListFilter")
public FileSystemPersistentAcceptOnceFileListFilter fileSystemPersistentAcceptOnceFileListFilter(final DataSource dataSource) {
return new FileSystemPersistentAcceptOnceFileListFilter(metadataStore(dataSource),"");
}
@Bean
@Qualifier("metadataStore")
public JdbcMetadataStore metadataStore(final DataSource dataSource) {
JdbcMetadataStore metadataStore = new JdbcMetadataStore(dataSource);
return metadataStore;
}
@Bean
public CompositeFileListFilter<File> compositeFileListFilter(FileSystemPersistentAcceptOnceFileListFilter fileSystemPersistentAcceptOnceFileListFilter) {
CompositeFileListFilter<File> filter = new CompositeFileListFilter<>(Arrays.asList(fileSystemPersistentAcceptOnceFileListFilter, new RegexPatternFileListFilter(pattern)));
return filter;
}
@Bean
@Primary
public PlatformTransactionManager transactionManager(final DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
I'm using Spring Boot 2.2.4.RELEASE, Java 8 and Oracle as database. Any help will be much appreciated.
回答1:
No, your configuration is fully OK. It really doesn't matter because it is read only once at start up. At runtime you have only beans and they are wired properly. So, it's OK to have that PlatformTransactionManager
and compositeFileListFilter
in Java config and use them as references from XML one.
I think the problem is not about transactions. More over it even looks like they work!
Both of your transactions try to insert and don't see a value. Of course, one of them is OK on commit, but another fails because the value is already inserted. With IINSERT INTO .. SELECT ... HAVING
we achieve an atomicity for the current transaction. But it really doesn't give us a chance to avoid conflict on commit...
It looks like our JdbcMetadataStore.putIfAbsent()
should be improved to catch this SQLIntegrityConstraintViolationException
as a fact of data presence and return the current value.
Please, raise a GH issue and we'll think how to proceed!
来源:https://stackoverflow.com/questions/61747197/duplicatekeyexception-raised-when-using-spring-jdbcmetadatastore-oracle