Как я могу решить исключение stackoverflow, используя spring-boot и hibernate? - PullRequest
0 голосов
/ 21 июня 2019

Я попытался сделать успокоительный сервер API (имея логин и управление). Итак, я создал классы User и Role (на самом деле у него больше классов с той же проблемой). Проблема возникает здесь. Это создает бесконечную проблему вызова.

Я уже пытался использовать @JsonManagedReference и удалить fetch=FetchType.EAGER. Во-первых, это ошибка печати Content type 'application/json;charset=UTF-8' not supported. Я догадался, что это не сработало в моем спокойном API. Еще одна такая же ошибка, бесконечный корень.

Это мой User класс.

public class User implements UserDetails {
    @Id
    private String username;
    @Column(nullable = false)
    private String password;
    @Column(nullable = false)
    private String passwordQuestion;
    @Column(nullable = false)
    private String passwordAnswer;
    @Column(nullable = false)
    private String email;

    @ManyToMany//(fetch = FetchType.EAGER)
    @JsonManagedReference
    private Set<Role> roles = new HashSet<Role>();

    ...
}

А это мой ролевый класс.

public class Role implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    @Column(nullable=false)
    private String rolename;
    @ManyToMany(cascade=CascadeType.ALL)
    private Set<User> users = new HashSet<User>();

    public void addUser(User user) {
        users.add(user);
    }
}

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


Дополнительная Это целое сообщение об ошибке в консоли. Я добавил @JsonBackReference в users и roles и удалил @JsonManagedReference.

2019-06-21 17:29:09.667  WARN 16596 --- [nio-8080-exec-2] .c.j.MappingJackson2HttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class com.t3q.userManage.model.User]]: com.fasterxml.jackson.databind.JsonMappingException: Multiple back-reference properties with name 'defaultReference'

Это часть моего контроллера.

public class UserController {

//  private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private SiteRepository siteRepository;

    @Autowired
    private RoleRepository roleRepository;

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    JwtTokenProvider jwtTokenProvider;

    @Autowired
    PasswordEncoder passwordEncoder;

//  private CryptoUtil passwordEncoder = new CryptoUtil();

    @PostMapping("/signup/{siteURL}")
    public User signUp(@PathVariable String siteURL, @Valid @RequestBody User user) throws Exception {
        User userSaved = getUser(user.getUsername());
//      Site site = siteRepository.save(new Site(0, siteURL));
        Site site = siteRepository.findBySiteURL(siteURL);
        Role userRole = roleRepository.findByRolename("ROLE_USER");
        Role adminRole = roleRepository.findByRolename("ROLE_ADMIN");
        if (userSaved == null) {
            userSaved = userRepository.save(User.builder().username(user.getUsername())
                                                        .password(passwordEncoder.encode(user.getPassword()))
                                                        .passwordQuestion(user.getPasswordQuestion())
                                                        .passwordAnswer(user.getPasswordAnswer())
                                                        .email(user.getEmail())
                                                        .roles(new HashSet<Role>() {
                                                            {
                                                                add(userRole);
                                                                add(adminRole);
                                                            }
                                                        })
                                                        .sites(new HashSet<Site>() {
                                                            {
                                                                add(site);
                                                            }
                                                        })
                                                        .build());
            addUserToSite(userSaved, site);
            addUserToRole(userSaved, userRole);
            addUserToRole(userSaved, adminRole);
        } else {
            throw new Exception("User " + userSaved.getUsername() + " already exists.");
        }
        return userSaved;
    }
...
}

Должен ли я восстановить @JsonManagedReference?

Ответы [ 3 ]

1 голос
/ 21 июня 2019

Мой лучший совет для вас - никогда не возвращать объекты, помеченные @Entity, в качестве типа возврата вашего контроллера (даже обернутого вокруг ResponseEntity). Создание объектов передачи данных (DTO). В вашем случае это особенно важно, потому что вы не хотите раскрывать поле пароля (событие, если оно хэшировано). DTO немного увеличат стандартный код (может помочь lombok), однако, он значительно упорядочит ваш код и сделает его работу более удобной.

Создайте метод, который будет переводить вашу сущность в DTO (для извлечения), и создайте метод, который преобразует ваше DTO в сущность (для вставок и обновлений).

Вот так будет выглядеть ваша сущность:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements UserDetails {

    @Id
    private String username;
    @Column(nullable = false)
    private String password;
    @Column(nullable = false)
    private String passwordQuestion;
    @Column(nullable = false)
    private String passwordAnswer;
    @Column(nullable = false)
    private String email;

    @ManyToMany//(fetch = FetchType.EAGER)
    private Set<Role> roles = new HashSet<Role>();
}

Затем создайте свой DTO:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
class UserDto {
    private String username;
    private String password;
    private String passwordQuestion;
    private String passwordAnswer;
    private String email;
    private Set<String> roles = new HashSet<String>();
}

Наконец, создайте методы отображения в вашем UserService:

@Service
@RequiredArgsConstructor
class UserService {

    private final PasswordEncoder passwordEncoder;

    // Update which attributes you will reveal in the dto when retrieving it
    public UserDto toDto(User user) {
        return UserDto.builder()
                .username(user.getUsername())
                .passwordQuestion(user.getPasswordQuestion())
                .email(user.getEmail())
                .build();
    }

    public User toEntity(UserDto userDto) {
        return User.builder()
                .username(userDto.getUsername())
                .password(passwordEncoder.encode(userDto.getPassword()))
                .email(userDto.getEmail())
                .passwordQuestion(userDto.getPasswordQuestion())
                .passwordAnswer(userDto.getPasswordAnswer())
                .build();
    }
}
0 голосов
/ 21 июня 2019

Я решил эту проблему, используя @JsonIgnore.И я добавил тип выборки, потому что это также происходит ошибка.

@JsonIgnore
@ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
private Set<User> users = new HashSet<User>();

Это работает в моем restful API.

0 голосов
/ 21 июня 2019

Вы должны добавить JsonBackReference к пользователям. Также установите в классе Role:

@JsonBackReference
@ManyToMany(cascade=CascadeType.ALL)
private Set<User> users = new HashSet<User>();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...