Java Hibernate实体代理初始化

Java Hibernate实体代理初始化,java,hibernate,jpa,orm,proxy,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} int

我对未初始化的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
注意
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;

    ...

}
public class EntityReferenceImpl extends AbstractEntity implements EntityReference {

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

...

}
那么副作用是什么呢:当POJO稍后带有更新的属性时,我仍然具有相同的结构(如上所述),并且我可以在
目标下看到更新的属性。

但是,当我尝试使用
session.merge()
session.update()
session.saveOrUpdate()
更新实体时,Hibernate不会检测“脏”属性,也不会调用数据库的更新查询


有人对这种奇怪的行为有什么线索吗?我尽我所能尝试了一切,但没有任何结果。
欢迎大家帮忙!!

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

您不需要调用
HibernateUtil.initialize
,只需使用“fetch joins”加载您对单个查询感兴趣的所有实体即可

如果实体附加到当前会话,则将自动将所有语句转换为数据库DML语句

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

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


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

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

当您有某个实体可以由hibernate代理时,即使在正确初始化后,该实体也会存储在代理对象中。初始化后,代理对象本身不会消失

public class EntityReferenceImpl extends AbstractEntity implements EntityReference {

@ManyToOne(fetch = FetchType.LAZY, ...)
private SomeEntity someEntity;
...
在您的示例中,您有
EntityReferenceImpl
实体,它有
@manytone(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
,类型为
JavassitLazInitializer

让我们看看
JavassitLazInitializer
的外观:

JavassistLazyInitializer {
  + target; //holds SomeEntity object
  invoke(..., Method thisMethod, Method proceed, args) {
    if (target == null) {
      target = initialize(); // calls sessionImpl.immediateLoad
    }
    return thisMethod.invoke( target, args );
  }
}
因此,当您查看代理对象时,具有类似
firstname
lastname
等字段。 初始化此代理时,
SomeEntity
加载到target字段中。代理对象上的
firstname
lastname
字段与以前一样为空
-代理不使用它们,但实际数据位于
target
字段持有的
SomeEntity
对象中

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

你可能会问——为什么会有这样的解决方案?这种设计源于多态性问题。如果
SomeEntity
将是抽象父类,包含两个子类
EntityA
EntityB
,则hibernate没有问题-SomeEntity字段持有扩展
SomeEntity
的代理(生成)类,但在
target
字段内具有具体的
EntityA
EntityB

然而,这种解决方案和多态性存在一些缺陷。你的
someEntity
字段将是
someEntity的实例
,但永远不会
实体a的实例
也不会
实体b的实例

这篇文章对我很有用。由于实体模型对象中的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;
  }
}

我同意你的意见。我已经在HQL查询中使用了“fetch join”,但效果相同。这就是为什么我尝试Hibernate.initialize()只是为了确定。我正在使用readOnly=false的Spring事务注释。Hibernate似乎没有检测到脏属性。问题是代理没有正确初始化(就像您在调试视图中看到的那样)。Hibernate不检查未初始化的关联(这将是一个巨大的开销)。问题仍然是为什么它没有正确初始化。它是否与实现接口有关?根据我的经验,最好避免实体上的接口关联。您可以有接口,但要在实际实体上建立所有关联,因为这会模拟实际关系。
HibernateUtil
类来自哪个包?你没有使用
Hibernate
class吗?你解决了这个问题吗?我也面临着同样的问题我也面临着同样的问题。我的对象包含一个“处理程序”。在这个名为“target”的属性的处理程序中,有我的真实对象。我怎样才能强迫他先调用自己呢?虽然这个链接可以回答这个问题,但最好在这里包含这个链接,并提供链接供参考。如果链接页面发生更改,则仅链接的答案可能无效。
@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;
  }
}