You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LazyInitializationException问题:私有方法@Transactional无效求助

解决Spring中私有方法@Transactional不生效导致的LazyInitializationException问题

问题核心原因

Spring的@Transactional注解依赖AOP代理实现,默认的动态代理(JDK动态代理/CGLIB)无法拦截私有方法。当同一个类中的methodA直接调用私有方法methodB时,不会经过代理类,因此methodB上的@Transactional注解完全不生效——methodB执行时没有开启Hibernate Session,调用person.getObjects().size()时就会触发懒加载异常。

可行解决方案

方案1:调整方法可见性并通过代理调用

  1. 将methodB的可见性改为public或protected
  2. 由于同类内直接调用不会触发代理,需通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 01:47:02