Hi I have 2 entities: Department and User.
I find both entities and want to add one to the other But if I add a user to the department, the user has Department = null. And if the opposite is true, then List = null. How to do this correctly?
@Entity
@Table(name = "department")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Department {
@Id
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@GeneratedValue(strategy = GenerationType.IDENTITY, generator = "uuid2")
@Column(length = 36, nullable = false, updatable = false)
private UUID id;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "description", nullable = false)
private String description;
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
private List<User> users = new ArrayList<>();
}
@Entity
@Table(name = "user")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
@Id
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@GeneratedValue(strategy = GenerationType.IDENTITY, generator = "uuid2")
@Column(length = 36, nullable = false, updatable = false)
private UUID id;
private String firstName;
private String lastName;
private Integer age;
@ManyToOne
private Department department;
@ManyToMany
@JoinTable(
name = "device_devices",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "device_id"))
Set<Device> devices = new HashSet<>();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equal(id, user.id);
}
@Override
public int hashCode() {
return Objects.hashCode(id);
}
}
And service
@Override
@Transactional
public void addUserToDepartment(UUID departmentId,UUID userId){
Department department = departmentRepository.findById(departmentId).orElseThrow(DepartmentNotFoundException::new);
User user = userRepository.findById(userId).orElseThrow(UserNotFoundException::new);
department.getUsers().add(user);
//user.setDepartment(department);
}
It seems to me that adding user to department and simultaneously adding department to user is not correct. They have to pull themselves up. Help Me
That is why you have mappedBy
attribute. It hints to hibernate what side will be responsible for those mappings when you have a bi-directional ascosiation.
So considering your configuration
public class Department {
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
private List<User> users = new ArrayList<>();
...
}
Then setting the mapping only on User on field department would be enough
@Override
@Transactional
public void addUserToDepartment(UUID departmentId,UUID userId){
Department department = departmentRepository.findById(departmentId).orElseThrow(DepartmentNotFoundException::new);
User user = userRepository.findById(userId).orElseThrow(UserNotFoundException::new);
//department.getUsers().add(user); You don't need this.
user.setDepartment(department); This here is enough for hibernate!
}
as pointed out by hibernate documentation in order to have normal java usage inside a method you need to manually update both sides. This means that if you update only 1 side then the changes will be persisted on DB level but on java level your other instance will not be synced with those changes.
Check the following examples on hibernate documentation (Example 297, Example 298) Hibernate Bi directional mapping normal java usage