There's a lot one can find about this googling a bit but I haven't quite found a workable solution to this problem.
Basically what I have is a big CLOB on a particular class that I want to have loaded on demand. The naive way to do this would be:
class MyType {
// ...
@Basic(fetch=FetchType.LAZY)
@Lob
public String getBlob() {
return blob;
}
}
That doesn't work though, apparently due to the fact I'm using oracle drivers, i.e. Lob objects aren't treated as simple handles but are always loaded. Or so I've been led to believe from my forays. There is one solution that uses special instrumentation for lazy property loading, but as the Hibernate docs seem to suggest they're less than interested in making that work correctly, so I'd rather not go that route. Especially with having to run an extra compile pass and all.
So the next solution I had envisioned was separating out this object to another type and defining an association. Unfortunately, while the docs give conflicting information, it's apparent to me that lazy loading doesn't work on OneToOne associations with shared primary key. I'd set one side of the association as ManyToOne, but I'm not quite sure how to do this when there's a shared primary key.
So can anybody suggest the best way to go about this?
According to this only PostgreSQL implements Blob as really lazy. So the best solution is to move the blob to another table. Do you have to use a shared primary key? Why don't you do something like this:
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;
}
}
Instead of doing equilibristics with hibernate annotations, one may just try converting the field from String
into Clob
(or 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);
}
Worked for me (the field started to load lazily, on Oracle).
Since you appear to be using Hibernate I wonder if your problem is related to the following Hibernate feature:
Using Lazy Properties Fetching
Hibernate3 supports the lazy fetching of individual properties. This
optimization technique is also known as fetch groups. Please note that
this is mostly a marketing feature; optimizing row reads is much more
important than optimization of column reads. However, only loading
some properties of a class could be useful in extreme cases. For
example, when legacy tables have hundreds of columns and the data
model cannot be improved.
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.
See Bytecode Instrumentation for Hibernate Using Maven.
Old post, but only one that helped me, thanks to @TadeuszKopec answer.
Looks like it is hard to do lazy loading of blob with JPA. I tried @OneToOne association, but it complicates more than help.
I just moved the bytes to another class, with no association with MyClass (parent. Same table, same 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 .....
}
Just remember to flush parent, before saving the blob:
em.persist(parent);
em.flush();
em.merge(new MyBlobWrapper(parent_id,new byte[1000]));
Now I can load the pdf alone:
String query1 = " select PDF from MyBlobWrapper PDF where PDF.id = :id";
I am just beginner with JPA, hope that helps.