默认情况下,在Hibernate中为所有FetchType.LAZY非集合启用无代理行为

使用标准JPA注释时,可以在非集合字段(即@ManyToOne@OneToOne )上指定FetchType.LAZY 。 在这种情况下,似乎Hibernate内部使用“代理”提取。 但代理提取有其inheritance问题,我认为最好将无代理提取与字节码检测结合使用。 不幸的是,Hibernate仍然要求你在hbm -file中指定“no-proxy”或者使用特定于Hibernate的@LazyToOne注释。

我的问题是:Hibernate是否支持配置选项,对所有非集合字段使用无代理提取策略,即FetchType.LAZY

以下是我需要的内容:一方面,我想在大多数情况下只使用JPA注释。 另一方面,我想避免inheritance和惰性字段的问题。 我不喜欢在接口中包装所有内容的想法,因为我在当前项目中使用DDD,所以我认为我的域模型中不存在样板垃圾,只有纯业务逻辑。

我知道一个不好的解决方法:通过使用字节码修改,我在@ManyToOne出现的地方添加@LazyToOne注释。 但我更喜欢内置的Hibernatefunction,如果存在的话。


这是(众所周知的)代理提取的问题,以使事情更清楚:

 @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; } } 

准备:

 D d = new D(); C c = new C(); d.setA(c); em.persist(d); 

并且失败断言(在另一个EM中,另一个事务):

 D d = em.createQuery("select d from D d", D.class).getSingleResult(); List 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注释,我不希望默认启用相同的function。

好的,我放弃了接受答案。 我仔细检查了Hibernate源代码并得出结论,Hibernate本身没有实现我想要的属性。 但是我想出了一个肮脏的黑客,它给了我我想要的东西。 所以,这里是:

 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); } } } } } 

还必须有一个名为META-INF/services/javax.persistence.spi.PersistenceProvider其中包含一个具有该类名称的单行。

要使用此hack,您应在persistence.xml指定以下内容:

 packagename.DirtyHackedHibernatePersistence    

完整的示例可在此处获得 。

请注意,如果删除hibernate.hack.no-proxy-by-default属性并重建项目,则两个断言都会被破坏。

此外,我将向Hibernate团队发布一个function请求。