Hibernate one-to-one: 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 () to не вызвать другую выборку. Я хочу, чтобы hibernate дал мне прокси-объект, который позволяет мне звонить .getId () без фактического извлечения объекта Bar.

что я делаю не так?

9 53

9 ответов:

использовать стратегия доступа к собственности

вместо

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

использовать

private Bar bar;

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

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

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

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

просто чтобы добавить к Arthur Ronald F D 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();
}

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

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

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

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

пока вы обращаетесь только к свойству идентификатора базы данных, нет инициализация прокси-сервера необходима. (Обратите внимание, что это не так если вы сопоставляете свойство identifier с прямым доступом к полю; Hibernate тогда даже не знает, что метод 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 на BAR класса.

@Entity
public class Bar {

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

    ...
}

просто :)

в org.зимовать.Сеанс у вас есть функция, которая выполняет работу без ленивой загрузки сущности:

public Serializable getIdentifier (Object object) вызывает 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();
        }
  }

теперь здесь есть библиотека типов данных jackson hibernate:

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

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

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

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

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

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

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;
}