Hibernate实体代理初始化

时间:2014-10-02 16:26:21

标签: java hibernate jpa orm proxy

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

如果我查看我的调试信息,我希望我的实体可以初始化 但它看起来如下:

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

请注意,即使在进行显式初始化后,我的Hibernate POJO仍然只包含handler ...
在我的调试视图中,当我展开target节点时,我可以看到“真实的”属性值(上面没有显示)。

我在做什么:

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;

注意HibernateUtil.initialize来电!

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;

    ...

}

EntityReferenceImpl映射:

public class EntityReferenceImpl extends AbstractEntity implements EntityReference {

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

...

}

那么副作用是什么:当POJO后来带有更新的属性时,我仍然具有相同的结构(如上所述),我可以在target节点下看到更新的属性。
但是当我尝试使用session.merge()session.update()session.saveOrUpdate()更新实体时,Hibernate不检测'dirty'属性,也不会调用数据库的更新查询。 / p>


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

3 个答案:

答案 0 :(得分:9)

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

当你有一些可能被hibernate代理的实体时,即使在正确初始化之后,该实体也存储在内部代理对象中。初始化后,代理对象本身并没有消失......

public class EntityReferenceImpl extends AbstractEntity implements EntityReference {

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

在您的示例中,您有EntityReferenceImpl个实体,其中包含@ManyToOne(LAZY)SomeEntity个实体。

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

此代理对象如下所示:

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() {...}
}

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

最重要 - 代理类有新字段:handler类型JavassistLazyInitializer

让我们看看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 );
  }
}

因此,当您查看代理对象时,您的字段,如firstnamelastname等。 初始化此代理时,SomeEntity会将加载到目标字段中。代理对象上的firstnamelastname字段与以前一样为空 - 代理不使用它们,但实际数据位于SomeEntity对象中target字段。

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

你可能会问 - 为什么这样的解决方案?这种设计来自多态问题。如果SomeEntity是具有2个子类EntityAEntityB的抽象父类,则hibernate没有问题 - someEntity字段保存代理(生成)类,扩展SomeEntity但具有具体{{1}在} EntityA字段内{}}或EntityB

然而,这种解决方案和多态性存在一些缺陷。您的target字段将为someEntity,但永远不会 instance of SomeEntity,也不会instance of EntityA

答案 1 :(得分:1)

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

您无需致电HibernateUtil.initialize,只需使用“获取联接”即可加载您对单个查询感兴趣的所有实体。

如果实体附加到当前会话,dirty checking mechanism将自动将所有entity state transitions转换为数据库DML语句。

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

Session.merge用于将实体状态复制到已加载的实体上(如果以前未加载,则会动态加载)。

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

答案 2 :(得分:0)

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