使用JPA和Hibernate延迟加载实体属性的最佳方法 - Vlad Mihalcea

栏目: Hibernate · 发布时间: 5年前

内容简介:获取实体时,也会加载所有属性。这是因为每个隐式使用但是,属性获取策略可以设置为FetchType.LAZY,在这种情况下,实体属性只有在第一次访问时才加载,通过select语言的执行。@Basic(fetch = FetchType.LAZY)

获取实体时,也会加载所有属性。这是因为每个隐式使用 @Basic 实体属性提取策略都默认FetchType.EAGER。

但是,属性获取策略可以设置为FetchType.LAZY,在这种情况下,实体属性只有在第一次访问时才加载,通过select语言的执行。

@Basic(fetch = FetchType.LAZY)

仅此配置是不够的,因为Hibernate需要字节码检测来拦截属性访问请求并按需发出select语句。

使用Maven字节码增强插件时,enableLazyInitialization必须将配置属性设置true为以下示例中所示:

<plugin>
    <groupId>org.hibernate.orm.tooling</groupId>
    <artifactId>hibernate-enhance-maven-plugin</artifactId>
    <version>${hibernate.version}</version>
    <executions>
        <execution>
            <configuration>
                <failOnError><b>true</b></failOnError>
                <enableLazyInitialization><b>true</b></enableLazyInitialization>
            </configuration>
            <goals>
                <goal>enhance</goal>
            </goals>
        </execution>
    </executions>
</plugin>

有了这个配置,所有JPA实体类都将使用延迟属性获取进行检测。此过程发生在构建时,就在从关联的源文件编译实体类之后。

与存储大量数据的列类型处理当属性延迟抓取机构是非常有用的(例如BLOB,CLOB,VARBINARY)。这样,可以在不自动从基础大型列类型中加载数据的情况下获取实体,从而提高性能。

演示

为了演示属性延迟提取的工作原理,以下示例将使用Attachment可以存储任何媒体类型的实体(例如PNG,PDF,MPEG)。

@Entity @Table(name = <font>"attachment"</font><font>)
<b>public</b> <b>class</b> Attachment {
 
    @Id
    @GeneratedValue
    <b>private</b> Long id;
 
    <b>private</b> String name;
 
    @Enumerated
    @Column(name = </font><font>"media_type"</font><font>)
    <b>private</b> MediaType mediaType;
 
    @Lob
    @Basic(fetch = FetchType.LAZY)
    <b>private</b> byte[] content;
 
    </font><font><i>//Getters and setters omitted for brevity</i></font><font>
}
</font>

在每个实体负载上都要急切地获取诸如实体标识符,名称或媒体类型之类的属性。另一方面,只有在被应用程序代码访问时,才应该懒惰地获取媒体文件内容。

在检测Attachment实体后,类字节码更改如下:

@Transient
<b>private</b> <b>transient</b> PersistentAttributeInterceptor
    $$_hibernate_attributeInterceptor;
 
<b>public</b> byte[] getContent() {
    <b>return</b> $$_hibernate_read_content();
}
 
<b>public</b> byte[] $$_hibernate_read_content() {
    <b>if</b> ($$_hibernate_attributeInterceptor != <b>null</b>) {
        <b>this</b>.content = ((byte[])
            $$_hibernate_attributeInterceptor.readObject(
                <b>this</b>, <font>"content"</font><font>, <b>this</b>.content));
    }
    <b>return</b> <b>this</b>.content;
}
</font>

执行以下测试用例时:

Attachment book = entityManager.find(
    Attachment.<b>class</b>, bookId);
 
LOGGER.debug(<font>"Fetched book: {}"</font><font>, book.getName());
 
assertArrayEquals(
    Files.readAllBytes(bookFilePath),
    book.getContent()
);
</font>

Hibernate生成以下 SQL 查询:

SELECT a.id AS id1_0_0_,
       a.media_type AS media_ty3_0_0_,
       a.name AS name4_0_0_
