Hibernate一对一:getId()而不提取整个对象

时间:2010-04-07 15:27:14

标签: java hibernate jpa

我想在不加载整个对象的情况下获取一对一关系的id。我想我可以使用延迟加载执行此操作,如下所示:

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对象。

我做错了什么?

9 个答案:

答案 0 :(得分:38)

使用属性访问策略

而不是

@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 user guide

答案 1 :(得分:27)

添加到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();
}

也适用于分离的实体。

答案 2 :(得分:14)

添加 @AccessType("属性")

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

答案 3 :(得分:14)

不幸的是,接受的答案是错误的。其他答案也没有提供最简单或最明确的解决方案。

使用ID类的BAR的属性访问级别。

@Entity
public class Bar {

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

    ...
}

就像那样简单:)

答案 4 :(得分:7)

Java Persistence with 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。如果实例是真实对象,则返回id。

答案 5 :(得分:3)

在org.hibernate.Session中你有一个函数可以在不延迟加载实体的情况下完成工作:

public Serializable getIdentifier(Object object)抛出HibernateException;

在hibernate 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();
        }
  }

答案 6 :(得分:2)

现在有一个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);

这将包括延迟加载关系的id -

答案 7 :(得分:0)

您可以使用HQL查询。 getBar()方法将真正返回一个代理,在您调用某些数据绑定方法之前不会获取该代理。我不确定你究竟是什么问题。你能给我们一些更多的背景吗?

答案 8 :(得分:0)

改变你的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;
}