I am writing application using Spring framework. I want to create dto model for my entity object and using it in spring controller. It is not difficult, but I have a relation between my table in database and I must have set in my entity object.
User entity
@Entity
@Table(name = "users")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@Column(name = "id")
private Integer id;
@NotBlank
@NotNull
@Size(min = 3, max = 40)
@Column(name = "username")
private String username;
@NotBlank
@NotNull
@Size(min = 3, max = 40)
@Column(name = "password")
private String password;
@NotBlank
@NotNull
@Size(min = 3, max = 40)
@Column(name = "firstName")
private String firstName;
@NotBlank
@NotNull
@Size(min = 3, max = 40)
@Column(name = "lastName")
private String lastName;
@Size(min = 11, max = 11)
@Column(name = "personalId")
private String personalId;
@Size(max = 40)
@Column(name = "city")
private String city;
@Size(max = 40)
@Column(name = "address")
private String address;
@NotBlank
@NotNull
@Email
@Size(max = 40)
@Column(name = "email")
private String email;
@Size(min = 9, max = 9)
@Column(name = "phone")
private String phone;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<UserRole> userRoleSet;
user_roles entity
@Entity
@Table(name = "user_roles")
public class UserRole implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@Column(name = "id")
private Integer id;
@NotNull
@Size(max = 40)
@Column(name = "name")
private String name;
@JoinColumn(name = "userId")
@ManyToOne(targetEntity = User.class)
private User user;
Table user have a relation one to many to user_role table. I create a DTO object for this entities:
userDto
public class UserDto {
private Integer id;
@NotBlank
@NotNull
@Size(min = 3, max = 40)
private String username;
@NotBlank
@NotNull
@Size(min = 3, max = 40)
private String password;
@NotBlank
@NotNull
@Size(min = 3, max = 40)
private String firstName;
@NotBlank
@NotNull
@Size(min = 3, max = 40)
private String lastName;
@Size(min = 11, max = 11)
private String personalId;
@Size(min = 3, max = 40)
private String city;
@Size(min = 3, max = 40)
private String address;
@NotBlank
@NotNull
@Email
@Size(max = 40)
private String email;
@Size(min = 9, max = 9)
private String phone;
private Set<UserRoleDto> userRoleSetDto;
userRoleDto
public class UserRoleDto {
private Integer id;
@NotNull
@Size(min = 3, max = 40)
private String name;
private UserDto userDto;
My controller which redirects to a page with a list of users
@SuppressWarnings("unchecked")
@RequestMapping(value = "/admin/adminlist", method = RequestMethod.GET)
public ModelAndView goAdminList() {
ModelAndView mav = new ModelAndView("admin/adminlist");
List<UserDto> admins = prepareUserListDto(userService.getAdminList());
mav.addObject("admins", admins);
return mav;
}
In this controller I prepare user list to userdto list, using method prepareUserListDto:
private List<UserDto> prepareUserListDto(List<User> users) {
List<UserDto> userDtoList = null;
if (users != null && !users.isEmpty()) {
userDtoList = new ArrayList<UserDto>();
UserDto userDto = null;
for (User user : users) {
userDto = new UserDto();
userDto.setId(user.getId());
userDto.setUsername(user.getUsername());
userDto.setPassword(user.getPassword());
userDto.setFirstName(user.getFirstName());
userDto.setLastName(user.getLastName());
userDto.setEmail(user.getEmail());
userDto.setUserRoleSetDto(prepareUserRoleDtoSet(user
.getUserRoleSet()));
userDtoList.add(userDto);
}
}
return userDtoList;
}
this method invoke to method to prepare userRole to userRoleDto
private Set<UserRoleDto> prepareUserRoleDtoSet(Set<UserRole> userRoles) {
Set<UserRoleDto> userRoleDtoSet = null;
if (userRoles != null && !userRoles.isEmpty()) {
userRoleDtoSet = new HashSet<UserRoleDto>();
UserRoleDto userRoleDto = null;
for (UserRole userRole : userRoles) {
userRoleDto = new UserRoleDto();
userRoleDto.setId(userRole.getId());
userRoleDto.setName(userRole.getName());
userRoleDto.setUserDto(prepareUserDto(userRole.getUser()));
userRoleDtoSet.add(userRoleDto);
}
}
return userRoleDtoSet;
}
but this method again invoke to method:
private UserDto prepareUserDto(User user) {
UserDto userDto = new UserDto();
userDto.setId(user.getId());
userDto.setUsername(user.getUsername());
userDto.setPassword(user.getPassword());
userDto.setFirstName(user.getFirstName());
userDto.setLastName(user.getLastName());
userDto.setEmail(user.getEmail());
userDto.setUserRoleSetDto(prepareUserRoleDtoSet(user.getUserRoleSet()));
return userDto;
}
In conclusion when i try show the admin list I have recursive call and i get this error:
java.lang.StackOverflowError org.hibernate.collection.internal.PersistentSet.isEmpty(PersistentSet.java:166) pl.piotr.ibank.controller.AdminController.prepareUserRoleDtoSet(AdminController.java:170) pl.piotr.ibank.controller.AdminController.prepareUserDto(AdminController.java:139) pl.piotr.ibank.controller.AdminController.prepareUserRoleDtoSet(AdminController.java:177) pl.piotr.ibank.controller.AdminController.prepareUserDto(AdminController.java:139) pl.piotr.ibank.controller.AdminController.prepareUserRoleDtoSet(AdminController.java:177) pl.piotr.ibank.controller.AdminController.prepareUserDto(AdminController.java:139) pl.piotr.ibank.controller.AdminController.prepareUserRoleDtoSet(AdminController.java:177) pl.piotr.ibank.controller.AdminController.prepareUserDto(AdminController.java:139) pl.piotr.ibank.controller.AdminController.prepareUserRoleDtoSet(AdminController.java:177) pl.piotr.ibank.controller.AdminController.prepareUserDto(AdminController.java:139)
Line 139 is userDto.setUserRoleSetDto(prepareUserRoleDtoSet(user.getUserRoleSet()));
in private UserDto prepareUserDto(User user)
method and Line 177 is private UserDto prepareUserDto(User user)
in private Set<UserRoleDto> prepareUserRoleDtoSet(Set<UserRole> userRoles)
method.
How to write dto for this entity? If it would not have a constraint this is easy, but my entity have a constraint between user and user_role and I don't know how to good solution.
A very simple solution would be to remove completely the
userDto
property from the UserRoleDto class. In contrast to the entity classes where you might need to go from one entity to another and include bi-directional relations, the client will usually only need to get the roles of a user, and never users from a role. Therefore you do not need bi-directional relations between those two dtos