简体   繁体   English

Hibernate延迟加载反向一对一解决方法 - 这是如何工作的?

[英]Hibernate lazy loading for reverse one to one workaround - how does this work?

I was having problems today with lazy loading not working when using a mapped by collection. 今天我遇到问题,延迟加载在使用映射集合时无法正常工作。 I found this excellent article that seems to fix the problem 我发现这篇优秀的文章似乎解决了这个问题

http://justonjava.blogspot.co.uk/2010/09/lazy-one-to-one-and-one-to-many.html http://justonjava.blogspot.co.uk/2010/09/lazy-one-to-one-and-one-to-many.html

One thing I do not understand is how the workaround using FieldHandled works. 我不明白的一件事是使用FieldHandled的解决方法是如何工作的。 Can anyone help me understand this? 任何人都可以帮我理解这个吗? The code in question is below (copied from the example on the link): 有问题的代码如下(从链接上的示例中复制):

@Entity
public class Animal implements FieldHandled {
   private Person owner;
   private FieldHandler fieldHandler;

   @OneToOne(fetch = FetchType.LAZY, optional = true, mappedBy = "animal")
   @LazyToOne(LazyToOneOption.NO_PROXY)
   public Person getOwner() {
     if (fieldHandler != null) {
        return (Person) fieldHandler.readObject(this, "owner", owner);
     }
     return owner;
   }

   public void setOwner(Person owner) {
     if (fieldHandler != null) {
       this.owner = fieldHandler.writeObject(this, "owner", this.owner, owner);
       return;
     }
     this.owner = owner;
   }

   public FieldHandler getFieldHandler() {
     return fieldHandler;
   }

   public void setFieldHandler(FieldHandler fieldHandler) {
     this.fieldHandler = fieldHandler;
   }
}

What am I missing? 我错过了什么? Perhaps I dont know enough about hibernate's lifecycle here? 也许我对hibernate的生命周期知之甚少? Im happy to investigate but can anyone give me some pointers. 我很高兴调查,但任何人都可以给我一些指示。

Thanks in advance. 提前致谢。

EDIT 编辑

I pushed through a lot of changes so a lot of my entities implemented FieldHandled but then discovered some of my tests were failing. 我推动了很多变化,所以我的很多实体都实现了FieldHandled但后来发现我的一些测试失败了。 I pumped out the SQL and got some weird things where the SQLs were happening in different orders if this interface was implemented with just these methods set. 我抽出了SQL并得到了一些奇怪的东西,如果这个接口是用这些方法集实现的,那么SQL会以不同的顺序发生。

   public FieldHandler getFieldHandler() {
     return fieldHandler;
   }

   public void setFieldHandler(FieldHandler fieldHandler) {
     this.fieldHandler = fieldHandler;
   }

This was causing tests to fail as things were not quite in the correct state when I was asserting. 这导致测试失败,因为当我断言时,事情并没有完全处于正确的状态。 This adds to my mis-understanding of this FieldHandler variable. 这增加了我对这个FieldHandler变量的误解。

The following code tells Hibernate to use interception handler instead of proxy. 以下代码告诉Hibernate使用拦截处理程序而不是代理。

@LazyToOne(LazyToOneOption.NO_PROXY)

From javadoc: 来自javadoc:

give back the real object loaded when a reference is requested (Bytecode enhancement is mandatory for this option, fall back to PROXY if the class is not enhanced) 在请求引用时返回加载的实际对象(此选项必须使用字节码增强,如果未增强类,则返回PROXY)

As can be seen, it's required to instrument the bytecode before using it. 可以看出,在使用之前需要检测字节码。 'Persisted class is enhanced' after its 'bytecode is instrumented'. 在''字节码被检测'后,'持久类被增强'。

The idea is to fool Hibernate that the entity class which we want to use has been already instrumented 我们的想法是欺骗Hibernate我们想要使用的实体类已经被检测过了

Instrumentation task is called after code is compiled. 编译代码后调用Instrumentation任务。 Instrumented entity extends FieldHandled . 检测实体扩展了FieldHandled FieldHandled is an 'Interface introduced to the enhanced class' FieldHandled是一个'引入增强类的接口'

