Having unidirectional ManyToMany mapping in an entity called A to entity called B on field called files:
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = JOIN_TABLE_NAME)
private List<B> files = new ArrayList<>();
, which creates a join table with ids of each entity as composite primary key.
Automatically, when an item is removed from files list and A entity is saved, a row is also removed from join table.
I would like to implement this behavior:
Whenever a row is removed from this join table, check if there are no more rows with B-id as removed row B-id, if so, remove rows from B table matching that B-id.
There might be several ways to implement this. Some of which i have tought, but are unsatisfactory or do not work:
Create join table as separate entity and add a post remove method. However this method is not called:
@Entity
@PrimaryKeyJoinColumns(
@PrimaryKeyJoinColumn(name = "a_id"),
@PrimaryKeyJoinColumn(name = "b_id")
)
@Table(name = JOIN_TABLE_NAME)
public class ABjoinTable {
@EmbeddedId
private ABId abid;
@Embeddable
public static final class ABId {
@Column(name = "a_id);
public long aid;
@Column(name = "b_id);
public long bid;
}
@PostRemove
public void removeDanglingFiles() {
// not called
}
}
Create a remove trigger on join table, however B entity has
PostRemove method already implemented, which in this solution will
not be called (trigger is on db level).
Use PostUpdate method on A entity and remove dangling rows manually, however rows are not yet removed from join table in post update context:
@PostLoad
public void createFilesSnapshot() {
filesSnapshot = new ArrayList<>(this.files);
}
@PostUpdate
public void removeDanglingFiles() {
// if there are no more references to other Bids, delete that B
val removed = filesSnapshot.stream()
.filter(b -> !this.files.contains(b))
.map(PersistentObject::getId)
.collect(Collectors.toSet());
try (val em = JPAutil.getEntityManager()) {
val tx = em.getTransaction();
tx.begin();
for (val rId : removed) {
val q = em.createQuery("SELECT a" +
" FROM A a JOIN a.files b" +
" WHERE b.id = :bId", B.class);
q.setParameter("bId", rId);
val result = q.getResultList();
if (result.size() == 0) { // at this point in post load, join table row is not yet removed, so size is >= 1
em.remove(...);
}
}
tx.commit();
} catch (final Exception e) {
}
}
Using EclipseLink jpa provider and MySql db.