Ale*_*eev 14 java hibernate jpa
使用标准JPA注释时,您可以FetchType.LAZY在非集合字段(即@ManyToOne和@OneToOne)上指定.在这种情况下,似乎Hibernate内部使用"代理"提取.但代理提取有其继承问题,我认为最好将无代理提取与字节码检测结合使用.不幸的是,Hibernate仍然要求你在hbm-file中指定"no-proxy" 或者使用特定于Hibernate的@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;
}
}
Run Code Online (Sandbox Code Playgroud)
准备:
D d = new D();
C c = new C();
d.setA(c);
em.persist(d);
Run Code Online (Sandbox Code Playgroud)
并且失败断言(在另一个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);
Run Code Online (Sandbox Code Playgroud)
以下是我要修复上述断言的方法:
@Entity public class D {
@Id private Integer id;
@ManyToOne(fetch = FetchType.LAZY) @LazyToOne(LazyToOneOption.NO_PROXY)
private A a;
public A getA() {
return a;
}
}
Run Code Online (Sandbox Code Playgroud)
而且我不希望在没有@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);
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
还必须有一个名为META-INF/services/javax.persistence.spi.PersistenceProvider包含单行类名称的资源。
要使用此 hack,您应该在 a 中指定以下内容persistence.xml:
<provider>packagename.DirtyHackedHibernatePersistence</provider>
<properties>
<property name="hibernate.hack.no-proxy-by-default" value="true"/>
</properties>
Run Code Online (Sandbox Code Playgroud)
完整的示例可以在这里找到。
请注意,如果删除该hibernate.hack.no-proxy-by-default属性并重建项目,则两个断言都会被破坏。
另外,我将向 Hibernate 团队发布功能请求。
| 归档时间: |
|
| 查看次数: |
3734 次 |
| 最近记录: |