Спящий режим Отложенной загрузки, прокси-серверы и наследование

StackOverflow https://stackoverflow.com/questions/8823094

Вопрос

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

В приведенном ниже коде, если вы вызываете equals() для экземпляра A, проверка завершается ошибкой в функции C.equals() при проверке, является ли объект o экземпляром C.Это не удается, потому что другой объект на самом деле является прокси-сервером гибернации, созданным javassist, который расширяет B, а не C.

Я понимаю, что Hibernate не может создать прокси-сервер типа C без обращения к базе данных и, таким образом, нарушения отложенной загрузки.Есть ли способ заставить функцию getB() в классе A возвращать конкретный экземпляр B вместо прокси (лениво)?Я пытался использовать специфичную для гибернации аннотацию @LazyToOne(LazyToOneOption.NO_PROXY) в методе getB(), но безрезультатно.

@Entity @Table(name="a")
public class A {
    private B b;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="b")
    public B getB() {
        return this.b;
    }

    public boolean equals(final Object o) {
        if (o == null) {
            return false;
        }

        if (!(o instanceof A)) {
            return false;
        }
        final A other = (A) o;
        return this.getB().equals(o.getB());
    }
}

@Entity @Table(name="b")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name="type",
    discriminatorType=DiscriminatorType.STRING
)
public abstract class B {
   private long id;

   public boolean equals(final Object obj) {
       if (this == obj) {
           return true;
       }
       if (obj == null) {
           return false;
       }
       if (!(obj instanceof B)) {
           return false;
       }
       final B b = (B) o;
       return this.getId().equals(b.getId());
    }
}

@Entity @DiscriminatorValue("c")
public class C extends B {
   private String s;

   public boolean equals(Object obj) {
       if (this == obj) {
           return true;
       }
       if (!super.equals(obj)) {
           return false;
       }
       if (obj == null) {
           return false;
       }
       if (!super.equals(obj)) {
           return false;
       }
       if (!(obj instanceof C)) {
           return false;
       }
       final C other = (C) o;
       if (this.getS() == null) {
           if (other.getS() != null) {
               return false;
           }
       } else if (!this.getS().equals(other.getS())) {
           return false;
       }
       return true;
    }
}

@Entity @DiscriminatorValue("d")
public class D extends B {
    // Other implementation of B
}
Это было полезно?

Решение

Оказывается, я был на правильном пути, пытаясь использовать аннотацию @LazyToOne(LazyToOneOption.NO_PROXY).У меня это не работало из коробки, потому что я еще не запустил на нем инструмент Hibernate bytecode enhancer.Инструкции для этого можно найти здесь:

19.1.7.Использование отложенной выборки свойств

Другие советы

Возможно, вы захотите попробовать изменить fetch свойство в getB() для FetchType.EAGER.Надеюсь, это поможет.

Независимо от того, являются ли объекты сущностями и / или загружены с задержкой, практически невозможно соблюдать контракт equals и иметь специализацию equals в подклассе, который использует instanceof.Действительно, тогда вы оказались бы в ситуации, когда у вас были бы b1.equals(c1) == true, но c1.equals(b1) == false.

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

Тем не менее, ваш метод equals в B неверен:

if (!super.equals(obj)) {
   return false;
}

Это означает, что объектная реализация equals должна возвращать true, чтобы два экземпляра B были равны.Это означает, что два экземпляра B равны только в том случае, если они являются одним и тем же объектом.

if (!(obj instanceof C)) {
    return false;
}

Почему класс B проверяет, что другой экземпляр является экземпляром C.Он должен проверить, является ли другой экземпляр экземпляром B.

Поскольку, в конце концов, две базы данных равны, если они имеют одинаковый идентификатор, и поскольку идентификаторы должны быть уникальными для всего дерева наследования, вы в безопасности, если сделаете этот метод equals окончательным.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top