@Transactional(propagation = Propagation.REQUIRES_NEW)方法

时间:2017-07-19 15:39:21

标签: hibernate spring-boot spring-data-jpa

使用案例

我正在构建CSV文件的导入程序,我有一个@Controller用于上传CSV文件,@Entity用于CSV文件和CSV文件中的行。 由于CSV文件可能非常大,我想导入它们@Async

问题

当我上传文件时,我将其保存到数据库并触发我的异步功能来处理它,这是有效的。然后我处理自己的事务中的每一行,并将其作为一个实体及其行号保存到数据库,这也可以正常工作。然后我触发实际的业务逻辑来处理来自CSV行的行,这行在自己的事务中执行。

在最后一个事务中,我从该类中使用的存储库中获取LazyInitializationException,我不知道原因。

在调用者的catch块中,我使用异常消息并将其保存到数据库中,这在其自己的事务中按需要工作。 我的上传页面立即返回,CSV文件被异步处理,我收到CSV文件中每一行的错误,这些错误被保存到数据库中。

之后我拥有数据库中的所有内容(不应该)。

问题/想法

存储库似乎使用了另一个事务,所以它没有使用Importer类中的事务,为什么?

这是抛出LazyInitializationException的最终类,调用类如下所示。

@Service
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public class Importer {

    private final PersonRepository personRepository;

    public Importer(PersonRepository personRepository) {
        this.personRepository = personRepository;
    }

    void import(String[] importLine) {
        getPersonAddress(importLine);
    }

    private PersonAddress getPerson(String[] importLine) {
        String name = importLine[5];
        Person person = personRepository.findByName(name);
        // This results in the following exception:
        // org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: de.twimbee.Person.addresses, could not initialize proxy - no Session
        return person.getAddresses().get(1);
    }
}

其他课程

用于上传文件并将其保存到数据库的控制器。

@Controller
@Transactional(rollbackFor = Exception.class)
public class MyController {

    private final CsvFileRepository csvFileRepository;
    private final CsvImporter csvImporter;

    public MyController(CsvFileRepository csvFileRepository, CsvImporter csvImporter) {
        this.csvFileRepository = csvFileRepository;
        this.csvImporter = csvImporter;
    }

    public String postFile(String filename) {
        CsvFile csvFile = new CsvFile(filename)
        csvFile = csvFileRepository.save(new CsvFile(filename));

        try {
            List<String[]> csvContent = readCsvContent(filename);
            csvImporter.import(csvFile.getId(), csvContent);
        } catch (IOException e) {
            csvFile.setError(e.getMessage());
        }
    }
}

仅为spring提供的服务可以拦截@Async

@Service
public class CsvImporter {

    private final LineImporter lineImporter;

    public CsvImporter(LineImporter lineImporter) {
        this.lineImporter = lineImporter;
    }

    @Async
    void import(int csvFileId, List<String[]> csvContent) {
        for(int i = 0; i < csvContent.size(); i++) {
            lineImporter.import(csvFileId, csvContent.get(i), i + 1);
        }
    }
}

具有REQUIRES_NEW的行导入程序,以便每行都在其自己的事务中导入。

@Service
@Transactional(rollbackFor = Exception.class)
public class LineImporter {

    private final CsvFileRepository csvFileRepository;
    private final ImportLineRepository importLineRepository;
    private final Importer importer;

    public LineImporter(CsvFileRepository csvFileRepository, ImportLineRepository importLineRepository, Importer importer) {
        this.csvFileRepository = csvFileRepository;
        this.importLineRepository = importLineRepository;
        this.importer = importer;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    void import(int csvFileId, String[] csvContent, int lineNumber) {
        ImportLine importLine = new ImportLine(lineNumber);
        importLine.setCsvFile(csvFileRepository.findOne(csvFileId));
        try {
            int dataId = importer.import(importLine);
            importLine.setDataId(dataId);
        } catch (ImportException e) {
            importLine.setError(e.getMessage());
        }
        importLineRepository.save(importLine);
    }
}

直到这一点,一切似乎都按预期工作。 Importer会为每一行抛出异常,错误消息会写入数据库。

存储库基本上看起来都一样。

@Repository
@Transactional(rollbackFor = Exception.class)
public interface CsvFileRepository extends JpaRepository<CsvFile, Integer> {
}

1 个答案:

答案 0 :(得分:1)

@Transactional(propagation = Propagation.REQUIRES_NEW)
void import(int csvFileId, String[] csvContent, int lineNumber) {

默认情况下,@ Transaction仅适用于公共方法

要改变你需要从Spring AOP Proxt切换到AspecJ Proxy

您可以在此处找到更多详细信息 Spring AOP vs AspectJ