java Hibernate Entity 代理初始化

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26165188/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 09:25:03  来源:igfitidea点击:

Hibernate Entity proxy initialization

javahibernatejpaormproxy

提问by user2054927

I'm having a problem with a Hibernate entity that does not get initialised.
It seems that it's still returning a not initialised proxy...

我遇到了未初始化的 Hibernate 实体的问题。
似乎它仍在返回一个未初始化的代理......

If I take a look at my debug info I would expect my entity to be initialised.
But it looks like the following:

如果我查看我的调试信息,我希望我的实体被初始化。
但它看起来像下面这样:

entity = {SomeEntity_$$_jvst47c_1e@9192}"SomeEntityImpl@1f3d4adb[id=1,version=0]"
    handler = {org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer@9196}
        interfaces = {java.lang.Class[2]@9197}
        constructed = true
        persistentClass = {java.lang.Class@3605}"class SomeEntityImpl"
        getIdentifierMethod = null
        setIdentifierMethod = null
        overridesEquals = true
        componentIdType = null
        replacement = null
        entityName = {java.lang.String@9198}"SomeEntityImpl"
        id = {java.lang.Long@9199}"1"
        target = {SomeEntityImpl@9200}"SomeEntityImpl@1f3d4adb[guid=<null>,id=1,version=0]"
        initialized = true
        readOnly = true
        unwrap = false
        session = {org.hibernate.internal.SessionImpl@6878}"SessionImpl(PersistenceContext[entityKeys=[EntityKey[EntityReferenceImpl#2], EntityKey[SomeEntityImpl#1], EntityKey[...
        readOnlyBeforeAttachedToSession = null
        sessionFactoryUuid = null
        allowLoadOutsideTransaction = false

Notice that my Hibernate POJO still only contains a handlereven after doing an explicit initialisation...
In my debug view, I can see the 'real' property values (not displayed above) when I expand the targetnode.

请注意,我的 Hibernate POJOhandler在进行了显式初始化后仍然只包含一个偶数...
在我的调试视图中,当我展开target节点时,我可以看到“真实”属性值(上面未显示)。

What I'm doing:

我在做什么:

EntityReferenceImpl entityReference = findEntityReference(session);
SomeEntity entity = null;
if (entityReference != null) {
    // initialize association using a left outer join
    HibernateUtil.initialize(entityReference.getSomeEntity());
    entity = entityReference.getSomeEntity();
}
return entity;

Notice the HibernateUtil.initializecall!

注意HibernateUtil.initialize来电!

SomeEntitymapping:

SomeEntity映射:

public class SomeEntityImpl extends AbstractEntity implements SomeEntity {
    @OneToMany(mappedBy = "someEntity", fetch = FetchType.EAGER, targetEntity = EntityReferenceImpl.class, orphanRemoval = true)
    @Cascade(CascadeType.ALL)
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private Set<EntityReference> entityReferences = new HashSet<>();

    @Target(EntityName.class)
    @Embedded
    private Name name;

    @Target(EntityAddress.class)
    @Embedded
    private Address address;

    ...

}

EntityReferenceImplmapping:

EntityReferenceImpl映射:

public class EntityReferenceImpl extends AbstractEntity implements EntityReference {

@ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = SomeEntityImpl.class)
@JoinColumn(name = "entity_id")
private SomeEntity someEntity;

...

}

So what is the side effect: When the POJO later comes with updated properties I'm still having the same structure (as mentioned above) and I can see the updated properties under the targetnode.
But when I'm trying to update the entity using session.merge()or session.update()or session.saveOrUpdate(), Hibernate does not detect the 'dirty' properties and does not invoke an update query to the database.

那么副作用是什么:当 POJO 稍后带有更新的属性时,我仍然具有相同的结构(如上所述)并且我可以在target节点下看到更新的属性。
但是,当我试图更新使用实体session.merge()session.update()或者session.saveOrUpdate(),Hibernate不检测“脏”的属性和不调用更新查询数据库。


Does anyone have some clues about this weird behavior? I have tried everything what I can but without any results.
All help is very welcome!!


有没有人对这种奇怪的行为有一些线索?我已经尽我所能,但没有任何结果。
非常欢迎所有帮助!!

回答by przemek hertel

Entity in your debug window looks like properly initialized.

调试窗口中的实体看起来已正确初始化

When you have some entity that may be proxied by hibernate, this entity is stored insideproxy object even after being properly initialized. After initialisation proxy object itself doesn't disappear...

当您有一些实体可能被休眠代理时,即使在正确初始化后,该实体也会存储代理对象中。初始化后代理对象本身不会消失......

public class EntityReferenceImpl extends AbstractEntity implements EntityReference {

@ManyToOne(fetch = FetchType.LAZY, ...)
private SomeEntity someEntity;
...

In your example you have EntityReferenceImplentity which has @ManyToOne(LAZY)to SomeEntityentity.

在您的例子中,你必须EntityReferenceImpl拥有实体@ManyToOne(LAZY),以SomeEntity实体。

When hibernate loads EntityReferenceImplit fills all fields from resultSet values but someEntityfield is set to proxy object.

当休眠加载时,EntityReferenceImpl它会填充 resultSet 值中的所有字段,但someEntity字段设置为代理对象。

This proxy objects looks like this:

此代理对象如下所示:

class SomeEntity_$$_javassist_3 extends SomeEntity implements HibernateProxy {
  + firstname = NULL;
  + lastname = NULL;
  + age = 0;
  + handler; //of type: JavassistLazyInitializer

