使用标准JPA注释时,可以FetchType.LAZY在非集合字段(即@ManyToOne和@OneToOne)上指定。在这种情况下,Hibernate似乎在内部使用“代理”获取。但是代理获取在继承方面存在问题,我认为最好将无代理获取与字节码检测结合使用。不幸的是,Hibernate仍然要求您在hbm-file中指定“ no-proxy” 或使用特定于Hibernate的@LazyToOne注释。
FetchType.LAZY
@ManyToOne
@OneToOne
hbm
@LazyToOne
我的问题是:Hibernate是否支持配置选项,以便对所有非收集字段使用无代理获取策略FetchType.LAZY?
这是我需要的:一方面,在大多数情况下,我只想使用JPA批注。另一方面,我想避免继承和惰性字段的问题。而且我不喜欢将所有内容包装在接口中的想法,因为我在当前项目中使用了DDD,所以我认为域模型中不应该存在任何样板垃圾,而只能是纯业务逻辑。
我有一个较差的解决方法的想法:通过使用字节码修改,我在@LazyToOne所有@ManyToOne出现的地方添加注释。但是我更喜欢内置的Hibernate功能(如果存在)。
这是代理获取的(众所周知的)问题,这使事情更清楚了:
@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<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本身不具备实现我所希望的属性。但是我想出了一个肮脏的技巧,这正是我想要的。因此,这里是:
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其中包含带有类名称的一行。
META- INF/services/javax.persistence.spi.PersistenceProvider
要使用此技巧,您应在中指定以下内容persistence.xml:
persistence.xml
<provider>packagename.DirtyHackedHibernatePersistence</provider> <properties> <property name="hibernate.hack.no-proxy-by-default" value="true"/> </properties>
完整的示例可在此处获得。
请注意,如果删除该hibernate.hack.no-proxy-by-default属性并重建项目,则两个断言都会被破坏。
hibernate.hack.no-proxy-by-default
我还将向Hibernate团队发布功能请求。