微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

由于循环问题,Mapstruct 的 LazyInitializationException

如何解决由于循环问题,Mapstruct 的 LazyInitializationException

我有一个开发项目,使用 Spring Data JPAMapStruct 在实体和 DTO 之间进行映射。上周我决定是时候解决我推迟了一段时间的 FetchType.EAGERLAZY 问题。我选择在需要时使用 @NamedEntityGraph@EntityGraph 来加载属性。但是,在进行从实体到 dto 的映射时,我遇到了这个 LazyInitializationExeption 问题。我想我知道这是在哪里发生的,但我不知道如何通过它。

代码

@NamedEntityGraph(name="Employee.full",...)
@Entity
public class Employee {
  private Set<Role> roles = new HashSet<>();
}

@Entity
public class Role {
  private Set<Employee> employees = new HashSet<>();
}

public interface EmployeeRepository extends JpaRepository<Employee,Long> {
  @EntityGraph(value = "Employee.full")
  @Override
  Page<Employee> findAll(Pageable pageable);
}

@Service
public class EmployeeService {
  public Page<EmployeeDTO> findAll(PageRequest pageRequest) {
    Page<Employee> employees = repository.findAll(pageRequest); // ok
    Page<EmployeeDTO> dtos = employees.map(emp -> mapper.toDTO(emp,new CycleAvoidMappingContext()); // this is where the exception happens
    return dtos;
  }
}

// also there is EmployeeDTO and RoleDTO classes mirroring the entity classes 
// and there is a simple interface EmployeeMapper loaded as a spring component 
// without any special mappings. However CycleAvoidingMappingContext is used.

我已经追踪到 LazyInitializationException 在映射器尝试映射角色依赖项时发生的情况。 Role 对象确实有 Set<Employee>,因此存在循环引用。

使用 FetchType.EAGERnew CycleAvoidingMappingContext() 解决了这个问题,但使用 LAZY 这不再有效。

有谁知道如何避免异常并同时正确映射我的 DTO?

解决方法

问题是当代码从 findAll 返回时,实体不再被管理。因此,您有一个 LazyInitializationException,因为您正在尝试在会话范围之外访问尚未初始化的集合

添加eager使其工作,因为它确保集合已经被初始化。

您有两种选择:

  1. 使用 EAGER 提取;
  2. 确保在您从 findAll 返回时实体仍处于管理状态。添加@Transactional的方法应该工作:
    @Service
    public class EmployeeService {
    
        @Transactional
        public Page<EmployeeDTO> findAll(PageRequest pageRequest) {
            Page<Employee> employees = repository.findAll(pageRequest);
            Page<EmployeeDTO> dtos = employees.map(emp -> mapper.toDTO(emp,new CycleAvoidMappingContext());
            return dtos;
        }
     }
    

我想说的是,如果您需要初始化集合,那么急切地(使用实体图或查询)获取它是有意义的。

查看这篇文章以了解有关 entities states in Hibernate ORM 的更多详细信息。

UPDATE:发生此错误的原因似乎是 Mapstruct 正在转换集合,即使您在 DTO 中不需要它。 在这种情况下,您有不同的选择:

  1. 从 DTO 中删除字段 roles。 Mapstruct 会忽略实体中的字段,因为 DTO 没有同名字段;
  2. 为这个特定案例创建一个不同的 DTO 类,没有字段 roles;
  3. 使用 @Mapping 注释忽略实体中的字段:
    @Mapping(target = "roles",ignore = true)
    void toDTO(...)
    
    或者,如果您有时需要 toDTO 方法
    @Mapping(target = "roles",ignore = true)
    void toSkipRolesDTO(...) // same signature as toDTO
    

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。