Как удалить ассоциацию «многие ко многим» в Hibernate без фактического удаления объектов - PullRequest
1 голос
/ 31 октября 2011

У меня есть 2 класса Hibernate в приложении на основе Spring, таких как:

@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()
 }
}

и

@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()
 }
}

Я могу добавить элементы в это отношение и просто запросить их, ноjUnit-тест, в котором я пытаюсь удалить отношение без фактического удаления одного из этих объектов, завершается неудачей.

В настоящее время я делаю отсоединение каждого объекта от другого в контексте транзакции:

// 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();
     }

}

Журнал Hibernate сообщает мне, что вставка в отношение к связи выполнена правильно, однако мои операторы удаления выполненыне появляются (даже после сессии.flush ()).Если возможно, я бы хотел продолжать ленивую выборку так, как она есть, потому что я ожидаю много чтений по любой из таблиц, которые не будут загружаться в соответствии с другим загруженным классом.

У кого-то есть указатели для меня?

UPDATE

относительно предложения @ Thor84no, я попытался удалить каждую ассоциацию, удалив ее через функцию iterator.remove(), поэтому мой объект BO теперь выглядит так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();
}

}

В моем (отредактированном) Junittest:

    // ...
    someClass.deleteImage(image.getId());
    assertTrue(image.getInspectionObjectDetails().size() == 0);
    assertTrue(inspectionObjectDetail.getPictures().size() == 0);

Первое утверждение успешно, но второе - неудачно, поэтомудвунаправленная связь между

Image <-> InspectionObjectDetail была уменьшена только доImage <- <code>InspectionObjectDetail

Ответы [ 3 ]

1 голос
/ 01 ноября 2011

Для тех, кто сталкивался с подобной проблемой, реальная проблема заключалась в том, что картинка, установленная в inspectionObjectDetail, все еще имела изображение после вызова deleteImage.Чтобы проще всего проверить, так ли это, вы можете использовать отладчик для пошагового выполнения кода и проверить, изменяется ли Set, или распечатать его до и после вызова метода удаления.

Если вы обнаружите, чтообъект не удален, обязательно проверьте методы hashCode() и equals() объекта, который вы удаляете из набора.Набор может использовать только hashCode(), но помните, что hashCode() и equals() должны быть согласованными, и нет гарантии, что они проигнорируют equals().

1 голос
/ 31 октября 2011
while(image.getInspectionObjectDetails().iterator().hasNext()) {
    InspectionObjectDetail inspectionObjectDetail = image.getInspectionObjectDetails().iterator().next();
    inspectionObjectDetail.getPictures().remove(image);
    image.getInspectionObjectDetails().remove(inspectionObjectDetail);
}

Знаете ли вы, что вызов iterator () в списке создает новый Iterator при каждом его вызове?Я полагаю, ваша проблема (или, по крайней мере, одна из них) в том, что вы находитесь здесь в бесконечном цикле.Хотя while будет всегда истинно, пока image.getInspectionObjectDetails() имеет хотя бы 1 элемент, а также image.getInspectionObjectDetails().iterator().next() всегда будет возвращать один и тот же (первый) элемент.

Попробуйте что-то вроде этого:

Iterator<InspectionObjectDetail> it = image.getInspectionObjectDetails().iterator();
while (it.hasNext()
    InspectionObjectDetail detail = it.next();
    detail.getPictures().remove(image);
    it.remove();
}

ОБНОВЛЕНИЕ:

if (imageIterator.next() == image)

Никогда не сравнивайте подобные объекты в Java;вместо этого используйте equals и внедрите equals / hashCode в вашей сущности.== просто проверит, имеют ли оба операнда одну и ту же ссылку, так что это может привести к значению false, хотя логически это одна и та же сущность (один и тот же бизнес-ключ): http://leepoint.net/notes-java/data/expressions/22compareobjects.html

После этого вы можете переписать:

Iterator<UOCImage> imageIterator = inspectionObjectDetail.getPictures().iterator();
            while (imageIterator.hasNext()) {
                if (imageIterator.next() == image) {
                    imageIterator.remove();
                }
            }

как:

inspectionObjectDetail.getPictures().remove(image);
0 голосов
/ 01 ноября 2011

Правило большого пальца при удалении ассоциации - очистить Коллекции в задействованных Java-объектах и ​​затем сохранить объекты.

Таким образом, если класс A имеет коллекцию класса B, а класс B имеет коллекцию класса A (случай отношения многих ко многим), тогда просто очистите коллекцию

  • Класс B в классе A
  • Класс A в классе B

и сохраните класс A и класс B. Отношение будет удалено, но объекты останутся.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...