在Hibernate中惰性加载CLOB

16

谷歌搜索一下就能找到很多相关信息,但是我还没有找到一个可行的解决方案。

基本上,我有一个大的CLOB在一个特定的类上,我希望它能够按需加载。最朴素的做法是:

class MyType {

  // ...

  @Basic(fetch=FetchType.LAZY)
  @Lob
  public String getBlob() {
    return blob;
  }
}

然而这并不起作用,显然是因为我正在使用 Oracle 驱动程序,即 Lob 对象并不被视为简单的句柄,而是始终被加载。或者说,从我的尝试中我被引导相信了这一点。有一种解决方法使用特殊的工具来实现懒加载属性,但是由于 Hibernate 文档似乎表明他们对使其正常工作不太感兴趣,所以我不想走这条路。尤其是需要运行额外的编译过程。

因此,我设想的下一个解决方案是将此对象分离到另一个类型中并定义一个关联。不幸的是,尽管文档提供了冲突的信息,但很明显对我来说,共享主键的 OneToOne 关联没有启用懒加载。我会将关联的一侧设置为 ManyToOne,但是当存在共享主键时,我不太确定如何操作。

那么,有人能否建议最佳方法?


你能解释一下为什么它不能与Oracle一起使用吗? - skaffman
我稍微详细地阐述了一下,但我不确定确切的问题是什么(细节有点模糊)。如果问题出在映射上,你能否提供一个应该正确进行懒加载的 CLOB 映射? - wds
如果你尝试使用Oracle,但失败了,它是如何失败的? - skaffman
1
它并没有像这样失败,Hibernate只是生成查询,其中始终获取完整的CLOB(它在选择中),无论是否将其标记为lazy。 - wds
这也发生在sqlserver和jtds驱动程序中。根本没有懒加载。 - Tommy
4个回答

8
根据这个,只有PostgreSQL真正实现了懒加载Blob。所以最好的解决方法是将Blob移动到另一个表中。您需要使用共享主键吗?为什么不尝试像这样做:
public class MyBlobWrapper {
    @Id
    public Long getId() {
       return id;
    }
    @Lob
    public String getBlob() {
        return blob;
    }
    @OneToOne(fetch=FetchType.LAZY,optional=false) 
    public MyClass getParent() {
        return parent;
    }
}

Blob只是同一张表中的一个字段,所以是的,必须使用共享主键。只要在拥有方设置了optional="false",您的方法就可以工作。我假设当对象为空时,这种方法会出现严重错误? - wds
当然,它永远不会为空,因为主键存在。只有 clob 字段可能为空。有点小失误,谢谢提醒。 :-) - wds
这是父类(MyType)侧面必要的映射,你可能想把它包含在你的回答中: @OneToOne(fetch=FetchType.LAZY,optional=false) - wds
完成了,我添加了建议的参数。 - Tadeusz Kopec for Ukraine
好的,但它需要在父类中,就像你的例子中的 MyClass。 - wds
我知道这是一个相当古老的帖子,但我想对这个最流行的答案添加一些评论。在Oracle 11g中,(C/B)lob的懒加载也可以工作,但它需要像@Edwin Dalorzo建议的那样进行字节码检测 - 使用Hibernate 5进行了检查(它可能在2009年没有工作)。 - Paweł Dulęba

5

不必在Hibernate注释上做平衡术,可以尝试将字段从String转换为Clob(或Blob):

@Lob  
@Basic(fetch=FetchType.LAZY)  
@Column(name = "FIELD_COLUMN")  
public Clob getFieldClob() {  
  return fieldClob;  
}  

public void setFieldClob(Clob fieldClob) {  
  this.fieldClob = fieldClob;  
}  

@Transient  
public String getField()  
{  
  if (this.getFieldClob()==null){  
    return null;  
  }  
  try {  
    return MyOwnUtils.readStream(this.getFieldClob().getCharacterStream());  
  } catch (Exception e) {  
    e.printStackTrace();  
  }  

  return null;  
}  

public void setField(String field)  
{  
  this.fieldClob = Hibernate.createClob(field);  
} 

对我有用(该字段开始在Oracle上懒加载)。


这会导致该字段如何被懒加载?据我所知,这只是添加了方便的getter/setter以将lob作为字符串访问? - Ben George
注意:在我的答案中,Lob注释被放在Clob getter上,而不是问题中的String getter上。我不直接使用Clob字段,而是仅通过一个短暂的getter来使用它。警告:我尝试过的Hibernate版本是3.2(如果我没记错的话)。为了验证加载确实是延迟的,我使用Wireshark检查了与数据库之间的通信。 - John Donn

4

看起来你正在使用Hibernate,我想知道你的问题是否与以下Hibernate功能相关:

使用延迟属性获取

Hibernate3支持单个属性的延迟获取。这种优化技术也被称为获取组。请注意,这主要是一项营销功能;优化行读取比优化列读取更重要。但是,在极端情况下,仅加载类的某些属性可能会很有用。例如,当遗留表具有数百列且数据模型无法改进时。

延迟属性加载需要构建时字节码注入。如果您的持久类未增强,则Hibernate将忽略延迟属性设置并返回立即获取。

请参见使用Maven为Hibernate进行字节码注入


1

虽然这篇文章比较老,但只有@TadeuszKopec的回答对我有帮助,谢谢。

看起来使用JPA实现blob的懒加载比较困难。我尝试过使用@OneToOne关联,但它只会让问题更加复杂。我只是将字节移到另一个类中,与MyClass(父级)没有任何关联(同一张表,同一个id):

@Entity
@Table(name="MyTable")
public class MyBlobWrapper{

    @Id
    @Column(name = "id") // id of MyTable, same as MyClass
    private Long id;

    @Lob
    private byte[] bytes;   
}

@Entity
@Table(name="MyTable")
public class MyClass{

    @Id
    @Column(name = "id")
    private Long id;
    // other fields  .....
}

在保存blob之前,记得先清空父级:

 em.persist(parent);
 em.flush();
 em.merge(new MyBlobWrapper(parent_id,new byte[1000]));

现在我可以单独加载PDF:
String query1 = " select PDF from MyBlobWrapper PDF where PDF.id = :id";

我只是一个JPA的初学者,希望这有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接