There are some disadvantages to the
Lightweight Class, including the ones already mentioned in the link, plus the fact that client code has to know about the lightweight and heavyweight classes and decide between them.
Hibernate's
CGLib proxies can be used to prevent all of the object's many-to-one properties from loading, but cannot be used for fine-grained loading of only certain properties.
I presume (can anyone confirm? I'll get around to testing this if nobody confirms) that if two Hibernate proxies are joined together by a graph, then instantiating the first proxy will not provoke instantiation of the second.
Here's a third solution which allows fine-grained loading of only certain entity properties (as in Lightweight/Heavyweight but without its disadvantages), using the Proxy pattern as well. It is based on the Document class described in the Lightweight pattern (minus a couple of properties).
Code:
public interface Document {
public Long getKey();
public String getName();
public void setName(String name);
public Folder getFolder();
public void setFolder(Folder folder);
public Clob getText();
public void setText(Clob text);
}
class DocumentImpl implements Document {
private Long _key;
private String _name;
private Folder _folder;
private Clob _text;
public Long getKey() { return _key; }
protected void setKeyDB(Long key) { _key = key; }
public String getName() { return _name; }
public void setName(String name) { _name = name; }
public Folder getFolder() { return _folder; }
public void setFolder(Folder folder) { _folder = folder; }
public Clob getText() { return _text; }
public void setText(Clob text) { _text = text; }
}
class DocumentProxy implements Document {
private DocumentImpl _subject = null;
private Long _key;
private String _name;
private Folder _folder;
public Long getKey() { return _subject==null ? _key : getSubject().getKey(); }
protected void setKeyDB(Long key) { _key = key; }
public String getName() { return _subject==null ? _name : getSubject().getName(); }
public void setName(String name) { getSubject().setName() }
protected void setNameDB(String name) { _name = name; }
public Folder getFolder() { return _subject==null ? _folder : getSubject().getFolder(); }
public void setFolder(Folder folder) { getSubject().setFolder(folder); }
protected void setFolderDB(Folder folder) { _folder = folder; }
public Clob getText() { return getSubject().getText(); }
public void setText(Clob text) { getSubject().setText(text); }
private DocumentImpl getSubject() {
if (_key==null) {
// ... get Hibernate session
_subject = (DocumentImpl) session.load(DocumentImpl.class, _key);
}
return _subject;
}
}
We can now implement certain methods to return the proxies, but the client doesn't know the difference, since all he gets back is the Document interface. As soon as he tries to get the CLOB, the real subject is loaded for him behind the scenes and the CLOB retrieved.
Something like this:
Code:
public Set findDocumentsByName(String name) throws HibernateException {
// ... get the hibernate session
Iterator results = session.iterate("SELECT document.key, document.name, document.folder"
+ " FROM tests.DocumentImpl AS document"
+ " WHERE document.name LIKE ?"
+ " ORDER BY document.name",
"%" + name + "%",
Hibernate.STRING);
HashSet documents = new LinkedHashSet();
while (results.hasNext()) {
Object[] result = (Object[]) results.next();
Long key = (Long) result[0];
String name = (String) result[1];
Folder folder = (String) result[2];
DocumentProxy document = new DocumentProxy();
document.setKeyDB(key);
document.setNameDB(name);
document.setFolderDB(folder);
documents.add(document);
}
return documents;
}
Note that you could implement a graph of objects as proxies (say, we could make Folder a proxy), and fill in the entire graph as above.
Any thoughts? Is this a good approach? I know there is nothing revolutionary about this, but is it worth putting up on the Wiki alongside the Lightweight/Heavyweight pattern?
Best regards,
Assaf