问题
I'm writing a Spring Batch process to migrate a dataset from one system into another. In this case this is as simple as using a RowMapper
implementation to build the object from a query before handing off to the ItemWriter
. The ItemWriter
calls the save
method on my DAO (defined as an interface and handled by spring data)
The issue is this: I have a unique constraint on the MyItem table, and therefore saving a duplicated record will result in a DataIntegrityViolationException
. I have tried catching this within the ItemWriter
to allow me to log that a record was not imported, however during execution it never enters this catch statement. I have attempted catching Exception
AND Throwable
to no avail too.
From what I've noticed, there is a @Transactional
annotation on the 'save' method of my DAO where I would expect the commit and flush to occur. Does Spring Batch alter this transaction in any way? Such that the @Transactional
annotation applies to the 'write' method of the ItemWriter
?
Can I even catch the exception in this class?
I've provided code snippets below, if you need further information - please let me know.
Many thanks for any help you can provide
ItemWriter
@Component
public class MyItemWriter implements ItemWriter<MyItem> {
private static final Logger LOG = LoggerFactory.getLogger(MyItemWriter.class);
@Resource
private MyItemDao myItemDao;
@Override
public void write(List<? extends MyItem> myItems) throws Exception {
for (MyItem myItem : myItems) {
try {
myItemDao.save(myItem);
} catch (Throwable ex) {
LOG.warn("Failed to import MyItem: {}: {} ", myItem.getId(), ex.toString());
}
}
}
}
DAO
public interface MyItemDao extends PagingAndSortingRepository<MyItem, Integer> {
[Custom methods omitted]
}
Spring Batch configuration
<batch:job id="myImportJob" restartable="true" job-repository="jobRepository">
<batch:step id="myImportStep" allow-start-if-complete="true">
<batch:tasklet>
<batch:chunk reader="myItemReader" writer="myItemWriter" commit-interval="50" />
</batch:tasklet>
</batch:step>
</batch:job>
回答1:
A couple points here:
- Remove the
@Transactional
annotation on your DAO.@Transactional
and Spring Batch typically don't play nice. Spring Batch manages the transactions as part of the framework's functionality and attempting to manipulate that functionality can cause unexpected side effects. - As M. Deinum points out, your
ItemWriter
and therefore your DAO is participating in a transaction that Spring Batch is managing. Because of this, you won't get that exception until the transaction commits.
With the above considerations in place, you have two options:
- You can configure skip logic to skip records that throw that exception. If you need to log the item, you can add a
SkipListener
to the mix so that you can log the item that caused the exception. However, there is a performance penalty to be paid with this approach since throwing the exception will result in the transaction being rolled back and replayed one item at a time. - You can filter the items via an
ItemProcessor
. This saves the performance hit for skip logic.
You can read more about Spring Batch's skip logic in section 5.1.5 here: http://docs.spring.io/spring-batch/trunk/reference/html/configureStep.html
来源:https://stackoverflow.com/questions/29146209/cannot-catch-exception-in-spring-batchs-itemwriter