FROM   attachment a
WHERE  a.id = 1
 
-- Fetched book: High-Performance Java Persistence
 
SELECT a.content AS content2_0_
FROM   attachment a
WHERE  a.id = 1

因为它标记有FetchType.LAZY注释并且启用了延迟提取字节码增强,所以content不会提取该列以及初始化Attachment实体的所有其他列。只有当数据访问层尝试访问该content属性时,Hibernate才会发出辅助选择以加载此属性。

就像FetchType.LAZY关联一样,这种技术很容易出现 N + 1个查询问题 ,因此建议谨慎行事。字节码增强机制的一个细微缺点是所有实体属性,而不仅仅是标记有FetchType.LAZY注释的属性,将被转换,如前所述。

获取子实体

另一种避免加载相当大的表列的方法是将多个子实体映射到同一个数据库表。

比如BaseAttachment有两个子类Attachment实体和AttachmentSummary。

使用JPA和Hibernate延迟加载实体属性的最佳方法 - Vlad Mihalcea

无论是Attachment实体和AttachmentSummary子实体继承BaseAttachment所有公共属性

@MappedSuperclass
<b>public</b> <b>class</b> BaseAttachment {
 
    @Id
    @GeneratedValue
    <b>private</b> Long id;
 
    <b>private</b> String name;
 
    @Enumerated
    @Column(name = <font>"media_type"</font><font>)
    <b>private</b> MediaType mediaType;
 
    </font><font><i>//Getters and setters omitted for brevity</i></font><font>
}
</font>

虽然AttachmentSummary扩展BaseAttachment而没有声明任何新属性:

@Entity @Table(name = <font>"attachment"</font><font>)
<b>public</b> <b>class</b> AttachmentSummary
    <b>extends</b> BaseAttachment {}
</font>

Attachment实体继承超类BaseAttachment所有基本属性并映射content列。

@Entity @Table(name = <font>"attachment"</font><font>)
<b>public</b> <b>class</b> Attachment
    <b>extends</b> BaseAttachment {
 
    @Lob
    <b>private</b> byte[] content;
 
    </font><font><i>//Getters and setters omitted for brevity</i></font><font>
}
</font>

当抓取AttachmentSummary子实体时:

AttachmentSummary bookSummary = entityManager.find(
    AttachmentSummary.<b>class</b>, bookId);

产生SQL:

SELECT a.id as id1_0_0_,
       a.media_type as media_ty2_0_0_,
       a.name as name3_0_0_
FROM attachment a
WHERE  a.id = 1

当抓取Attachment 实体时:

Attachment book = entityManager.find(
    Attachment.<b>class</b>, bookId);

Hibernate将从底层数据库表中获取所有列:

SELECT a.id as id1_0_0_,
       a.media_type as media_ty2_0_0_,
       a.name as name3_0_0_,
       a.content as content4_0_0_
FROM attachment a
WHERE  a.id = 1

结论

对于延迟获取实体属性,您可以使用字节码增强或子实体两种方式。

虽然字节码检测允许您每个表只使用一个实体,但子实体更灵活,甚至可以提供更好的性能,因为它们在读取实体属性时不涉及拦截器调用。

子实体方式其实就是将由大数据如图片或文件的对象和文字小数据的对象分开。

在读取数据时,子实体与 DTO投影 非常相似。但是,与DTO投影不同,子实体可以跟踪状态更改并将它们传播到数据库。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Linux多线程服务端编程

Linux多线程服务端编程

陈硕 / 电子工业出版社 / 2013-1-15 / 89.00元

本书主要讲述采用现代C++ 在x86-64 Linux 上编写多线程TCP 网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread。这是在Linux 下以native 语言编写用户态高性能网络程序最成熟的模式,掌握之后可顺利地开发各类常见的服务端网络应用程序。本书以muduo 网络库为例,讲解这种编程模型的使用方法及注意事项。 本......一起来看看 《Linux多线程服务端编程》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

html转js在线工具
html转js在线工具

html转js在线工具