JPA: отношения многие ко многим - JsonMappingException: бесконечная рекурсия - PullRequest
0 голосов
/ 13 апреля 2020

У меня проблемы с отношениями многие ко многим с 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);
}

...

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.faster xml .jackson.databind.JsonMappingException: бесконечная рекурсия (StackOverflowError) (через цепочку ссылок: com.chillibits.particulatematterapi.model.db.main.User ["sensorLinks"]))

Как я могу решить эту проблему?

Заранее спасибо

Март c

-------------- ---------------------- Редактировать --------------------------- --------

Согласно ответу Абины sh Gho sh, я добавил следующие 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;
}

LinkDto:

@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]
...

Ответы [ 2 ]

0 голосов
/ 13 апреля 2020

Это работает! Помогло это сообщение: { ссылка }

Похоже, вам не следует использовать Lombok @Data в таких случаях.

0 голосов
/ 13 апреля 2020

Когда User сериализуется для ответа, вызываются все методы получения полей User. Таким образом, User геттерное поле sensorLinks также вызывается для установки значения. Это произошло рекурсивно. Это причина бесконечной рекурсии.

Лучше не использовать Entity в качестве ответа. Создайте класс DTO для User, затем сопоставьте User значение сущности с DTO, а затем отправьте ответ. Не используйте класс Enity снова в DTO, тогда это приведет к той же проблеме

Для динамического отображения одной модели на другую вы можете использовать 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);
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...