Rollback Checked Exception in Spring Transaction

How to handle checked exceptions in Spring transactions to ensure rollback behavior.

hpyYh avatar
  • hpyYh
  • 2 min read

Spring Rollback Issue

normally, in Spring Boot, when dealing with databases, if an unchecked exception occurs, the application rolls back the transaction. Conversely, if a checked exception occurs, the transaction does not roll back. The most common unchecked exceptions are SQLException and IOException.

    @Transactional
    @Override
    public UUID createNotice(NoticeCommand noticeCommand) throws IOException {

        User creator = userReader.getUser(noticeCommand.getCreatorId());
        Notice notice = noticeCommand.toEntity(creator);

        noticeStore.store(notice);

        throw new IOException("Timeout Exception Test");
    }

for example, if an IOException occurs as above, it does not roll back. Spring assumes that users can sufficiently preprocess such exceptions. (mostly expected to obligatorily include try-catch) in most cases, it is common to handle it with try-catch at the function

However, Most people expect to handle all exceptions. There are several ways to solve this problem.

@Transactional
@Override
public UUID createNotice(NoticeCommand noticeCommand) {

    User creator = userReader.getUser(noticeCommand.getCreatorId());
    Notice notice = noticeCommand.toEntity(creator);

    noticeStore.store(notice);
    try {
        throw new IOException("Timeout Exception Test");
    }catch (IOException e){
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

@Transactional(rollbackFor = Exception.class)

developer add rollbackFor option to @Transactional annotation.

@Transactional(rollbackFor = Exception.class)
@Override
public UUID createNotice(NoticeCommand noticeCommand) {

    User creator = userReader.getUser(noticeCommand.getCreatorId());
    Notice notice = noticeCommand.toEntity(creator);

    noticeStore.store(notice);
    throw new IOException("Timeout Exception Test");
}

New annotation Create : NewTransactional

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Transactional(rollbackFor = Exception.class)
public @interface NewTransactional {
    @AliasFor(annotation = Transactional.class)
    Propagation propagation() default Propagation.REQUIRED;
}

AOP, class shawding, annotation overriding …

@Transactional annotation automatically add Exception rollback rule.

@Configuration
public class TransactionConfig {

    @Bean(name = "winiTransactionAttributeSource")
    @Primary
    public TransactionAttributeSource winiTransactionAttributeSource() {
        return new AnnotationTransactionAttributeSource() {

            @Override
            protected TransactionAttribute findTransactionAttribute(Method method) {
                return updateRollbackRule(super.findTransactionAttribute(method));
            }

            @Override
            protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
                return updateRollbackRule(super.findTransactionAttribute(clazz));
            }

            private TransactionAttribute updateRollbackRule(TransactionAttribute attr) {
                if (attr instanceof RuleBasedTransactionAttribute) {
                    RuleBasedTransactionAttribute rbta = (RuleBasedTransactionAttribute) attr;

                    rbta.getRollbackRules().add(new RollbackRuleAttribute(Exception.class));
                }
                return attr;
            }
            
        };
    }
}

Comment

comments powered by Disqus
hpyYh

Written by : hpyYh

Continue Reading

← Previous Post [CKA] 009. Security 2/2
Next Post → Postgresql Backup