Подтвердить что ты не робот

Спящий режим один к одному: getId() без выборки всего объекта

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

class Foo { 
    @OneToOne(fetch = FetchType.LAZY, optional = false)
    private Bar bar; 
}


Foo f = session.get(Foo.class, fooId);  // Hibernate fetches Foo 

f.getBar();  // Hibernate fetches full Bar object

f.getBar().getId();  // No further fetch, returns id

Я хочу, чтобы f.getBar() не вызывал другую выборку. Я хочу, чтобы hibernate дал мне прокси-объект, который позволяет мне вызвать .getId(), не выбирая объект Bar.

Что я делаю неправильно?

4b9b3361

Ответ 1

Использовать стратегию доступа

Вместо

@OneToOne(fetch=FetchType.LAZY, optional=false)
private Bar bar;

Использование

private Bar bar;

@OneToOne(fetch=FetchType.LAZY, optional=false)
public Bar getBar() {
    return this.bar;
}

Теперь он отлично работает!

Прокси инициализируется, если вы вызываете любой метод , который не является методом getter-метода идентификатора. Но он просто работает при использовании стратегии доступа к ресурсам. Имейте это в виду.

Смотрите: Hibernate 5.2 руководство пользователя

Ответ 2

Просто добавьте к Arthur Ronald FD Garcia'post: вы можете принудительно получить доступ к свойствам @Access(AccessType.PROPERTY) (или устаревший @AccessType("property")), см. http://256stuff.com/gray/docs/misc/hibernate_lazy_field_access_annotations.shtml

Другое решение может быть:

public static Integer getIdDirect(Entity entity) {
    if (entity instanceof HibernateProxy) {
        LazyInitializer lazyInitializer = ((HibernateProxy) entity).getHibernateLazyInitializer();
        if (lazyInitializer.isUninitialized()) {
            return (Integer) lazyInitializer.getIdentifier();
        }
    }
    return entity.getId();
}

Работает и для отдельных объектов.

Ответ 3

добавить @AccessType ( "свойство" )

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@AccessType("property")
protected Long id;

Ответ 4

Сохранение Java с Hibernate Book упоминает об этом в "13.1.3" Понимание прокси ":

Пока вы получаете доступ только к свойству идентификатора базы данных, нет необходима инициализация прокси. (Обратите внимание, что это неверно если вы сопоставляете свойство идентификатора с прямым доступом к полю; зимовать то даже не знает, что метод getId() существует. Если вы это называете, прокси должен быть инициализирован.)

Однако на основе ответа @xmedeko на этой странице я разработал взломы, чтобы избежать инициализации прокси-сервера даже при использовании стратегии прямого доступа к полю. Просто измените метод getId(), как показано ниже.

Вместо:

    public long getId() { return id; }

Использование:

    public final long getId() {
        if (this instanceof HibernateProxy) {
            return (long)((HibernateProxy)this).getHibernateLazyInitializer().getIdentifier();
        }
        else { return id; }
    }

Идея здесь состоит в том, чтобы пометить метод getId() как final, чтобы прокси не могли переопределить его. Затем вызов метода не может запускать какой-либо прокси-код и, следовательно, не может инициализировать прокси-сервер. Сам метод проверяет, является ли его экземпляр прокси, и в этом случае возвращает id из прокси. Если экземпляр является реальным объектом, он возвращает идентификатор.

Ответ 5

К сожалению, принятый ответ неверен. Также другие ответы не дают простейшего или четкого решения.

Используйте уровень доступа к свойствам для ID класса BAR.

@Entity
public class Bar {

    @Id
    @Access(AccessType.PROPERTY)
    private Long Id;

    ...
}

Так же просто:)

Ответ 6

В org.hibernate.Session у вас есть функция, которая выполняет работу без ленивой загрузки объекта:

public Serializable getIdentifier (объект объекта) выдает исключение HibernateException;

Найдено в спящем режиме 3.3.2.GA:

public Serializable getIdentifier(Object object) throws HibernateException {
        errorIfClosed();
        checkTransactionSynchStatus();
        if ( object instanceof HibernateProxy ) {
            LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
            if ( li.getSession() != this ) {
                throw new TransientObjectException( "The proxy was not associated with this session" );
            }
            return li.getIdentifier();
        }
        else {
            EntityEntry entry = persistenceContext.getEntry(object);
            if ( entry == null ) {
                throw new TransientObjectException( "The instance was not associated with this session" );
            }
            return entry.getId();
        }
  }

Ответ 7

В настоящее время существует библиотека данных dibatate hibernate Jackson:

https://github.com/FasterXML/jackson-datatype-hibernate

И вы можете настроить функции:

 Hibernate4Module hibernate4Module = new Hibernate4Module();
 hibernate4Module.configure(Hibernate4Module.Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true);

Это будет включать идентификатор ленивого загруженного отношения -

Ответ 8

Вы можете использовать запрос HQL. Метод getBar() действительно вернет прокси-сервер, который не будет выбран до тех пор, пока вы не вызовете некоторый метод привязки данных. Я не уверен, что именно ваша проблема. Можете ли вы дать нам еще больше фона?

Ответ 9

измените свой метод getter следующим образом:

public Bar getBar() {
    if (bar instanceof HibernateProxy) {
        HibernateProxy hibernateProxy = (HibernateProxy) this.bar;
        LazyInitializer lazyInitializer = hibernateProxy.getHibernateLazyInitializer();
        if (lazyInitializer.getSession() == null)
            bar = new Bar((long) lazyInitializer.getIdentifier());
    }

    return bar;
}