2017-07-19 136 views
0

使用案例: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> { 
} 
+0

你可以发布代码给你地址实体和个人实体吗?你似乎有 @OneToOne(fetch = FetchType.LAZY)而不是 @OneToOne(fetch = FetchType.EAGER)在你的个人实体中,无论你在哪里建立地址和人的关系 – user8271644

+0

@ user8271644它是@OneToMany(fetch = FetchType.LAZY),EAGER会帮助你是正确的,但这不是我想要的,我希望数据在同一个事务中被延迟地获取。 –

回答

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

默认情况下@Transactional只适用于公共方法

要更改u需要从Spring AOP的Proxt切换到AspecJ代理

你可以找到更多的细节在这里 Spring AOP vs AspectJ

+0

该死,很容易修复:D非常感谢! –

相关问题