Hibernate verifies entity at a run time and comes to a conclusion that class was enhanced that's why it uses the real object instead of proxy and isn't loading related entity object as it normally did . Hibernate在运行时验证实体,并得出结论,类已被增强,这就是为什么它使用真实对象而不是代理, 并且不像通常那样加载相关的实体对象

Edit: 编辑:

Lets take a look under the hood: 让我们来看看引擎盖:

  1. AnnotationBinder handles NO_PROXY option AnnotationBinder处理 NO_PROXY选项

     if ( lazy != null ) { toOne.setLazy( !( lazy.value() == LazyToOneOption.FALSE ) ); toOne.setUnwrapProxy( ( lazy.value() == LazyToOneOption.NO_PROXY ) ); } 
  2. Both org.hibernate.mapping.ManyToOne and org.hibernate.mapping.OneToOne are subclasses of org.hibernate.mapping.ToOne . 无论org.hibernate.mapping.ManyToOneorg.hibernate.mapping.OneToOne是子类org.hibernate.mapping.ToOne ToOne#isUnwrapProxy() only usage is in #getType : ToOne#isUnwrapProxy()仅用于#getType

    getMappings().getTypeResolver().getTypeFactory().oneToOne( getMappings()。getTypeResolver()。getTypeFactory()。oneToOne(

  3. Both ManyToOneType and OneToOneType are subclasses of EntityType and only usage of 'EntityType#unwrapProxy' is in EntityType#resolveIdentifier(Serializable, SessionImplementor) ManyToOneTypeOneToOneType都是EntityType子类,只有'EntityType#unwrapProxy'的使用在EntityType#resolveIdentifier(Serializable, SessionImplementor)

     boolean isProxyUnwrapEnabled = unwrapProxy && session.getFactory() .getEntityPersister( getAssociatedEntityName() ) .isInstrumented(); 
  4. Here's Tentative call hierarchy: AbstractEntityPersister#isInstrumented() -> EntityMetamodel#isInstrumented() -> EntityInstrumentationMetadata#isInstrumented() -> etc. and finally BytecodeProviderImpl.EntityInstrumentationMetadataImpl.EntityInstrumentationMetadataImpl() 这是暂定的调用层次结构: AbstractEntityPersister#isInstrumented() - > EntityMetamodel#isInstrumented() - > EntityInstrumentationMetadata#isInstrumented() - >等等,最后是BytecodeProviderImpl.EntityInstrumentationMetadataImpl.EntityInstrumentationMetadataImpl()

      this.isInstrumented = FieldHandled.class.isAssignableFrom( entityClass ); 

That's why it's required either instrument the code (eg with InstrumentTask ) or implement FieldHandled . 这就是为什么需要检测代码(例如使用InstrumentTask )或实现FieldHandled


In order to make long story short you may take a look at EntityType#resolveIdentifier(Serializable, SessionImplementor) . 为了简而言之,您可以查看EntityType#resolveIdentifier(Serializable, SessionImplementor) That's the reason why second object is not loaded even if it's nullable. 这就是为什么第二个对象即使可以为空也不会被加载的原因。

The FieldHandled interface has been replaced with the PersistentAttributeInterceptable interface in Hibernate 5. You can achieve the same result by implementing this new interface: FieldHandled接口已被Hibernate 5中的PersistentAttributeInterceptable接口替换。您可以通过实现此新接口来实现相同的结果:

@Entity
public class Animal implements PersistentAttributeInterceptable {
    private Person owner;
    private PersistentAttributeInterceptor interceptor;

    @OneToOne(fetch = FetchType.LAZY, optional = true, mappedBy = "animal")
    @LazyToOne(LazyToOneOption.NO_PROXY)
    public Person getOwner() {
        if (interceptor != null) {
            return (Person) interceptor.readObject(this, "owner", owner);
        }
        return owner;
    }

    public void setOwner(Person owner) {
        if (interceptor != null) {
            this.owner = interceptor.writeObject(this, "owner", this.owner, owner);
            return;
        }
        this.owner = owner;
    }

    @Override
    public PersistentAttributeInterceptor $$_hibernate_getInterceptor() {
        return interceptor;
    }

    @Override
    public void $$_hibernate_setInterceptor(PersistentAttributeInterceptor interceptor) {
        this.interceptor = interceptor;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM