use*_*927 9 java orm proxy hibernate jpa
我遇到了一个没有初始化的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
Run Code Online (Sandbox Code Playgroud)
请注意,我的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;
Run Code Online (Sandbox Code Playgroud)
注意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;
...
}
Run Code Online (Sandbox Code Playgroud)
EntityReferenceImpl 制图:
public class EntityReferenceImpl extends AbstractEntity implements EntityReference {
@ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = SomeEntityImpl.class)
@JoinColumn(name = "entity_id")
private SomeEntity someEntity;
...
}
Run Code Online (Sandbox Code Playgroud)
那么副作用是什么:当POJO后来带有更新的属性时,我仍然具有相同的结构(如上所述),我可以在target节点下看到更新的属性.
但是当我尝试使用session.merge()or session.update()或更新实体时session.saveOrUpdate(),Hibernate没有检测到"脏"属性,也没有调用数据库的更新查询.
有没有人对这种奇怪的行为有一些线索?我尽我所能,但没有任何结果.
非常欢迎所有的帮助!!
调试窗口中的实体看起来像是正确初始化的.
当你有一些可能由hibernate代理的实体时,即使在正确初始化之后,该实体也存储在代理对象中.初始化后,代理对象本身不会消失......
public class EntityReferenceImpl extends AbstractEntity implements EntityReference {
@ManyToOne(fetch = FetchType.LAZY, ...)
private SomeEntity someEntity;
...
Run Code Online (Sandbox Code Playgroud)
在您的例子中,你必须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() {...}
}
Run Code Online (Sandbox Code Playgroud)
你的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 );
}
}
Run Code Online (Sandbox Code Playgroud)
所以,当你进入你的代理对象-它拥有你的领域,如firstname,lastname等当你初始化这个代理,SomeEntity加载到目标领域.你firstname,lastname对代理对象的字段为空像以前一样 -代理服务器不使用它们,但实际的数据是SomeEntity通过持有对象target场.
这就是在hibernate中实现代理的方式.
你可能会问 - 为什么这样解决?这种设计来自多态性问题.如果SomeEntity是具有2个子类的抽象父类EntityA并且EntityBhibernate没有问题 - someEntity字段保持代理(生成)类扩展SomeEntity但具有具体EntityA或EntityB内部target字段.
然而,这种解决方案和多态性存在一些缺陷.你的someEntity领域会instance of SomeEntity,但从来没有 instance of EntityA,也没有instance of EntityB.
Hibernate 使用代理来拦截对 LAZY 实体的调用。您在调试中看到的结构就是代理的样子。
您不需要调用HibernateUtil.initialize,而只需使用“fetch joins”来加载您对单个查询感兴趣的所有实体。
如果实体附加到当前Session,脏检查机制将自动将所有实体状态转换转换为数据库DML语句。
Session.update旨在重新附加分离的实体(在已关闭的会话中加载的实体)。
Session.merge用于将实体状态复制到已加载的实体上(如果之前未加载,则动态加载)。
检查您是否启用了交易,否则您只能选择实体。对于持久/合并和脏检查更新,您必须使用事务(使用 Java EE 或 Spring@Transactional支持)。