使用案例
我正在构建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> {
}
答案 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