默认情况下,在Hibernate中为所有FetchType.LAZY非集合启用无代理行为 [英] Enable no-proxy behaviour for all FetchType.LAZY non-collections by default in Hibernate
问题描述
使用标准的JPA批注时,您可以在非集合字段上指定 FetchType.LAZY
(即 @ManyToOne
和 @OneToOne
)。看起来Hibernate在这种情况下内部使用代理获取。但是代理获取有其继承的问题,我认为使用无代理获取与字节码检测相结合会更好。不幸的是,Hibernate仍然要求你在 hbm
-file中指定no-proxy,或者使用特定于Hibernate的 @LazyToOne
注解。
我的问题是:Hibernate是否支持一个配置选项,对所有非集合字段使用无代理获取策略,即 FetchType.LAZY
?
这是我需要的:我想在大多数情况下只使用JPA注释一方面。另一方面,我想避免继承和懒惰领域的问题。我不喜欢将所有内容封装在接口中的想法,因为我在当前项目中使用了DDD,所以我认为在我的域模型中不应该存在样板垃圾,只有纯业务逻辑。
我有一个糟糕的解决方法的想法:通过使用字节码修改,我添加 @LazyToOne
注释到处 @ManyToOne <出现code>。但是,如果存在,我宁愿使用内置的Hibernate功能。
这是众所周知的代理抓取问题,使事情变得更清晰:
@Entity @DiscriminatorColumn(t)@DiscriminatorValue()
public abstract class A {
@Id private Integer id;
}
@Entity @DiscriminatorValue(B)
公共抽象类B扩展A {
}
@Entity @DiscriminatorValue (C)
public abstract class C extends A {
}
@Entity public class D {
@Id private Integer id;
@ManyToOne(fetch = FetchType.LAZY)private A a;
public A getA(){
return a;
$ / code $ / pre
准备:
D d = new D();
C c = new C();
d.setA(c);
em.persist(d);
和失败声明(在另一个EM中是另一个事务):
D d = em.createQuery(从D d选择d,D.class).getSingleResult();
列表< C> cs = em.createQuery(select c from C c,C.class).getResultList();
assert d.getA()instanceof C;
assert d.getA()== cs.get(0);
以下是我要解决上述断言的方法:
@Entity public class D {
@Id private Integer id;
@ManyToOne(fetch = FetchType.LAZY)@LazyToOne(LazyToOneOption.NO_PROXY)
private A a;
public A getA(){
return a;
}
}
我不想要同样的东西没有 @LazyToOne
注解。
解决方案好吧,我放弃了接受答案。我仔细检查了Hibernate源代码,并得出结论认为Hibernate本身没有任何属性来实现我想要的。但是我想出了一点肮脏的黑客攻击,这正是我想要的。因此,这里是:
$ pre $ public $ D $ H $ $ $ $ $ $ $
@SuppressWarnings({ rawtypes,unchecked})
public EntityManagerFactory createEntityManagerFactory(String persistenceUnitName,
Map properties){
properties.put(AvailableSettings.PROVIDER,HibernatePersistence.class.getName());
Ejb3Configuration cfg = new Ejb3Configuration()。configure(persistenceUnitName,properties);
if(cfg == null){
return null;
}
cfg.buildMappings();
hackConfiguration(cfg);
return cfg.buildEntityManagerFactory();
$ b @Override
@SuppressWarnings({rawtypes,unchecked})
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info,
Map属性) {
properties.put(AvailableSettings.PROVIDER,HibernatePersistence.class.getName());
Ejb3Configuration cfg = new Ejb3Configuration()。configure(info,properties);
if(cfg == null){
return null;
}
cfg.buildMappings();
hackConfiguration(cfg);
return cfg.buildEntityManagerFactory();
private void hackConfiguration(Ejb3Configuration cfg){
System.out.println(Hacking configuration);
String noProxyByDefault = cfg.getProperties()。getProperty(hibernate.hack.no-proxy-by-default,false);
if(Boolean.parseBoolean(noProxyByDefault)){
Iterator<?> iter = cfg.getClassMappings(); (iter.hasNext()){
hackClass((PersistentClass)iter.next());
}
}
}
private void hackClass(PersistentClass classMapping){
Iterator<?> iter = classMapping.getPropertyIterator(); (iter.hasNext()){
Property property =(Property)iter.next();
if(property.getValue()instanceof ToOne){
ToOne toOne =(ToOne)property.getValue();
if(toOne.isLazy()){
toOne.setUnwrapProxy(true);
}
}
}
}
}
还必须有一个名为 META-INF / services / javax.persistence.spi.PersistenceProvider
的资源,其中包含具有该类名称的单行。
要使用这种方法,您应该在 persistence.xml
中指定以下内容:
< provider> packagename.DirtyHackedHibernatePersistence< / provider>
<属性>
< property name =hibernate.hack.no-proxy-by-defaultvalue =true/>
< / properties>
完整的示例可用 here 。
请注意,如果您删除 hibernate .hack.no-proxy-by-default
属性和重建项目,这两个断言都被破坏了。
另外我要发布一个特性请求Hibernate团队。
When using standard JPA annotations, you can specify FetchType.LAZY
on non-collection fields (i.e. @ManyToOne
and @OneToOne
). It seems Hibernate internally uses "proxy" fetching in this case. But proxy fetching has its problems with inheritance and I think it is better to use no-proxy fetching in combination with bytecode instrumentation. Unfortunately, Hibernate still requires you either to specify "no-proxy" in the hbm
-file or to use the Hibernate-specific @LazyToOne
annotation.
My question is: does Hibernate support a configuration option to use a no-proxy fetch strategy for all non-collection fields, that are FetchType.LAZY
?
Here is what I need this for: I would like to use only JPA annotations in most cases on the one hand. On the other hand, I'd like to avoid issues with inheritance and lazy fields. And I don't like the idea of wrapping everything in interfaces, because I use DDD in my current project, so I think that no boilerplate garbage should exist in my domain model, only pure business logic.
I have an idea of a poor workaround: by using bytecode modification, I add @LazyToOne
annotation everywhere @ManyToOne
appears. But I would prefer a built-in Hibernate feature, if exists.
Here is (well known) issue with proxy fetching, to make things a bit clearer:
@Entity @DiscriminatorColumn("t") @DiscriminatorValue("")
public abstract class A {
@Id private Integer id;
}
@Entity @DiscriminatorValue("B")
public abstract class B extends A {
}
@Entity @DiscriminatorValue("C")
public abstract class C extends A {
}
@Entity public class D {
@Id private Integer id;
@ManyToOne(fetch = FetchType.LAZY) private A a;
public A getA() {
return a;
}
}
prepare:
D d = new D();
C c = new C();
d.setA(c);
em.persist(d);
and fail assertion (in another EM, another transaction):
D d = em.createQuery("select d from D d", D.class).getSingleResult();
List<C> cs = em.createQuery("select c from C c", C.class).getResultList();
assert d.getA() instanceof C;
assert d.getA() == cs.get(0);
Here's what I would do to fix the assertions above:
@Entity public class D {
@Id private Integer id;
@ManyToOne(fetch = FetchType.LAZY) @LazyToOne(LazyToOneOption.NO_PROXY)
private A a;
public A getA() {
return a;
}
}
And I don't want the same thing to be enabled by default, without @LazyToOne
annotation.
Ok, I gave up receiving an answer. I carefully examined Hibernate source code and made conclusion that Hibernate itself has no property to achieve what I wish to. But I came up with a little dirty hack, that gives me exactly what I want. So, here it is:
public class DirtyHackedHibernatePersistence extends HibernatePersistence {
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public EntityManagerFactory createEntityManagerFactory(String persistenceUnitName,
Map properties) {
properties.put(AvailableSettings.PROVIDER, HibernatePersistence.class.getName());
Ejb3Configuration cfg = new Ejb3Configuration().configure(persistenceUnitName, properties);
if (cfg == null) {
return null;
}
cfg.buildMappings();
hackConfiguration(cfg);
return cfg.buildEntityManagerFactory();
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info,
Map properties) {
properties.put(AvailableSettings.PROVIDER, HibernatePersistence.class.getName());
Ejb3Configuration cfg = new Ejb3Configuration().configure(info, properties);
if (cfg == null) {
return null;
}
cfg.buildMappings();
hackConfiguration(cfg);
return cfg.buildEntityManagerFactory();
}
private void hackConfiguration(Ejb3Configuration cfg) {
System.out.println("Hacking configuration");
String noProxyByDefault = cfg.getProperties().getProperty("hibernate.hack.no-proxy-by-default", "false");
if (Boolean.parseBoolean(noProxyByDefault)) {
Iterator<?> iter = cfg.getClassMappings();
while (iter.hasNext()) {
hackClass((PersistentClass)iter.next());
}
}
}
private void hackClass(PersistentClass classMapping) {
Iterator<?> iter = classMapping.getPropertyIterator();
while (iter.hasNext()) {
Property property = (Property)iter.next();
if (property.getValue() instanceof ToOne) {
ToOne toOne = (ToOne)property.getValue();
if (toOne.isLazy()) {
toOne.setUnwrapProxy(true);
}
}
}
}
}
also there must be a resource named META-INF/services/javax.persistence.spi.PersistenceProvider
containing a single line with name of the class.
To use this hack, you should specify the following in a persistence.xml
:
<provider>packagename.DirtyHackedHibernatePersistence</provider>
<properties>
<property name="hibernate.hack.no-proxy-by-default" value="true"/>
</properties>
The full example is available here.
Note that it if you remove the hibernate.hack.no-proxy-by-default
property and rebuild project, both assertions get broken.
Also I am going to post a feature request to the Hibernate team.
这篇关于默认情况下,在Hibernate中为所有FetchType.LAZY非集合启用无代理行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!