0
@Service
public class DocumentServiceScheduler {

    private final Path storageArchitasPath;

    @Autowired
    private DocumentService documentService;

    public DocumentServiceScheduler(final DocumentConfiguration documentConfiguration) {
        this.storageArchitasPath = documentConfiguration.storageArchitasPath();
    }

    @Scheduled(cron = "${document.scan.timer}")
    public void scanDocumentsTask() {
        this.scanDocuments();
    }

    public void scanDocuments() {
        final List<DocumentProcessDetail> report = new ArrayList<>();

        FileCoreUtils.loadPathFilesInDirectory(this.storageArchitasPath).forEach(fileName -> report.add(this.documentService.processDocument(fileName)));

        if (!report.isEmpty()) {
            final DataSource excelReport = FileCoreUtils.writeReportAsExcel(report);
            this.documentService.sendReportByEmail(excelReport);
        }
    }
}

Business Class

@Service
public class DocumentServiceImpl implements DocumentService {

    private static Logger logger = LoggerFactory.getLogger(DocumentServiceImpl.class);

    private static String DOCUMENT_SEPARATOR = "#";

    private final Path storageArchitasPath;

    private final Path storageSystemPath;

    private final DocumentRepository documentRepository;

    private final DocumentTypeRepository documentTypeRepository;

    public DocumentServiceImpl(final DocumentRepository documentRepository, final DocumentTypeRepository documentTypeRepository, final DocumentConfiguration documentConfiguration) {
        this.documentRepository = documentRepository;
        this.documentTypeRepository = documentTypeRepository;
        this.storageArchitasPath = documentConfiguration.storageArchitasPath();
        this.storageSystemPath = documentConfiguration.storageSystemPath();
    }

    @Override
    @Transactional
    public DocumentProcessDetail processDocument(final Path fileName) {
        final Date uploadDate = new Date();

        try {
            final String[] splitFileName = fileName.getFileName().toString().split(DOCUMENT_SEPARATOR);
            String brokerId = splitFileName[0].trim();

            final StringBuilder error = this.validateDocumentFormat(splitFileName);

            if (error.length() != 0) {
                FileUtils.forceDelete(this.storageArchitasPath.resolve(fileName).toFile());
                return new DocumentProcessDetail(fileName.toString(), uploadDate, "Failure", error.toString());
            }

            if (splitFileName.length == 2) {
                if (this.deleteDocument(splitFileName)) {
                    return new DocumentProcessDetail(fileName.toString(), uploadDate, "Success", null);
                }

                FileUtils.forceDelete(this.storageArchitasPath.resolve(fileName).toFile());
                error.append("Couldn't delete file, not found in database");
                return new DocumentProcessDetail(fileName.toString(), uploadDate, "Failure", error.toString());

            }

            final String type = splitFileName[1].toUpperCase().trim();
            final String language = splitFileName[2].toUpperCase().trim();
            final String filename = splitFileName[3].trim();
            final String uuid = UUID.randomUUID().toString();
            final String newFileName = uuid + "." + FilenameUtils.getExtension(fileName.toString());

            final Path destination = this.generateStorageSystemPath(brokerId, newFileName);
            brokerId = brokerId.equalsIgnoreCase("all") ? null : brokerId;

            final Optional<DocumentTypeDB> documentTypeDB = this.documentTypeRepository.findById(DocumentType.valueOf(type.toUpperCase()));

            final DocumentDB document = new DocumentDB(filename, brokerId, documentTypeDB.get(), uploadDate, DocumentLanguage.valueOf(language), uuid);

            this.documentRepository.save(document);
            FileCoreUtils.moveFile(this.storageArchitasPath.resolve(fileName), destination);
            throw new Exception();
            //return new DocumentProcessDetail(uuid, fileName.toString(), uploadDate, "Success", null);
        } catch (final Exception e) {
            try {
                FileUtils.forceDelete(this.storageArchitasPath.resolve(fileName).toFile());
            } catch (final IOException e1) {
                logger.error("An unexpected error while deleting file: " + e.getStackTrace());
            }
            //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw new RuntimeException();
            //return new DocumentProcessDetail(fileName.toString(), uploadDate, "failure", "An unexpected error occurred: " + e.getStackTrace());
        }
    }
}

Repository class

@Repository
public interface DocumentRepository extends JpaRepository<DocumentDB, Long>, 
DocumentRepositoryCustom {

    DocumentDB findByDocumentReference(String reference);

}

Using mysql 5.7

The problem here is that Transactional is not being triggered... I throw a RuntimeException and it saves my item in database anyway... I tried to programmatically force rollback, but it also gets ignored... TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() I also added @EnableTransactionManagementalso does not work and I got this message in console

==========================
CONDITION EVALUATION DELTA
==========================


Positive matches:
-----------------

    None


