Микросервис JPA Spring Boot - бесконечный цикл при сохранении сущности с двумя сопоставлениями ManyToOne - PullRequest
0 голосов
/ 10 июня 2019

Следуя прекрасным советам, данным в этом посте ...

Как смоделировать трехсторонние отношения в микро-сервисе JPA Spring Boot с серверной частью MySQL

.. Я кодировал три класса Entity (показанных ниже) в Java-приложении Spring-boot, чтобы представить базу навыков моей организации, где у каждого пользователя может быть много навыков. Атрибут enum уровня используется для представления компетенции пользователя для каждого конкретного навыка.

Я добавил несколько сопоставлений контроллеров, запустил свое приложение и провел базовое тестирование API, в котором я смог успешно добавить навыки пользователям и указать дополнительный атрибут уровня.

Вот классы сущностей:

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false)
    private Integer id;

    private String name;

    private String email;

    @OneToMany(mappedBy = "user",
            cascade = CascadeType.MERGE,
            orphanRemoval = true
    )
    private Set<UserSkill> skills = new HashSet<>();

    (getters and setters)
@Entity
@Table(name = "skills")
public class Skill {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false)
    private Integer id;

    private String name;

    @OneToMany(mappedBy = "skill",
            cascade = CascadeType.MERGE,
            orphanRemoval = true
    )
    private Set<UserSkill> users = new HashSet<>();

    (getters and setters)
@Entity
@Table(name = "users_skills")
public class UserSkill {

    @EmbeddedId
    private UserSkillId id;

    @ManyToOne
    @JoinColumn(name = "fk_user", insertable = false, updatable = false)
    private User user;

    @ManyToOne
    @JoinColumn(name = "fk_skill", insertable = false, updatable = false)
    private Skill skill;

    @Column
    @Enumerated(EnumType.STRING)
    private Level level;

    public UserSkill() {
    }

    public UserSkill(User u, Skill s, Level l) {
        // create primary key
        this.id = new UserSkillId(u.getId(), s.getId());

        // initialize attributes
        this.user = u;
        this.skill = s;
        this.level = l;

        // update relationships to assure referential integrity
        u.getSkills().add(this);
        s.getUsers().add(this);
    }

Я назначаю навыки пользователям, использующим классы репозитория, представляющие три сущности:

        User user = userRepository.findById(userid).get();
        Skill skill= skillRepository.findById(skillid).get();
        Level level = Level.valueOf(levelid);

        UserSkill userSkill = new UserSkill(user, skill, level);
        userSkillRepository.save(userSkill);
In my Controller, I have a mapping to retrieve the user and associated skills and add this to my Model:

    @GetMapping("/user/{userid}/skills/get")
    public String getUserSkills(@PathVariable("userid") Integer userid, Model model) {

        User user = userRepository.findById(userid).get();

        model.addAttribute("user", user);
        Set<UserSkill> userSkills = userSkillRepository.findAllByUser(user);
        model.addAttribute("userskills", userSkills);

        return "update-user";
    }

На мой взгляд (HTML и ThymeLeaf) я пытаюсь отобразить эту информацию. Когда у пользователя нет навыков, я могу успешно отобразить пользователя в своем представлении. Но когда у пользователя есть навыки, и я пытаюсь получить навыки, как это ...

<tr th:each="userskill : ${userskills}">
  <td th:text="${userskill.skill.name}"></td>

... Я получаю следующую ошибку:

org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'skill' cannot be found on object of type 'uk.gov.hmrc.skills.model.UserSkill' - maybe not public or not valid?

Я предположил, что это потому, что в моем классе UserSkill Entity не было геттеров и сеттеров, поэтому я добавил их. Но это сделало меня шаг назад. Теперь, когда я пытаюсь добавить навык для пользователя, как этот ...

        userSkillRepository.save(userSkill);

... Я вхожу в бесконечный цикл.

В необработанном выводе я вижу, что это повторяется, казалось бы, бесконечно:

{"id":1,"name":"Dave","email":"dave@test.com","skills":[{"user":
{"id":1,"name":"Dave","email":"dave@test.com","skills":[{"user":
{"id":1,"name":"Dave","email":"dave@test.com","skills":[{"user":
...

Этого не происходит, когда геттеры и сеттеры не входят в мой класс UserSkill Entity.

Как относительный новичок в JPA, я растерялся и растерялся на этом этапе и был бы очень признателен за помощь и руководство!

1 Ответ

2 голосов
/ 10 июня 2019

Проблема известна как круговые ссылки при использовании пружинного упора JSON.

Чтобы исправить это, добавьте аннотацию @JsonIgnoreProperties ("навыки") в user Поле в UserSkill классе (или @JsonIgnore, чтобы игнорировать поле при возврате результата json).

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@Entity
@Table(name = "users_skills")
public class UserSkill {

    @EmbeddedId
    private UserSkillId id;

    @ManyToOne
    @JoinColumn(name = "fk_user", insertable = false, updatable = false)
    @JsonIgnoreProperties("skills")
    private User user;

    @ManyToOne
    @JoinColumn(name = "fk_skill", insertable = false, updatable = false)
    @JsonIgnoreProperties("users")
    private Skill skill;

    @Column
    @Enumerated(EnumType.STRING)
    private Level level;

        public UserSkill() {
        }

        ...
}

https://hellokoding.com/handling-circular-reference-of-jpa-hibernate-bidirectional-entity-relationships-with-jackson-jsonignoreproperties/

2-е решение Использование Jackons JSON Views

https://www.baeldung.com/jackson-json-view-annotation

3-е решение Использование шаблона DTO

https://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...