Reputation: 143
I'm developing a project which uses BackboneJS in front-end and Java - Spring Core in back-end. I have a problem about mapping entity(domain) objects to DTO objects. I am getting an error message like that :
org.apache.cxf.interceptor.Fault: Infinite recursion (StackOverflowError) (through reference chain: 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>();
}
Business Logic : CategoryServiceImpl.java I'm getting error in forEach loop.
@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;
}
Also Do I have to use DTO object in Presentation Layer? Can I use entity objects in presentation layer instead of DTO objects?
How can I solve this problem? Sorry my bad english. Thank you!
Upvotes: 1
Views: 1878
Reputation: 514
For those who are struggling with infinite recursion issue in Dozer.
I use mapId
to define a leaf object and stops the recursion.
Let assume we have two entities Course
and Teacher
, which contains a Many-to-Many relationship, and we want to convert the following object graph to one represented by CourseDTO
and TeacherDto
. And we hope Dozer stops at the 3rd level.
Teacher 1 ---> m Course 1 ---> m Teacher ---> ...
1st level 2nd level 3rd level
We can first define the following definition for Teacher
to TeacherDTO
conversion.
This first mapping is used for the root Teacher
entity.
Include any other fields you need in the mapping.
mapping(Teacher.class, TeacherDTO.class,
TypeMappingOptions.oneWay()
, mapNull(false)
).fields("courses", "courses");
The following mapping will prevent Dozer from going further to map the contained Course
. We define a mapId teacherLeaf
for it.
Exclude the fields that cause the infinite recursion. (In my example, it's courses)
Include any other fields you need in the mapping.
mapping(Teacher.class, TeacherDTO.class,
TypeMappingOptions.oneWay(), TypeMappingOptions.mapId("teacherLeaf")
, mapNull(false)
).exclude("courses");
The last one is the mapping rule for Course
to courseDTO
. The key is that we tell the mapper to use the teacherLeaf
mapping rule defined previously to convert the contained Teacher
s.
mapping(Course.class, CourseDTO.class,
TypeMappingOptions.oneWay()
, mapNull(false)
).fields("teachers", "teachers", useMapId("teacherLeaf"));
Hope this helps!
I use Dozer 6.1.0.
Upvotes: 1
Reputation: 3476
Please Try :
@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;
}
Upvotes: 1