资讯专栏INFORMATION COLUMN

Hibernate的延迟加载

jollywing / 2623人阅读

摘要:非集合属性非集合属性的延迟加载相对比较复杂。仅通过注解是无法实现延迟加载的。但是在一对一关系中,延迟加载是有陷阱的。这样一来,就会立即为配置延迟加载的实体生成一个动态代理类。

Hibernate中,延迟加载针对属性类别可以分为两类,一类是延迟属性加载,另一类是延迟关联实体加载。

属性延迟加载

属性有可以分为两种类型:一种是集合属性,一种是非集合属性(如String、Integer……)。

集合属性

集合属性的延迟加载通过PersistentSet、 PersistentList、PersistentBag、PersistentMap、PersistentSortedMap、PersistentSortedSet作为代理类来实现,代理类中保存了session以及owner属性,owner属性表示了集合属性所属的one侧的实体。

非集合属性

非集合属性的延迟加载相对比较复杂。仅通过@Basic(fetch = FetchType.LAZY)注解是无法实现延迟加载的。需要让实体实现FieldHandled接口,声明FieldHandler属性,通过拦截器原理注入对应的FieldHandler属性,起到类似于上述代理类的作用,FieldHandler同样也保持了session,以及需要延迟加载的属性。下面的代码实现了非集合属性的延迟加载

@Basic(fetch = FetchType.LAZY)
@Column(name="CONTENT")
public String getContent() {
    if (fieldHandler != null) {
        return (byte[]) fieldHandler.readObject(this, "content", content);
    }
    return null;
}
public void setContent(byte[] content) {
    this.content = content;
}
@Override
public void setFieldHandler(FieldHandler handler) {
    this.fieldHandler = handler;
}
@Override
public FieldHandler getFieldHandler() {
    return this.fieldHandler;
}

Hibernate官网对非结合属性的延迟加载有如下的评论:

Lazy property loading requires buildtime bytecode instrumentation. If your persistent classes are not enhanced, Hibernate will ignore lazy property settings and return to immediate fetching.
A different way of avoiding unnecessary column reads, at least for read-only transactions, is to use the projection features of HQL or Criteria queries. This avoids the need for buildtime bytecode processing and is certainly a preferred solution.

大致的意思就是:应该是因为,我们并未用到编译时字节码增强技术的原因。如果只对部分property进行延迟加载的话,hibernate还提供了另外的方式,也是更为推荐的方式,即HQL或者条件查询。
更为推荐的方式说白了就是在查询的时候就过滤掉不需要的属性,以下列出HQL和Criterial的两种实现方法:

// criterial实现
criteria.setProjection(
                Projections.projectionList().add(Projections.property("id"), "id")
                        .add(Projections.property("age"), "age")).setResultTransformer(
                Transformers.aliasToBean(User.class));

// HQL实现
Query query = session.createQuery("select u.id as id,u.age as age from User u where u.id=2");
query.setResultTransformer(Transformers.aliasToBean(User.class));
关联实体延迟加载

关联实体延迟加载分两种情况,一种是多对一,另一种是一对一。

多对一关联

关联实体是多个实体时(包括一对多、多对多):此时关联实体将以集合的形式存在,Hibernate 将使用 PersistentSet、PersistentList、PersistentMap、PersistentSortedMap、PersistentSortedSet 等集合来管理延迟加载的实体。这就是前面所介绍的情形。

一对一关联

关联实体是单个实体时(包括一对一、多对一):当 Hibernate 加载某个实体时,延迟的关联实体将是一个动态生成代理对象。Hibernate 使用 Javassist 项目动态生成的代理类——当 Hibernate 延迟加载关联实体时,将会采用 Javassist 生成一个动态代理对象,这个代理对象将负责代理“暂未加载”的关联实体。但是在一对一关系中,延迟加载是有陷阱的。一对一关联一般有两种形式,一种是主键关联;另一种是外键关联。

主键关联

数据表Husband,两列属性id,name
数据表Wife,两列属性id,name

Husband实体:
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
@PrimaryKeyJoinColumn
getWife()
@GenericGenerator(name = "Wife", strategy = "foreign", parameters = { @Parameter(name = "property", value = "husband") })
Wife实体:
@OneToOne(mappedBy = "wife", fetch = FetchType.LAZY)
getHusband()

