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
Hibernate Entity proxy initialization
提问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 handler
even after doing an explicit initialisation...
In my debug view, I can see the 'real' property values (not displayed above) when I expand the target
node.
请注意,我的 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.initialize
call!
注意HibernateUtil.initialize
来电!
SomeEntity
mapping:
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
mapping:
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 target
node.
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 EntityReferenceImpl
entity which has @ManyToOne(LAZY)
to SomeEntity
entity.
在您的例子中,你必须EntityReferenceImpl
拥有实体@ManyToOne(LAZY)
,以SomeEntity
实体。
When hibernate loads EntityReferenceImpl
it fills all fields from resultSet values but someEntity
field 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 SomeEntity
class has (for example) methods getFirstName()
etc, but javassist generated classsimply extends your SomeEntity
and has few new bytecode-generated methods like c7getFirstName()
etc.
您的SomeEntity
类具有(例如)方法getFirstName()
等,但javassist 生成的类只是扩展了您的类SomeEntity
,并且几乎没有新的字节码生成方法等c7getFirstName()
。
And most important- proxy class has new field: handler
of type JavassistLazyInitializer
.
而最重要的-代理类都有新的领域:handler
类型JavassistLazyInitializer
。
Lets see how JavassistLazyInitializer
looks 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
, lastname
etc.
When you initialize this proxy, SomeEntity
is loaded into targetfield. Your firstname
, lastname
fields on proxy objects are null as before- proxy doesn't use them, but real data is in SomeEntity
object held by target
field.
所以当你查看你的代理对象时 - 它有你的字段firstname
,lastname
等等。当你初始化这个代理时,SomeEntity
被加载到目标字段中。您的firstname
,lastname
代理对象上的字段和以前一样为空- 代理不使用它们,但实际数据在SomeEntity
由target
字段保存的对象中。
This is how proxy is implemented in hibernate.
这就是代理在休眠中的实现方式。
You may ask - why such solution? Such design comes from polymorphismissues. If SomeEntity
would be abstract parent class with 2 subclasses EntityA
and EntityB
hibernate has no problem - someEntity field holds proxy (generated) class extending SomeEntity
but having concrete EntityA
or EntityB
inside target
field.
你可能会问——为什么会有这样的解决方案?这种设计来自多态问题。如果SomeEntity
将是具有 2 个子类的抽象父类EntityA
并且EntityB
休眠没有问题 - someEntity 字段持有代理(生成)类扩展SomeEntity
但具有具体EntityA
或EntityB
内部target
字段。
However there are some pitfalls with this solution and polymorphism. Your someEntity
field will be instance of SomeEntity
but neverinstance of EntityA
nor 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.update
is meant to re-attach detached entities (entities that were loaded in a Session that's been closed).
Session.update
旨在重新附加分离的实体(在已关闭的会话中加载的实体)。
Session.merge
is 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 @Transactional
support).
检查您是否启用了交易,否则您只能选择实体。对于持久/合并和脏检查更新,您必须使用事务(使用 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;
}
}