gpt4 book ai didi

java - 在 Hibernate 中默认为所有 FetchType.LAZY 非集合启用无代理行为

转载 作者:搜寻专家 更新时间:2023-10-30 19:46:48 26 4
gpt4 key购买 nike

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

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

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

我有一个糟糕的解决方法:通过使用字节码修改,我在所有出现 @ManyToOne 的地方添加了 @LazyToOne 注释。但我更喜欢内置的 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 的资源,其中包含一行类名。

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

<provider>packagename.DirtyHackedHibernatePersistence</provider>
<properties>
<property name="hibernate.hack.no-proxy-by-default" value="true"/>
</properties>

完整示例可用here .

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

我还将向 Hibernate 团队提交功能请求。

关于java - 在 Hibernate 中默认为所有 FetchType.LAZY 非集合启用无代理行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19928411/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com