以上是hibernate中的配置。其中“optional=false”的配置时关键,否则即使配置了fetch策略为lazy,也无法做到在获取husband实体的时候延迟加载wife实体。optional的默认值是true,表示关联的实体可以为null。在一对一的延迟加载中,hibernate并非一定对需要延迟加载的实体生成一个动态代理对象,而是当被关联的实体确定不为null时,才会生成,否则直接将其置为null。所以问题就来了,对于两个通过主键关联的一对一实体,在获取到其中一个实体后,要判断与之关联的实体是否存在,则必须要再查询一次数据库才可以。这也就是为什么在设置了延迟加载策略后,hibernate还是立即发送了一次查询请求给数据库。
要解决一对一关系中的延迟加载,共有两种方法:一种就是上面提到的,把optional设置为false,即关联的实体一定不为null。这样一来,hibernate就会立即为配置延迟加载的实体生成一个动态代理类。
但是这又存在一个坑,在创建实体husband的时候,其主键为null(还未生成),wife的主键也为null,此时save的话,hibernate理论上应该先insert husband实体,然后用生成出来的husband_id作为wife的主键进行insert。可事与愿违,hibernate认为此情况违背了optional=false的假设,故会抛异常IdentifierGenerationException,即wife的id为null。具体原因不太知晓,猜测可能是因为持久化的顺序,先持久化husband,此时wife的主键为null,但是你又配置了optional=false,故前后矛盾而抛出异常。

外键关联

数据表Husband,3列属性id,name, wife_id
数据表Wife,两列属性id,name
其中husband_id与Husbande中的id关联

Husband实体:
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "wife_id", , referencedColumnName = "id")
getWife()
Wife实体:
@OneToOne(mappedBy = "wife", fetch = FetchType.LAZY)
getHusband()

这样一来,直接可以通过husband中的wife_id的null与否来判断wife实体是否为null。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64770.html

相关文章

  • Hibernate5.2-5.3版本ManyToOne、OneToOne延迟加载(fetch=L

    摘要:项目使用的版本进行开发,里面使用到,而这个版本自带的实现是的版本。这个版本里面的延迟加载是没有问题的,当你设置时,关联的对象在你没有使用的时候,是不会发出的。这个问题,在升级版本后是解决了的。 项目使用springboot的1.5.19版本进行开发,里面使用到JPA,而springboot这个版本自带的JPA实现是Hibernate的5.0.12版本。 这个版本里面的延迟加载是没有问题...

    blair 评论0 收藏0
  • JAVA面试题(29)

    摘要:本文首发于的博客转载请注明出处逻辑分页和物理分页的区别是什么分页是为了节省网络传输的数据量逻辑分页是将数据全部加载到内存,再通过后端逻辑控制分页显示到前端物理分页是在数据库层面分部分获取数据,通常情况下对内存的压力较逻辑分页少是否支持延迟加     本文首发于cartoon的博客    转载请注明出处:https://cartoonyu.github.io/c... Mybatis...

    junbaor 评论0 收藏0
  • Hibernate最全面试题

    摘要:中怎样实现类之间的关系如一对多多对多的关系中怎样实现类之间的关系如一对多多对多的关系它们通过配置文件中的来实现类之间的关联关系的。 Hibernate常见面试题 Hibernate工作原理及为什么要用? Hibernate工作原理及为什么要用? 读取并解析配置文件 读取并解析映射信息,创建SessionFactory 打开Sesssion 创建事务Transation 持久化操作 提...

    张利勇 评论0 收藏0
  • Hibernate问题集锦

    摘要:查询照样写就行,如下参考问题七中关于多表连接查询和返回值集合中对象问题错误的查询语句释放分析原来是查询出来的字段并不能自动转换为对象。参考问题八原因原生的语句中返回值为,而语句中的返回值位型的,网上说的主要是兼容而做的。 首先奉上Hibernate3.2 API地址:http://docs.jboss.org/hiberna...Hibernate4.3 API地址:http://do...

    niceforbear 评论0 收藏0
  • Mybatis常见面试题

    摘要:执行没有,批处理不支持,将所有都添加到批处理中,等待统一执行,它缓存了多个对象,每个对象都是完毕后,等待逐一执行批处理。 Mybatis常见面试题 #{}和${}的区别是什么? #{}和${}的区别是什么? 在Mybatis中,有两种占位符 #{}解析传递进来的参数数据 ${}对传递进来的参数原样拼接在SQL中 #{}是预编译处理,${}是字符串替换。 使用#{}可以有效的防止...

    liuchengxu 评论0 收藏0

发表评论

0条评论

jollywing

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<