使用案例:LazyInitializationException中注入@Repository在@Transactional(传播= Propagation.REQUIRES_NEW)方法
我建立用于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());
}
}
}
,是专为存在弹簧能够拦截@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> {
}
你可以发布代码给你地址实体和个人实体吗?你似乎有 @OneToOne(fetch = FetchType.LAZY)而不是 @OneToOne(fetch = FetchType.EAGER)在你的个人实体中,无论你在哪里建立地址和人的关系 – user8271644
@ user8271644它是@OneToMany(fetch = FetchType.LAZY),EAGER会帮助你是正确的,但这不是我想要的,我希望数据在同一个事务中被延迟地获取。 –