Reputation: 101
I have a scenario when I need to update a relation a detail table "USER_PROFILE" in this case and update it's relation column reference to "NATIONALITY" parent table (just a change for NATIONALITY_ID column in USER_PROFILE, and NATIONALITY table is just a predefined lookup and won't change).
So, my code sample is as follow:-
@Entity
@Table(name = "USER_PROFILE", schema = "OQ_IDEATE")
public class UserProfile {
private Long userProfileId;
private Nationality nationalityByNationalityId;
private List<UserSkills> userSkillsByUserProfileId;
@Id
@Column(name = "USER_PROFILE_ID", nullable = false)
public Long getUserProfileId() {
return userProfileId;
}
public void setUserProfileId(Long userProfileId) {
this.userProfileId = userProfileId;
}
@ManyToOne
@JoinColumn(name = "NATIONALITY_ID", referencedColumnName = "NATIONALITY_ID")
public Nationality getNationalityByNationalityId() {
return nationalityByNationalityId;
}
public void setNationalityByNationalityId(Nationality nationalityByNationalityId) {
this.nationalityByNationalityId = nationalityByNationalityId;
}
@OneToMany(mappedBy = "userProfileByUserId", cascade = CascadeType.ALL)
public List<UserSkills> getUserSkillsByUserProfileId() {
return userSkillsByUserProfileId;
}
public void setUserSkillsByUserProfileId(List<UserSkills> userSkillsByUserProfileId) {
this.userSkillsByUserProfileId = userSkillsByUserProfileId;
}
}
@Entity
public class Nationality {
private Long nationalityId;
private String nationalityNameEn;
public Nationality() {
}
public Nationality(Long nationalityId) {
this.nationalityId = nationalityId;
}
@Id
@Column(name = "NATIONALITY_ID", nullable = false)
public Long getNationalityId() {
return nationalityId;
}
public void setNationalityId(Long nationalityId) {
this.nationalityId = nationalityId;
}
@Basic
@Column(name = "NATIONALITY_EN", nullable = false, length = 200)
public String getNationalityNameEn() {
return nationalityNameEn;
}
public void setNationalityNameEn(String nationalityNameEn) {
this.nationalityNameEn = nationalityNameEn;
}
}
@Entity
@Table(name = "USER_SKILLS", schema = "OQ_IDEATE")
@IdClass(UserSkillsPK.class)
public class UserSkills {
private Long userId;
private Long skillId;
private UserProfile userProfileByUserId;
private Skill skillBySkillId;
public UserSkills() {
}
public UserSkills(UserProfile userProfile, Skill skill){
this.userProfileByUserId = userProfile;
this.skillBySkillId = skill;
this.userId = userProfile.getUserProfileId();
this.skillId = skill.getSkillId();
}
@Id
@Column(name = "USER_ID", nullable = false)
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
@Id
@Column(name = "SKILL_ID", nullable = false)
public Long getSkillId() {
return skillId;
}
public void setSkillId(Long skillId) {
this.skillId = skillId;
}
@MapsId("userId")
@ManyToOne
@JoinColumn(name = "USER_ID", referencedColumnName = "USER_PROFILE_ID", nullable = false)
public UserProfile getUserProfileByUserId() {
return userProfileByUserId;
}
public void setUserProfileByUserId(UserProfile userProfileByUserId) {
this.userProfileByUserId = userProfileByUserId;
}
@MapsId("skillId")
@ManyToOne
@JoinColumn(name = "SKILL_ID", referencedColumnName = "SKILL_ID", nullable = false)
public Skill getSkillBySkillId() {
return skillBySkillId;
}
public void setSkillBySkillId(Skill skillBySkillId) {
this.skillBySkillId = skillBySkillId;
}
}
And my spring data jpa that updates the entity is doing it's job by updating USER_PROFILE table values in DB, but I need to get the updated NATIONALITY_EN mapped value (as I changed reference column ID from 248 to 249) , I have to re-execute another SELECT statement to get the corresponding NATIONALITY_EN value. Even it's not the case for my @OneToMany mapping for List in UserProfile entity, I got the updated information without the need to re-select from the DB.
@OneToMany(mappedBy = "userProfileByUserId", cascade = CascadeType.ALL)
public List<UserSkills> getUserSkillsByUserProfileId() {
return userSkillsByUserProfileId;
}
And here is the code that responsible for updates
dbUserProfile.setUserSkillsByUserProfileId(userProfileDataPOJO.getUserSkills()
.stream()
.map(skill -> new UserSkills(dbUserProfile, new Skill(skill.getSkillId())))
.collect(Collectors.toList()));
dbUserProfile.setBusinessUnitByBusinessUnitId(new BusinessUnit(userProfileDataPOJO.getBusinessUnit().getBusUntId()));
dbUserProfile.setNationalityByNationalityId(new Nationality(userProfileDataPOJO.getNationality().getNationalityId()));
dbUserProfile = userProfileDao.save(dbUserProfile);
// after updating and getting the updated UserProfile object from the DB
dbUserProfile.getNationalityByNationalityId().getNationalityNameEn(); \\ this is always null ..
dbUserProfile.getUserSkillsByUserProfileId(); \\ this always return the full needed object
What I am asking here, is there any @annotation that will make Nationality object act the same as userSkills object? because I don't want to make another select call to the DB to load the needed value.
Thanks in advance.
Upvotes: 1
Views: 1211
Reputation: 101
So, what I did is just Querying for the Nationality through the NationalityRepo, then added the newly fetched entity and add it to UserProfile, in this way I can only got the corresponding NationalityName for the Id that I am having. Also, I was afraid that the Hibernate will do another query while validating that nationality is exists in DB again before inserting the in UserProfile, but it doesn't, thanks for Hibernate cache.
Upvotes: 1
Reputation: 311
There is an option to fetch nested objects EAGERLY, add this 'fetch = FetchType.EAGER' in your @ManyToOne annotation.
Alternatively you can use @Transactional annotations around your entity fetch method, and call the Nationality getter method within this transaction scope. That way, Hibernate will still have an open session and load the nested object.
Upvotes: 1