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

Rob*_*Rob 60 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
Run Code Online (Sandbox Code Playgroud)

我希望f.getBar()不会触发另一个提取.我希望hibernate给我一个代理对象,允许我调用.getId()而不实际获取Bar对象.

我究竟做错了什么?

Art*_*ald 38

使用属性访问策略

代替

@OneToOne(fetch=FetchType.LAZY, optional=false)
private Bar bar;
Run Code Online (Sandbox Code Playgroud)

使用

private Bar bar;

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

现在它工作正常!

如果您调用任何不是标识符getter方法的方法,则会初始化代理.但它在使用属性访问策略时才有效.记在心上.

请参阅:Hibernate 5.2用户指南

  • 谢谢回复.我不相信我的项目中执行的查询,所以我添加了自己的数据库集成测试来验证.我现在有工作.唯一的变化是在目标实体的id上添加访问类型.这是唯一需要改变的.@Id @GeneratedValue(strategy = GenerationType.SEQUENCE,generator ="FILECONTENT_ID_SEQ")@SequenceGenerator(name ="FILECONTENT_ID_SEQ",sequenceName ="FILECONTENT_ID_SEQ")@Column(name ="ID",nullable = false)@Access(AccessType. PROPERTY)私人Long id; (2认同)

xme*_*eko 28

只是添加到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();
}
Run Code Online (Sandbox Code Playgroud)

适用于分离的实体.

  • 不推荐使用Hibernate注释"Acc​​essType".使用JPA2注释代替`@Access(AccessType.PROPERTY)` (2认同)

Pau*_*ski 17

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

使用属性访问级别为ID中的BAR类.

@Entity
public class Bar {

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

    ...
}
Run Code Online (Sandbox Code Playgroud)

就像那简单:)


小智 15

添加@AccessType("属性")

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@AccessType("property")
protected Long id;
Run Code Online (Sandbox Code Playgroud)

  • 不推荐使用Hibernate注释"Acc​​essType".请改用JPA2注释:`@Access(AccessType.PROPERTY)` (13认同)

Mar*_*rcG 8

Java Persistence with Hibernate Book在"13.1.3 Understanding Proxies"中提到了这一点:

只要您只访问数据库标识符属性,就不需要初始化代理.(请注意,如果使用直接字段访问映射标识符属性,则不会出现这种情况; Hibernate甚至不知道getId()方法是否存在.如果调用它,则必须初始化代理.)

但是,基于此页面中的@xmedeko答案,我开发了一个hack,以避免在使用直接字段访问策略时初始化代理.只需改变getId()如下所示的方法即可.

代替:

    public long getId() { return id; }
Run Code Online (Sandbox Code Playgroud)

使用:

    public final long getId() {
        if (this instanceof HibernateProxy) {
            return (long)((HibernateProxy)this).getHibernateLazyInitializer().getIdentifier();
        }
        else { return id; }
    }
Run Code Online (Sandbox Code Playgroud)

这里的想法是将getId()方法标记为final,以便代理不能覆盖它.然后,调用该方法不能运行任何代理代码,因此无法初始化代理.该方法本身检查其实例是否是代理,并在这种情况下从代理返回id.如果实例是真实对象,则返回id.

  • 哈哈,这确实是一个可怕的骇客:)“不要在家做” (3认同)
  • 它将转到Hibernate的内部类,它们可能会更改,恕不另行通知。尽管我毫不怀疑它能完美运行,但我不会将它放入应该持续多年的应用程序中。 (3认同)
  • 当然,对您有好处。对于其他人,除非他们计划坚持使用相同的主要版本,否则我建议不要依赖于此。 (3认同)
  • @OndraŽižka 你错了。这段代码完美运行。而且,它不违反任何规则,没有副作用,并且很清楚它在做什么以及为什么。因此,如果您能想到不使用此代码或为什么它“可怕”的任何原因,请分享。 (2认同)