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

Posted

技术标签:

【中文标题】JPA:多对多关系 - JsonMappingException:无限递归【英文标题】:JPA: Many to many relationship - JsonMappingException: Infinite recursion 【发布时间】:2020-07-25 22:24:34 【问题描述】:

我在与 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;

还有Link类(关系类):

@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);


...

用户存储库:

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:无限 递归(***Error)(通过引用链: com.chillibits.particulatematterapi.model.db.main.User["sensorLinks"])

我该如何解决这个问题?

提前致谢

马克

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

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

UserDto:

@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.***Error: null
at com.mysql.cj.NativeSession.execSQL(NativeSession.java:1109) ~[mysql-connector-java-8.0.19.jar:8.0.19]
...

【问题讨论】:

添加您的控制器和服务也可以找出您的问题 这能回答你的问题吗? Infinite Recursion with Jackson JSON and Hibernate JPA issue 我已经尝试过@JsonIgnore。当我为两者(sensorLinks 和 userLinks)添加它时,它可以工作。我还尝试将@JsonIgnore 仅添加到Sensor.class 中的userLinks,这不起作用。 从 LinkDTO 中删除 private Sensor sensor;。不要在 DTO 中再次使用任何实体类,否则会导致同样的问题 【参考方案1】:

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

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

【讨论】:

【参考方案2】:

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);
  

【讨论】:

那也行不通。我在上面的问题中添加了更改。我做错了什么? 从 LinkDTO 中删除 private Sensor sensor;,或者如果您需要这个,请创建另一个 DTO 如果您在 dto 中使用实体,则会再次发生递归获取 我解决了!见我上面的回答。确实,感谢您的帮助! 您是否尝试删除 private Sensor sensor; 这个?它也将起作用。使用 lombok 在开发中非常安全

以上是关于JPA:多对多关系 - JsonMappingException:无限递归的主要内容,如果未能解决你的问题,请参考以下文章

多对多关系的jpa标准

多对多关系 JPA 与实体

多对多关系中的 JPA 条件查询

春季 JPA |在多对多关系中搜索

JPA多对多关系:无法插入生成的表

JPA+HIBERNATE:多对多关系中的笛卡尔积