【问题标题】:Infinite Loop When Collection Mapping With Dozer使用 Dozer 进行集合映射时的无限循环
【发布时间】:2015-10-01 19:45:24
【问题描述】:

我正在开发一个项目,它在前端使用 BackboneJS,在后端使用 Java - Spring Core。我有一个关于将实体(域)对象映射到 DTO 对象的问题。我收到这样的错误消息:

org.apache.cxf.interceptor.Fault:无限递归(StackOverflowError)(通过引用链:com.countdown.dto.CategoryDTO["countdownList"]->java.util.ArrayList[0]->com.countdown .dto.CountdownDTO["category"]->.......

User.java

@Entity
@Table(name = "Users")
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "USER_ID", nullable = false)
    private int id;

    @Column(name = "EMAIL", nullable = false, unique = true)
    private String email;

    @Column(name = "NAME_SURNAME", nullable = false)
    private String nameSurname;

    @Column(name = "PASSWORD", nullable = false)
    private String password;

    @Column(name = "USERNAME", nullable = false, unique = true)
    private String username;

    @Column(name = "REGISTER_DATE", nullable = false)
    private Date registerDate;

    @ManyToOne
    @JoinColumn(name = "ROLE_ID")
    private Role role;

    @OneToMany(mappedBy = "createUser")
    private List<Countdown> createCountdownList = new ArrayList<Countdown>();

    @OneToMany(mappedBy = "updateUser")
    private List<Countdown> updateCountdownList = new ArrayList<Countdown>();

    @ManyToMany
    @JoinTable(name = "FOLLOWINGS",
            joinColumns = @JoinColumn(name = "USER_ID"),
            inverseJoinColumns = @JoinColumn(name = "COUNTDOWN_ID"))
    private List<Countdown> followings = new ArrayList<Countdown>();

    //Getters and setters..

}

Role.java

@Entity
public class Role implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ROLE_ID")
    private int id;

    @Column(name = "ROLE_NAME", nullable = false)
    private String roleName;

    @OneToMany(mappedBy = "role",fetch = FetchType.LAZY)
    List<User> userList = new ArrayList<User>();
 }

Countdown.java

@Entity
@Table(name = "COUNTDOWN")
public class Countdown implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "COUNTDOWN_ID")
    private int id;

    @Column(name = "COUNTDOWN_NAME", nullable = false)
    private String countdownName;

    @Column(name = "COUNTDOWN_DATE", nullable = false)
    private Date countdownDate;

    @Column(columnDefinition = "varchar(5000)")
    private String countdownDescription;

    @JoinColumn(name = "CATEGORY_ID", nullable = false)
    @ManyToOne
    private Category category;

    @JoinColumn(name = "CREATE_USER", nullable = false)
    @ManyToOne
    private User createUser;

    @Column(name = "CREATE_DATE", nullable = false)
    private Date createDate;

    @JoinColumn(name = "UPDATE_USER", nullable = false)
    @ManyToOne
    private User updateUser;

    @Column(name = "UPDATE_DATE", nullable = false)
    private Date updateDate;

    @Column(name = "CREATE_USER_IP", nullable = false)
    private int createIP;

    @ManyToMany
    private List<User> followers = new ArrayList<User>();

}

Category.java

@Entity
@Table(name="CATEGORY")
public class Category implements Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="CATEGORY_ID")
    private int id;

    @Column(name = "CATEGORY_NAME" , nullable = false)
    private String categoryName;

    @OneToMany(mappedBy = "category")
    private List<Countdown> countdownList = new ArrayList<Countdown>();

}

业务逻辑:CategoryServiceImpl.java 我在 forEach 循环中遇到错误。

@Transactional(readOnly = true)
public List<CategoryDTO> getAllCategories() {
    List<Category> categoryList;
    List<CategoryDTO> categoryDTOList = new ArrayList<CategoryDTO>();

    logger.debug("getAllCategories called");

    try {
        categoryList = categoryDAO.findAll();
        for(Category category : categoryList){
            categoryDTOList.add(mapper.map(category,CategoryDTO.class));
        }
    }catch (NoResultException e){
        logger.error("getAllCategories method : No Category wasn't found");
        logger.warn(e,e);
    }catch (Exception e){
        logger.error("getAllCategories method : Categories wasn't found");
        logger.warn(e,e);
    }
    return categoryDTOList;
}

