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

Java 默认情况下,在Hibernate中为所有FetchType.LAZY非集合启用无代理行为,java,hibernate,jpa,Java,Hibernate,Jpa,使用标准JPA批注时,可以在非集合字段(即@ManyToOne和@OneToOne)上指定FetchType.LAZY)。在这种情况下,Hibernate似乎在内部使用“代理”抓取。但是代理抓取在继承方面存在问题,我认为最好将无代理抓取与字节码检测结合使用。不幸的是,Hibernate仍然要求您在hbm-文件中指定“无代理”,或者使用特定于Hibernate的@LazyToOne注释 我的问题是:Hibernate是否支持对所有非集合字段(即FetchType.LAZY)使用无代理获取策略的配置

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

D d = new D();
C c = new C();
d.setA(c);
em.persist(d);
和失败断言(在另一个EM中,另一个事务中):


我不希望在没有
@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);
                }
            }
        }
    }
}
完整的示例是可用的

请注意,如果删除
hibernate.hack.no proxy默认属性和rebuild项目,这两个断言都会被破坏


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

我可能要解释一下,无代理抓取有什么帮助?好的,没有代理抓取会修改实体的字节码(也需要启用字节码检测),所以无论我在哪里读取延迟字段的值,它都会插入指令,从数据库中获取字段的值。并且总是获取正确的实例,而不是代理,子类化基类。另外,Hibernate和其他JPA实现之间在延迟抓取方面的性能差异也可能基于这些策略(EclipseLink将这种字节码操作称为“编织”,而OpenJPA称为“增强”)。是的,我阅读了本手册,也没有发现任何配置。但是,有些东西在Hibernate中描述得不够好。所以我想可能有一个未记录的功能?此外,默认情况下,AFAIK、OpenJPA和EclipseLink都使用基于字节码修改的延迟抓取(如果启用了编织,或者除了收集之外,其他都是急切地进行的)。但是转换ORM太痛苦了…嗨Alexey!你创造了票吗?
@Entity public class D {
    @Id private Integer id;
    @ManyToOne(fetch = FetchType.LAZY) @LazyToOne(LazyToOneOption.NO_PROXY)
    private A a;
    public A getA() {
        return a;
    }
}
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);
                }
            }
        }
    }
}
<provider>packagename.DirtyHackedHibernatePersistence</provider>
<properties>
   <property name="hibernate.hack.no-proxy-by-default" value="true"/>
</properties>