Спящий режим Отложенной загрузки, прокси-серверы и наследование
-
27-10-2019 - |
Вопрос
У меня проблема с отложенной загрузкой в режиме гибернации при работе с наследованием.У меня есть одна сущность, которая ссылается на вторую сущность, которая является подклассом.Я хочу, чтобы ссылка загружалась лениво, но это вызывает ошибки в моих методах .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.Инструкции для этого можно найти здесь:
Другие советы
Возможно, вы захотите попробовать изменить 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 окончательным.