Negative matches:
-----------------

   TransactionAutoConfiguration.EnableTransactionManagementConfiguration:
      Did not match:
         - @ConditionalOnMissingBean (types: org.springframework.transaction.annotation.AbstractTransactionManagementConfiguration; SearchStrategy: all) found beans of type 'org.springframework.transaction.annotation.AbstractTransactionManagementConfiguration' org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration (OnBeanCondition)

   TransactionAutoConfiguration.EnableTransactionManagementConfiguration.CglibAutoProxyConfiguration:
      Did not match:
         - Ancestor org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration did not match (ConditionEvaluationReport.AncestorsMatchedCondition)
      Matched:
         - @ConditionalOnProperty (spring.aop.proxy-target-class=true) matched (OnPropertyCondition)



Exclusions:
-----------

    None


Unconditional classes:
----------------------

    None

How can I force transaction rollback when an Exception occurs?

EDIT

    @Transactional
    public void testDoc() {
        final DocumentDB document = new DocumentDB("test", this.brokerId, new DocumentTypeDB(DocumentType.valueOf("TAX_FORMS")), new Date(), DocumentLanguage.valueOf("FRENCH"), "000");
        System.out.println("documentRepository.save called... start");
        this.documentRepository.save(document);
        System.out.println("documentRepository.save called... end");
    }

Transaction behavior

2021-08-18 15:32:26.486  INFO 7976 --- [           main] o.s.t.c.transaction.TransactionContext   : Began transaction (1) for test context [DefaultTestContext@7be58f16 testClass = DocumentServiceImplTest, testInstance = be.architas.broker.portal.core.service.DocumentServiceImplTest@553f1d75, testMethod = testDoc@DocumentServiceImplTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@242aa8d9 testClass = DocumentServiceImplTest, locations = '{}', classes = '{class be.architas.broker.portal.core.BrokerPortalImplTestConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@1e4d3ce5, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@379614be, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@14fa86ae, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@398dada8, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@4066c471, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@65466a6a], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@4ebd0b52]; rollback [true]
documentRepository.save called... start
2021-08-18 15:32:26.651 TRACE 7976 --- [           main] o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
Hibernate: 
    select
        documentty_.ID,
        documentty_.IS_OFFICIAL as is_offic2_1_,
        documentty_.RETENTION as retentio3_1_ 
    from
        DOCUMENT_TYPE documentty_ 
    where
        documentty_.ID=?
Hibernate: 
    insert 
    into
        DOCUMENT
        (BROKER_ID, DOCUMENT_REFERENCE, LANGUAGE, NAME, TYPE_ID, UPLOAD_DATE) 
    values
        (?, ?, ?, ?, ?, ?)
2021-08-18 15:32:26.757 TRACE 7976 --- [           main] o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
documentRepository.save called... end
2021-08-18 15:32:26.771  INFO 7976 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: [DefaultTestContext@7be58f16 testClass = DocumentServiceImplTest, testInstance = be.architas.broker.portal.core.service.DocumentServiceImplTest@553f1d75, testMethod = testDoc@DocumentServiceImplTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@242aa8d9 testClass = DocumentServiceImplTest, locations = '{}', classes = '{class be.architas.broker.portal.core.BrokerPortalImplTestConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@1e4d3ce5, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@379614be, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@14fa86ae, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@398dada8, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@4066c471, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@65466a6a], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]

It rolled back, but the repository apparently has its own transaction, which did the job then closes, is that the issue?

bindCake
  • 13
  • 1
  • 5
  • Are you using a parallel stream maybe? – slauth Aug 18 '21 at 09:25
  • Your code doesn't make sense. The `list.forEach` operateson a `Stream` of `List` so calling save with a single element won't work. Hence this isn't code you are actually using or is too obfuscated to be of any use. You are also committing a very important part and that is which database you are using. – M. Deinum Aug 18 '21 at 09:27
  • I'm not using parallel, and the code works, its just the rollback that does not work, I just edited the code, it is maybe more appealing now. Thanks for answering. – bindCake Aug 18 '21 at 09:50
  • The thing is that the items are files, and when manipulating them, something can go wrong, im just trying to understand how to rollback if that's the case. – bindCake Aug 18 '21 at 09:52
  • You still haven't explained which database you are using, that and still this is dumbed down code not real code. So the problem could be somewhere else but because this is dummed down code that is impossible to tell. – M. Deinum Aug 18 '21 at 09:58
  • Probably your `Repo#save` method closes the transaction so records are persisted to database? Do you have any special annotations in it? Can you post the `Repo` class also? – pleft Aug 18 '21 at 10:00
  • And please also post your @Configuration class – pleft Aug 18 '21 at 10:08
  • Just edited with my code, code is working fine, the only issue is when I wanted to see if it rolls on exceptions, and it doesn't. Concerning configuration for the transactional, I have none. – bindCake Aug 18 '21 at 11:06
  • Probably transaction management is disabled in your project? Read this answer here: https://stackoverflow.com/a/40724843/3635454 and see if it might help you – pleft Aug 18 '21 at 11:26
  • Isn't it configured by default on spring boot? and I tried to add the annotation @EnableTransactionManagement, still didn't work – bindCake Aug 18 '21 at 11:55
  • Just edited it with the transaction behavior.... Apparently the job is done inside jparepository with another transaction... is that it? if so, how to solve it? – bindCake Aug 18 '21 at 13:35

0 Answers0