我还必须在表示层中使用 DTO 对象吗?我可以在表示层中使用实体对象而不是 DTO 对象吗?

我该如何解决这个问题?对不起我的英语不好。谢谢!

【问题讨论】:

  • 如果是,请接受/投票

标签: java collections mapping infinite-loop dozer


【解决方案1】:

请尝试:

    @Transactional(readOnly = true)
    public List<CategoryDTO> getAllCategories() {
        List<Category> categoryList;
        List<CategoryDTO> categoryDTOList = new ArrayList<CategoryDTO>();

        logger.debug("getAllCategories called");

        try {
            categoryList = categoryDAO.findAll();

            for(Category category : categoryList){
                  if(category.getCountdownList() != null && !category.getCountdownList().isEmpty()){
                      for(Countdown countdown : category.getCountdownList()){
                          countdown.setCategory(null);
                      }
                   }
                categoryDTOList.add(mapper.map(category,CategoryDTO.class));
            }
        }catch (NoResultException e){
            logger.error("getAllCategories method : Hata: No Category wasn't found");
            logger.warn(e,e);
        }catch (Exception e){
            logger.error("getAllCategories method : Hata: Categories wasn't found");
            logger.warn(e,e);
        }
        return categoryDTOList;
    }

【讨论】:

  • 感谢您的回答,但在 if 语句中 IDE 无法识别类别对象。 imgur.com/IGFJN6t
  • 我试过了,但我现在收到了这个错误: 原因:org.postgresql.util.PSQLException:错误:“category_id”列中的空值违反了非空约束。我认为我们将 null 设置为 id 列。
  • 请写堆栈跟踪?
  • 我无法理解。我调试了项目。似乎没有问题。然后我删除了 Transactional(readOnly=True) 行。现在我得到了 LazyInitialization 异常:( paste.ubuntu.com/11872966
【解决方案2】:

对于那些在 Dozer 中遇到无限递归问题的人。

我使用mapId 定义一个叶对象并停止递归。

假设我们有两个实体CourseTeacher,其中包含多对多关系,我们想将下面的对象图转换为CourseDTO表示的对象图和TeacherDto。我们希望 Dozer 在第 3 级停止。

Teacher 1 ---> m Course 1 ---> m Teacher ---> ...
1st level        2nd level       3rd level

对于TeacherTeacherDTO的转换,我们可以先定义如下定义。

第一个映射用于根 Teacher 实体。 在映射中包含您需要的任何其他字段。

mapping(Teacher.class, TeacherDTO.class,
                    TypeMappingOptions.oneWay()
                    , mapNull(false)
            ).fields("courses", "courses");

以下映射将阻止 Dozer 进一步映射包含的 Course。我们为它定义了一个 mapId teacherLeaf。 排除导致无限递归的字段。 (在我的示例中,它是 课程) 在映射中包含您需要的任何其他字段。

                mapping(Teacher.class, TeacherDTO.class,
                    TypeMappingOptions.oneWay(), TypeMappingOptions.mapId("teacherLeaf")
                    , mapNull(false)
            ).exclude("courses");

最后一个是CoursecourseDTO的映射规则。关键是我们告诉mapper使用之前定义的teacherLeaf映射规则来转换包含的Teachers。

                mapping(Course.class, CourseDTO.class,
                    TypeMappingOptions.oneWay()
                    , mapNull(false)
            ).fields("teachers", "teachers", useMapId("teacherLeaf"));

希望这会有所帮助!

我使用推土机 6.1.0。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-06
    • 2011-07-06
    • 2018-04-19
    • 1970-01-01
    • 1970-01-01
    • 2012-11-03
    相关资源
    最近更新 更多