使用 @LazyToOne(LazyToOneOption.NO_PROXY) 进行 Hibernate 延迟加载

Hibernate Lazy Loading with @LazyToOne(LazyToOneOption.NO_PROXY)

我有一个应用 运行 Hibernate 4.2.21 on JBoss AS 7.2

我们目前有一些@OneToOne 关系,由于 known limitations of lazy loading,它们总是会在反面急切地获取。

为了启用反向关系的延迟加载,我正在尝试启用构建时字节码检测。

这是我到目前为止所做的...

1) 使用 maven-antrun-plugin 激活检测(我尝试了 hibernate-enhance-maven-plugin 但无法正常工作,但那是另一个问题),我现在在构建中得到以下 maven 输出日志:

[INFO] --- maven-antrun-plugin:1.7:run (Instrument domain classes) @ MyApp-entities ---
[INFO] Executing tasks

instrument:
[instrument] starting instrumentation
[INFO] Executed tasks

2) 接下来我将所有@OneToOne关系注释如下...

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "client", optional=false)
    @LazyToOne(LazyToOneOption.NO_PROXY)
    public ClientPrefs getClientPrefs() {
        return clientPrefs;
    }

    public void setClientPrefs(ClientPrefs clientPrefs) {
        this.clientPrefs = clientPrefs;
    }

3) 然后我将 implement FieldHandled 添加到 @Entity 类 以及私有字段和 getter 和 setter:

private FieldHandler fieldHandler;

成功...我现在在部署日志中得到以下输出:

15:54:09,720 INFO  [org.hibernate.tuple.entity.EntityMetamodel] (ServerService Thread Pool -- 56) HHH000157: Lazy property fetching available for: uk.co.myapp.entities.Session
15:54:09,730 INFO  [org.hibernate.tuple.entity.EntityMetamodel] (ServerService Thread Pool -- 57) HHH000157: Lazy property fetching available for: uk.co.myapp.entities.Session
15:54:09,969 INFO  [org.hibernate.tuple.entity.EntityMetamodel] (ServerService Thread Pool -- 56) HHH000157: Lazy property fetching available for: uk.co.myapp.entities.Client
15:54:09,970 INFO  [org.hibernate.tuple.entity.EntityMetamodel] (ServerService Thread Pool -- 57) HHH000157: Lazy property fetching available for: uk.co.myapp.entities.Client
15:54:09,999 INFO  [org.hibernate.tuple.entity.EntityMetamodel] (ServerService Thread Pool -- 56) HHH000157: Lazy property fetching available for: uk.co.myapp.entities.Country
15:54:10,003 INFO  [org.hibernate.tuple.entity.EntityMetamodel] (ServerService Thread Pool -- 57) HHH000157: Lazy property fetching available for: uk.co.myapp.entities.Country
15:54:10,054 INFO  [org.hibernate.tuple.entity.EntityMetamodel] (ServerService Thread Pool -- 56) HHH000157: Lazy property fetching available for: uk.co.myapp.entities.Pool
15:54:10,054 INFO  [org.hibernate.tuple.entity.EntityMetamodel] (ServerService Thread Pool -- 57) HHH000157: Lazy property fetching available for: uk.co.myapp.entities.Pool
15:54:10,569 INFO  [org.hibernate.tuple.entity.EntityMetamodel] (ServerService Thread Pool -- 56) HHH000157: Lazy property fetching available for: uk.co.myapp.entities.User
15:54:10,624 INFO  [org.hibernate.tuple.entity.EntityMetamodel] (ServerService Thread Pool -- 57) HHH000157: Lazy property fetching available for: uk.co.myapp.entities.User

关系现在不再急切加载...但它们也不会延迟加载,它们只是 return null 静默。

我尝试从实体中删除 FieldHandled 接口和 FieldHandler 字段,因为我不确定这是否有必要,此后我不再收到 'HHH000157: Lazy property fetching available for:' 消息启动并默认返回急切加载。

我是不是漏掉了什么?休眠文档没有明确说明如何实际设置它

编辑:根据评论添加了 Ant 任务配置:

<build>
        <plugins>
               <plugin>
                    <artifactId>maven-antrun-plugin</artifactId>
                    <version>1.7</version>
                    <executions>
                        <execution>
                            <phase>process-classes</phase>
                            <id>Instrument domain classes</id>
                            <configuration>
                                <target name="instrument">
                                    <taskdef name="instrument"
                                        classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
                                        <classpath>
                                            <path refid="maven.dependency.classpath" />
                                            <path refid="maven.plugin.classpath" />
                                        </classpath>
                                    </taskdef>
                                    <instrument verbose="true">
                                        <fileset dir="${project.build.outputDirectory}">
                                            <include name="MyApp-entities/co/uk/myapp/entities/*.class" />
                                        </fileset>
                                    </instrument>
                                </target>
                            </configuration>
                            <goals>
                                <goal>run</goal>
                            </goals>
                        </execution>
                    </executions>
                    <dependencies>
                        <dependency>
                            <groupId>org.hibernate</groupId>
                            <artifactId>hibernate-core</artifactId>
                            <version>4.2.21.Final</version>
                        </dependency>

                        <dependency>
                            <groupId>org.javassist</groupId>
                            <artifactId>javassist</artifactId>
                            <version>3.18.1-GA</version>
                        </dependency>
                    </dependencies>
                </plugin>
           </plugins>
    </build>

@LazyToOne(LazyToOneOption.NO_PROXY) 在您的 persistance.xml

中需要 <property name="hibernate.ejb.use_class_enhancer" value="true"/>

懒惰,在请求引用时返回加载的真实对象(此选项必须进行字节码增强,如果class未增强则回退到PROXY) 除非您负担不起使用代理,否则应避免使用此选项

NO_PROXY option

像往常一样这是一个配置问题,似乎 ant 运行 插件需要指向包含实体的确切目录而不是父目录之一

这对我有用...

<instrument verbose="true">
    <fileset dir="${project.build.directory}/classes/uk/co/myapp/entities">
        <include name="*.class" />
    </fileset>
</instrument>

你应该伪造一对多关系。这将起作用,因为延迟加载集合比延迟加载单个可空 属性 容易得多,但通常如果您使用复杂的 JPQL/HQL 查询,此解决方案非常不方便。

另一种是使用构建时字节码检测。有关详细信息,请阅读 Hibernate 文档:19.1.7。使用惰性 属性 抓取。请记住,在这种情况下,您必须将 @LazyToOne(LazyToOneOption.NO_PROXY) 注释添加到一对一关系中以使其变得惰性。将 fetch 设置为 LAZY 是不够的。

最后一个解决方案是使用运行时字节码检测,但它仅适用于那些在成熟的 JEE 环境中使用 Hibernate 作为 JPA 提供程序的人(在这种情况下,将 "hibernate.ejb.use_class_enhancer" 设置为 true 应该可以解决问题:实体管理器配置)或使用 Hibernate Spring 配置来执行运行时织入(这在一些较旧的应用程序服务器上可能很难实现)。在这种情况下,@LazyToOne(LazyToOneOption.NO_PROXY) 注释也是必需的。