  getFirstname() {
    handler.invoke(..., Method thisMethod, Method proceed, args);
  }
  getLastName() {...}
}

Your SomeEntityclass has (for example) methods getFirstName()etc, but javassist generated classsimply extends your SomeEntityand has few new bytecode-generated methods like c7getFirstName()etc.

您的SomeEntity类具有(例如)方法getFirstName()等,但javassist 生成的类只是扩展了您的SomeEntity,并且几乎没有新的字节码生成方法等c7getFirstName()

And most important- proxy class has new field: handlerof type JavassistLazyInitializer.

最重要的-代理类都有新的领域:handler类型JavassistLazyInitializer

Lets see how JavassistLazyInitializerlooks like:

让我们看看它的JavassistLazyInitializer样子:

JavassistLazyInitializer {
  + target; //holds SomeEntity object
  invoke(..., Method thisMethod, Method proceed, args) {
    if (target == null) {
      target = initialize(); // calls sessionImpl.immediateLoad
    }
    return thisMethod.invoke( target, args );
  }
}

So when you look into your proxy object - it hasyour fields like firstname, lastnameetc. When you initialize this proxy, SomeEntityis loaded into targetfield. Your firstname, lastnamefields on proxy objects are null as before- proxy doesn't use them, but real data is in SomeEntityobject held by targetfield.

所以当你查看你的代理对象时 - 它你的字段firstnamelastname等等。当你初始化这个代理时,SomeEntity被加载到目标字段中。您的firstname,lastname代理对象上的字段和以前一样为空- 代理不使用它们,但实际数据在SomeEntitytarget字段保存的对象中。

This is how proxy is implemented in hibernate.

这就是代理在休眠中的实现方式。

You may ask - why such solution? Such design comes from polymorphismissues. If SomeEntitywould be abstract parent class with 2 subclasses EntityAand EntityBhibernate has no problem - someEntity field holds proxy (generated) class extending SomeEntitybut having concrete EntityAor EntityBinside targetfield.

你可能会问——为什么会有这样的解决方案?这种设计来自多态问题。如果SomeEntity将是具有 2 个子类的抽象父类EntityA并且EntityB休眠没有问题 - someEntity 字段持有代理(生成)类扩展SomeEntity但具有具体EntityAEntityB内部target字段。

However there are some pitfalls with this solution and polymorphism. Your someEntityfield will be instance of SomeEntitybut neverinstance of EntityAnor instance of EntityB.

然而,这种解决方案和多态性存在一些缺陷。你的someEntity领域将是instance of SomeEntity永远instance of EntityA也不是instance of EntityB

回答by Vlad Mihalcea

Hibernate uses Proxies to intercept calls to LAZY entities. That structure you see in debug is how a Proxy looks like.

Hibernate 使用代理来拦截对 LAZY 实体的调用。您在调试中看到的结构就是代理的样子。

You don't need to call HibernateUtil.initialize, but simply use "fetch joins" to load all entities you are interested in a single query.

您不需要调用HibernateUtil.initialize,而只需使用“获取连接”来加载您对单个查询感兴趣的所有实体。

If the entity is attached to the current Session, the dirty checking mechanismwill automatically translate all entity state transitionsto database DML statements.

如果实体附加到当前会话,脏检查机制将自动将所有实体状态转换转换为数据库 DML 语句。

Session.updateis meant to re-attach detached entities (entities that were loaded in a Session that's been closed).

Session.update旨在重新附加分离的实体(在已关闭的会话中加载的实体)。

Session.mergeis for copying the entity state onto an already loaded entity (which is loaded on the fly, if not loaded previously).

Session.merge用于将实体状态复制到已加载的实体(如果之前未加载,则即时加载)。

Check if you have enabled transactions, as otherwise you can only select entities. For persist/merge and dirty checking updates you must use transactions (use Java EE or Spring @Transactionalsupport).

检查您是否启用了交易,否则您只能选择实体。对于持久/合并和脏检查更新,您必须使用事务(使用 Java EE 或 Spring@Transactional支持)。

回答by Srikant

The post https://forum.hibernate.org/viewtopic.php?f=1&t=997278was useful for me. As the getter methods in Entity Model Objects were marked as final, Javaassist wasn't able to override the method and thus change it's value. My Entity Objects were like this -

帖子https://forum.hibernate.org/viewtopic.php?f=1&t=997278对我很有用。由于实体模型对象中的 getter 方法被标记为 final,Javaassist 无法覆盖该方法并因此更改其值。我的实体对象是这样的 -

@Entity
@Table(name = "COUNTRIES")
public class Country {
  @Id
  private Long id;
  @Column(nullable = false)
  private String name;

  private final getId() {
    return id;
  }

  private final getName() {
    return name;
  }
}