JPA:多对多关系-JsonMappingException:无限递归



我在处理JPA的多对多关系时遇到了问题。我的代码如下:

传感器类别:

@Entity
@Table(name = "sensor")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Sensor {
@Id
private long chipId;
@OneToMany(mappedBy = "sensor")
@JsonBackReference
private Set<Link> userLinks;
private String firmwareVersion;
private long creationTimestamp;
private String notes;
private long lastMeasurementTimestamp;
private long lastEditTimestamp;
private double gpsLatitude;
private double gpsLongitude;
private double gpsAltitude;
private String country;
private String city;
private boolean indoor;
private boolean published;
}

用户类别:

@Entity
@Table(name = "user")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@JsonManagedReference
private int id;
private String firstName;
private String lastName;
private String email;
private String password;
@OneToMany(mappedBy = "user")
private Set<Link> sensorLinks;
private int role;
private int status;
private long creationTimestamp;
private long lastEditTimestamp;
}

链接类(关系类(:

@Entity
@Table(name = "link")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Link {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@ManyToOne
@JoinColumn(name = "user_id")
@MapsId("user_id")
private User user;
@ManyToOne
@JoinColumn(name = "sensor_id")
@MapsId("sensor_id")
private Sensor sensor;
private boolean owner;
private String name;
private int color;
private long creationTimestamp;
}

控制器:

...
@RequestMapping(method = RequestMethod.GET, path = "/user/{email}", produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "Returns details for one specific user")
public User getUserByEmail(@PathVariable("email") String email) {
return userRepository.findByEmail(email).orElse(null);
}
...

UserRepository:

public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByEmail(String email);
@Modifying
@Query("UPDATE User u SET u.firstName = ?2, u.lastName = ?3, u.password = ?4, u.role = ?5, u.status = ?6 WHERE u.id = ?1")
Integer updateUser(int id, String firstName, String lastName, String password, int role, int status);
}

我想实现的是,用户端点显示该特定用户的所有链接传感器。我得到的只是一条错误消息:

JSON映射问题:com.chillibits.particulatematterapi.model.db.main.User["sensorLinks"];嵌套异常为com.fasterxml.jackson.databind.JsonMappingException:无限递归(StackOverflowError((通过引用链:com.chillibits.particulatematterapi.model.db.main.User["sensorLinks"](

如何解决此问题?

提前感谢

Marc

------------------------------------编辑------------------------------

根据Abinash Ghosh的回答,我添加了以下DTO:

用户Dto:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDto {
private int id;
private String firstName;
private String lastName;
private Set<LinkDto> sensorLinks;
private int role;
private int status;
private long creationTimestamp;
private long lastEditTimestamp;
}

链接到:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class LinkDto {
private Integer id;
private SensorDto sensor;
private boolean owner;
private String name;
private int color;
private long creationTimestamp;
}

映射器(我意识到它有点不同,但应该是一样的(:

public UserDto getUserByEmail(@PathVariable("email") String email) {
User user = userRepository.findByEmail(email).orElse(null);
return convertToDto(user);
}
private UserDto convertToDto(User user) {
return mapper.map(user, UserDto.class);
}

这导致以下异常:

2020-04-13 14:22:24.383  WARN 8176 --- [nio-8080-exec-2] o.h.e.loading.internal.LoadContexts      : HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@68ab57c7<rs=HikariProxyResultSet@2017009664 wrapping Result set representing update count of -1>
1) Error mapping com.chillibits.particulatematterapi.model.db.main.User to com.chillibits.particulatematterapi.model.io.UserDto
1 error] with root cause
java.lang.StackOverflowError: null
at com.mysql.cj.NativeSession.execSQL(NativeSession.java:1109) ~[mysql-connector-java-8.0.19.jar:8.0.19]
...

它正在工作!这篇文章有助于:https://stackoverflow.com/a/57111004/6296634

在这种情况下,您似乎不应该使用龙目@Data

User为响应序列化时,会调用User字段的所有getter方法。因此,User关系字段sensorLinks的getter也被调用来设置值。这是递归发生的。这就是无限递归的原因。

最好不要使用实体作为响应。为User创建一个DTO类,然后将User实体值映射到DTO中,然后发送响应不要在DTO中再次使用任何Enity类,否则将导致相同的问题

为了将一个模型动态映射到另一个模型,您可以使用ModleMapper

public class UserDTO {
//Fields you want to show in response & don't use enity class 
private Set<LinkDTO> sensorLinks;
}
public class LinkDTO{
//Fields you want to show in response &don't use enity class 
}

public User getUserByEmail(@PathVariable("email") String email) {
User user = userRepository.findByEmail(email).orElse(null);
UserDTO userDto = merge(user,UserDTO.class)
return userDto;
}
public static <T> void merge(T source, T target) {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
modelMapper.map(source, target);
}

最新更新