Following the excellent advice given in this post...
How to model a three-way relationship in a JPA Spring Boot micro service with a MySQL back end
..I have coded three Entity classes (shown below) in a Spring-boot Java application to represent the skills base of my organisation, where each user may have many skills. A 'Level' enum attribute is used to represent competence of the user for each particular skill.
I added some Controller mappings, started my application and did some basic API testing, in which I was able to successfully add skills to users and specify the additional level attribute.
Here are the Entity classes:
@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);
}
I am assigning skills to users using Repository classes representing the three Entities:
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";
}
In my view (HTML and ThymeLeaf), I am attempting to display this information. When a user has no skills, I can successfully display the user in my view. But when the user has skills, and I attempt to retrieve the skills, like this...
<tr th:each="userskill : ${userskills}">
<td th:text="${userskill.skill.name}"></td>
...I get the following error:
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?
I assumed that this was because I did not have getters and setters on my UserSkill Entity class, so I added these. But this has taken me a step back. Now, when I attempt to add a skill to a user like this...
userSkillRepository.save(userSkill);
...I enter an infinite loop.
In the raw oputput, I see this repeating seemingly infinitely:
{"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":
...
This does not happen when the getters and setters are not in my UserSkill Entity class.
As a relative newcomer to JPA, I am confused and lost at this point and would hugely appreciate some help and direction!