I have 2 Hibernate classes in a Spring-driven Application like these:
@Entity
public class Image {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@ManyToMany(mappedBy = "picture", fetch = FetchType.LAZY)
private Set<InspectionObjectDetail> inspectionObjectDetails = new HashSet<InspectionObjectDetail>();
@Override
public int hashCode() {
// Eclipse generated hashCode()
}
@Override
public boolean equals(Object obj) {
// Eclipse generated equals()
}
}
and
@Entity
public class InspectionObjectDetail {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@ManyToMany (fetch = FetchType.LAZY)
@JoinTable(
name = "MXInspectionObjectDetail_picture",
joinColumns = {@JoinColumn(name = "INSPECTIONOBJECTDETAIL_ID")},
inverseJoinColumns = {@JoinColumn(name = "PICTURE_ID")}
)
@Where(clause = "active = true")
private Set<UOCImage> picture = new HashSet<UOCImage>();
@Override
public int hashCode() {
// Eclipse generated hashCode()
}
@Override
public boolean equals(Object obj) {
// Eclipse generated equals()
}
}
I can add elements to this relation and query them just fine, but a jUnit test where I try to remove the relation without actually deleting one of these objects fails.
What I’m currently doing is unlinking each object from the other one in a transactional context:
// Junit Test
@Test
public void deleteInspectionObjectDetailImage() {
InspectionObjectDetail inspectionObjectDetail = new InspectionObjectDetail();
dao.save(inspectionObjectDetail);
someClass.saveInspectionObjectDetailImage(controlObject.getId());
//passes
assertTrue(inspectionObjectDetail.getPictures().size() == 1);
UOCImage image = inspectionObjectDetail.getPictures().iterator().next();
someClass.deleteImage(image);
Set<UOCImage> images = inspectionObjectDetail.getPictures();
//fails
assertTrue(images.size() == 0);
}
// BO Class
@Transactional
@Component
public class SomeClass() {
public void delteImage(Image image) {
if(image!= null) {
image.setActive(false);
while(image.getInspectionObjectDetails().iterator().hasNext()) {
InspectionObjectDetail inspectionObjectDetail = image.getInspectionObjectDetails().iterator().next();
inspectionObjectDetail.getPictures().remove(image);
image.getInspectionObjectDetails().remove(inspectionObjectDetail);
}
}
sessionFactory.getCurrentSession().flush();
}
}
The Hibernate Log tells me that it did an insert to the relationship to the relationship is executed correctly, my remove statements however do not appear (Even after the session.flush() ).
If possible I’d like to keep the lazy fetching the way it is, because I expect a lot of reads on the either table that won’t need the according other class loaded.
Has someone any pointers for me?
UPDATE
regarding @Thor84no ‘s suggestion, I tried to remove each association by removing it through the iterator.remove() function, so my BO Object now looks like this:
// BO Class
@Transactional
@Component
public class SomeClass() {
public void delteImage(Image image) {
if(image!= null) {
image.setActive(false);
Iterator<InspectionObjectDetail> inspectionObjectDetailsIterator = image.getInspectionObjectDetails().iterator();
while (inspectionObjectDetailsIterator.hasNext()) {
InspectionObjectDetail inspectionObjectDetail = inspectionObjectDetailsIterator.next();
Iterator<UOCImage> imageIterator = inspectionObjectDetail.getPictures().iterator();
while (imageIterator.hasNext()) {
if (imageIterator.next() == image) {
imageIterator.remove();
}
}
inspectionObjectDetailsIterator.remove();
}
}
sessionFactory.getCurrentSession().flush();
}
}
In my (edited) Junittest:
// ...
someClass.deleteImage(image.getId());
assertTrue(image.getInspectionObjectDetails().size() == 0);
assertTrue(inspectionObjectDetail.getPictures().size() == 0);
The first assertion succeeds, but the second one fails, so the bidirectional association between
Image <–> InspectionObjectDetail was only reduced to
Image <– InspectionObjectDetail
For anyone with a similar problem, the real issue was that the picture Set in
inspectionObjectDetailstill had the image in it after thedeleteImagecall. In order to most easily test whether this is the case you could either use a debugger to step through the code and check whether the Set changes or print it out before and after calling the remove method.If you find that the object is not removed, make sure you check the
hashCode()andequals()methods of the object you are removing from the Set. The Set may only usehashCode(), but do remember thathashCode()andequals()are supposed to be consistent and there’s no guarantee they ignoreequals().