2012-01-11 3 views
3

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

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

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

ответ

1

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

19.1.7. Using lazy property fetching

+0

Существует опция использования NO_PROXY и инструментария. Вы можете открыть «это» вне объекта и использовать его для проверки instanceof. В «этом» у вас всегда будет развернутый и уже инициализированный объект. Вот мое подробное решение http://lifeinide.com/post/2017-05-28-hibernate-single-side-associations-lazy-fetch/ –

0

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

+0

Это было бы сделать это, но я хотел бы это поле, чтобы быть ленивым загружен. – sutlermb

0

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

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

Тем не менее, ваш метод равно в B не является правильным:

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

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

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

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

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

+0

Ошибки в равных функциях были ошибками типа-o/copy-paste. Я исправил их. Я пытаюсь использовать эти объекты как при сохранении, так и при не сохраняющемся, поэтому я хочу, чтобы равный мне сказать, равны ли объекты, а не только то, что идентификаторы равны. – sutlermb

+0

Ну, тогда вам нужно иметь функциональный идентификатор в вашем субъекте B. Этот функциональный идентификатор может быть возвращен защищенным абстрактным методом: return getFunctionalId(). Equals (b.getFunctionalId()). Но он также должен быть уникальным среди всего дерева наследования. –

+0

Я просто использовал equals() в качестве примера. Я столкнусь с той же проблемой, если мне нужно нарисовать a.getB(), чтобы набрать C во внешнем классе.Возникает вопрос: могу ли я получить конкретный тип B из getB(), а не прокси. – sutlermb