LazyInitializationException问题:私有方法@Transactional无效求助
解决Spring中私有方法@Transactional不生效导致的LazyInitializationException问题
问题核心原因
Spring的@Transactional注解依赖AOP代理实现,默认的动态代理(JDK动态代理/CGLIB)无法拦截私有方法。当同一个类中的methodA直接调用私有方法methodB时,不会经过代理类,因此methodB上的@Transactional注解完全不生效——methodB执行时没有开启Hibernate Session,调用person.getObjects().size()时就会触发懒加载异常。
可行解决方案
方案1:调整方法可见性并通过代理调用
- 将
methodB的可见性改为public或protected - 由于同类内直接调用不会触发代理,需通过Spring代理对象调用
methodB:
方式1:注入自身代理对象
@Service public class PersonService { @Autowired private PersonRepository personRepository; @Autowired private PersonService selfProxy; // 注入自身代理 public List<PersonDTO> methodA() { List<PersonDTO> personDTOList = selfProxy.methodB(); // 通过代理调用 methodC(personDTOList); return personDTOList; } @Transactional public List<PersonDTO> methodB() { // 修改为public List<PersonDTO> personDTOList = new ArrayList<>(); List<Person> personList = personRepository.findAll(); for (Person person : personList) { int objectSize = methodD(person); if (objectSize < 50) { PersonDTO personDTO = new PersonDTO(); personDTO.setName(person.getName()); personDTO.setAge(person.getAge()); personDTOList.add(personDTO); } } return personDTOList; } private int methodD(Person person) { return person.getObjects().size(); } private void methodC(List<PersonDTO> personDTOList) { for (PersonDTO personDTO : personDTOList) { System.out.println("Name: " + personDTO.getName() + ", Age: " + personDTO.getAge()); } } }
方式2:使用AopContext获取当前代理
// 启动类添加注解:@EnableAspectJAutoProxy(exposeProxy = true) public List<PersonDTO> methodA() { List<PersonDTO> personDTOList = ((PersonService) AopContext.currentProxy()).methodB(); methodC(personDTOList); return personDTOList; }
方案2:Repository层通过Fetch Join提前加载集合
直接在查询阶段加载objects集合,避免依赖事务懒加载:
// 修改PersonRepository public interface PersonRepository extends JpaRepository<Person, Long> { @Query("SELECT p FROM Person p JOIN FETCH p.objects") List<Person> findAllWithObjects(); }
// 修改methodB调用新方法 private List<PersonDTO> methodB() { List<PersonDTO> personDTOList = new ArrayList<>(); List<Person> personList = personRepository.findAllWithObjects(); // 提前加载集合 for (Person person : personList) { int objectSize = methodD(person); if (objectSize < 50) { PersonDTO personDTO = new PersonDTO(); personDTO.setName(person.getName()); personDTO.setAge(person.getAge()); personDTOList.add(personDTO); } } return personDTOList; }
方案3:使用TransactionTemplate手动管理事务
通过编程式事务替代注解式事务,无需修改方法可见性:
@Service public class PersonService { @Autowired private PersonRepository personRepository; @Autowired private TransactionTemplate transactionTemplate; public List<PersonDTO> methodA() { List<PersonDTO> personDTOList = methodB(); methodC(personDTOList); return personDTOList; } private List<PersonDTO> methodB() { // 手动执行事务逻辑 return transactionTemplate.execute(status -> { List<PersonDTO> personDTOList = new ArrayList<>(); List<Person> personList = personRepository.findAll(); for (Person person : personList) { int objectSize = methodD(person); if (objectSize < 50) { PersonDTO personDTO = new PersonDTO(); personDTO.setName(person.getName()); personDTO.setAge(person.getAge()); personDTOList.add(personDTO); } } return personDTOList; }); } private int methodD(Person person) { return person.getObjects().size(); } private void methodC(List<PersonDTO> personDTOList) { for (PersonDTO personDTO : personDTOList) { System.out.println("Name: " + personDTO.getName() + ", Age: " + personDTO.getAge()); } } }
内容的提问来源于stack exchange,提问作者Varox
相关产品推荐
相关产品推荐

