I'm writing an API using Spring Boot and Hibernate where my persisted entity objects are also used as DTOs sent to and from the client. This is a simplified version of a typical entity I use:
@Entity
@Table(name = "STUDENT")
public class Student {
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@ElementCollection
@CollectionTable(name = "GROUP_STUDENT",
joinColumns = @JoinColumn(name = "GROUP_ID"))
@Column(name="STUDENT_ID")
private Set<Long> groupIds;
@JsonIgnore
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name="GROUP_STUDENT",
joinColumns = @JoinColumn(name="GROUP_ID"),
inverseJoinColumns = @JoinColumn(name="STUDENT_ID")
)
private Set<Group> groups = new HashSet<>();
// getters and setters
}
and this is the associated class:
@Entity
@Table(name = "GROUP")
public class Group {
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@JsonIgnore
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "groups")
private Set<Student> students = new HashSet<>();
// getters and setters
}
As you can see, there is a @ManyToMany
association between Student
and Group
.
Since I send objects like these to the client, I choose to send only the id's of the associations and not the associations themselves. I've solved this using this answer and it works as expected.
The problem is this. When hibernate tries to persist a Student
object, it inserts the groups
as expected, but it also tries to insert the groupIds
into the mapping table GROUP_STUDENT
. This will of course fail because of the unique constraint of the mapping table composite id. And it isn't possible to mark the groupIds
as insertable = false
since it is an @ElementCollection
. And I don't think I can use @Formula
since I require a Set
and not a reduced value.
This can of course be solved by always emptying either the groups
of the groupIds
before saving or persisting such an entity, but this is extremely risky and easy to forget.
So what I want is basically a read only groupIds
in the Student
class that loads the data from the GROUP_STUDENT
mapping table. Is this possible? I'm grateful for any suggestions and glad to ellaborate on the question if it seems unclear.
I've managed to solve this by making the id-collection @Transient
and populating it using @PostLoad
:
@Entity
@Table(name = "STUDENT")
public class Student {
@PostLoad
private void postLoad() {
groupIds = groups.stream().map(Group::getId).collect(Collectors.toSet());
}
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@Transient
private Set<Long> groupIds;
@JsonIgnore
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name="GROUP_STUDENT",
joinColumns = @JoinColumn(name="GROUP_ID"),
inverseJoinColumns = @JoinColumn(name="STUDENT_ID")
)
private Set<Group> groups = new HashSet<>();
// getters and setters
}