I have entities User
and Event
which have many to many
relationship to each other. If I try to return User object from Controller to REST API, it results in cyclic dependency.
I decided to use DTOs
where I would use instead of List<Event>
just List<Integer>
that would represent ids of event objects.
But if I try to create custom PropertyMap
, it does not work. Do you recommend different approach, how to do that?
Event entity
@Entity
@Table(name = "events")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Event extends AbstractEntity {
@ManyToMany(cascade = CascadeType.PERSIST)
@JoinTable(name = "event_participants",
joinColumns = @JoinColumn(name = "event_id"),
inverseJoinColumns = @JoinColumn(name = "user_id"))
private List<User> eventParticipants;
User entity
@Entity
@Table(name = "users")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class User extends AbstractEntity {
@ManyToMany(mappedBy = "eventParticipants")
private List<Event> participatingEvents;
UserController
@RestController
@RequestMapping("/users")
public class UserController extends AbstractController {
@Autowired
private UserService userService;
@Autowired
private ModelMapper modelMapper;
@RequestMapping(method = RequestMethod.GET, value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public UserDTO find(@PathVariable("id") Integer id) {
final User user = userService.find(id);
if (user == null) {
//TODO throw some exception
return null;
}
UserDTO userDTO = userToDTO(user);
return userDTO;
}
private UserDTO userToDTO(User user) {
modelMapper.addMappings(new UserMap());
return modelMapper.map(user, UserDTO.class);
}
And finally, my PropertyMap class
public class UserMap extends PropertyMap<User, UserDTO> {
@Override
protected void configure() {
map().setFirstName(source.getFirstName());
map().setLastName(source.getLastName());
map().setEmail(source.getEmail());
List<Integer> friendsDTO = new ArrayList<>();
for(User u : source.getFriends()){
friendsDTO.add(u.getId());
}
map().setFriends(friendsDTO);
List<Integer> participatingEventsDTO = new ArrayList<>();
for(Event e : source.getParticipatingEvents()){
participatingEventsDTO.add(e.getId());
}
map().setParticipatingEvents(participatingEventsDTO);
List<Integer> ownedEventsDTO = new ArrayList<>();
for(Event e : source.getOwnedEvents()){
ownedEventsDTO.add(e.getId());
}
map().setOwnedEvents(ownedEventsDTO);
List<Integer> commentsDTO = new ArrayList<>();
for(Comment c : source.getComments()){
commentsDTO.add(c.getId());
}
map().setComments(commentsDTO);
List<Integer> thingsDTO = new ArrayList<>();
for(ThingToTake t : source.getThingsToTakeList()){
thingsDTO.add(t.getId());
}
map().setThingsToTakeList(thingsDTO);
}
}
This is an exception I get if I try to GET using rest:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.modelmapper.ConfigurationException: ModelMapper configuration errors:
1) Invalid source method java.util.List.add(). Ensure that method has zero parameters and does not return void.
2) Invalid source method java.util.List.add(). Ensure that method has zero parameters and does not return void.
3) Invalid source method java.util.List.add(). Ensure that method has zero parameters and does not return void.
4) Invalid source method java.util.List.add(). Ensure that method has zero parameters and does not return void.
5) Invalid source method java.util.List.add(). Ensure that method has zero parameters and does not return void.
5 errors
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root cause
org.modelmapper.ConfigurationException: ModelMapper configuration errors:
1) Invalid source method java.util.List.add(). Ensure that method has zero parameters and does not return void.
2) Invalid source method java.util.List.add(). Ensure that method has zero parameters and does not return void.
3) Invalid source method java.util.List.add(). Ensure that method has zero parameters and does not return void.
4) Invalid source method java.util.List.add(). Ensure that method has zero parameters and does not return void.
5) Invalid source method java.util.List.add(). Ensure that method has zero parameters and does not return void.
5 errors
org.modelmapper.internal.Errors.throwConfigurationExceptionIfErrorsExist(Errors.java:241)
org.modelmapper.internal.ExplicitMappingBuilder.visitPropertyMap(ExplicitMappingBuilder.java:229)
org.modelmapper.PropertyMap.configure(PropertyMap.java:380)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:483)
org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:195)
org.modelmapper.internal.TypeMapImpl.addMappings(TypeMapImpl.java:72)
org.modelmapper.internal.TypeMapStore.getOrCreate(TypeMapStore.java:101)
org.modelmapper.ModelMapper.addMappings(ModelMapper.java:93)
com.ear.tripplan.rest.UserController.userToDTO(UserController.java:50)
com.ear.tripplan.rest.UserController.find(UserController.java:39)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:483)
org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
I had the same problem and I did it by wrapping id in another object
and you dont need any propertyMap or converter
hope it helps
UPDATED
You can do it with Converters as well
}