I've hit the problem, too. But.. I've found a workaround.
Problem description
What I need to do, is not resolved by a "use load() or get()" answer. I want to use nested examples (nested objects) and filter by a nested object id (as a side effect, the workaround also solves the original issue).
Supossing foo object "has a" bar object, and that barExample, is an example restricted to an specific bar id, I want to perform something like:
Code:
session.createCriteria(Foo.class).add(fooExample).createCriteria("bar").add(barExample);
Hibernate resolves this, by joining foo and bar correctly.. BUT when I use a barExample that restricts the bar id, Hibernate does not append the "where bar.id == ?" clause to the generated SQL query. It retrieves a full join without the restriction I'm impossing to the barExample id.
Does anyone know the reason why it was implemented this way?
If I restrict a barExample property that it's not bar.id it works perfect, and Hibernate appends the "and bar.someProperty like ?" clause to the join.
Please let me know if I'm not being clear with my description of the problem.
Workaround.You will have to add the following code to org.hibernate.criterion.Example.
Note: I'm using hibernate 3.1.3 so, may be the line numbers are different from yours.
Near line: 206 before
Code:
if ( buf.length()==1 ) buf.append("1=1"); //yuck!
Add the following line
Code:
appendPrimaryKeyCondition(criteria,criteriaQuery,buf);
Also add the following method:
Code:
private void appendPrimaryKeyCondition(Criteria criteria, CriteriaQuery cq, StringBuffer buf) {
EntityPersister meta = (EntityPersister)cq.getFactory().getEntityPersister(cq.getEntityName(criteria));
String idName = meta.getIdentifierPropertyName();
Object idValue = meta.getIdentifier(entity, getEntityMode(criteria, cq));
if (!isNullOrEmpty(idValue)) appendPropertyCondition(
idName,
idValue,
criteria,
cq,
buf
);
}
Near line 239 before
Code:
return (TypedValue[]) list.toArray(TYPED_VALUES);
Add the following line:
Code:
appendPrimaryKeyValue(criteria,criteriaQuery,list);
You get the idea..
Now, the correspoding code to that method would be something like:
Code:
private void appendPrimaryKeyValue(Criteria criteria, CriteriaQuery cq, List list) {
EntityPersister meta = (EntityPersister)cq.getFactory().getEntityPersister(cq.getEntityName(criteria));
Type idType = meta.getIdentifierType();
Object idValue = meta.getIdentifier(entity, getEntityMode(criteria, cq));
if (!isNullOrEmpty(idValue)){
addKeyTypedValue(idValue, idType, list);
}
}
private void addKeyTypedValue(Object idValue, Type idType, List list) {
if ( idValue!=null ) {
if ( idValue instanceof String ) {
String string = (String) idValue;
if (isIgnoreCaseEnabled) string = string.toLowerCase();
idValue = string;
}
list.add( new TypedValue(idType, idValue, null) );
}
}
There are only a couple of things left to change..
Replace line 303
Code:
String op = isLikeEnabled && isString ? " like " : "=";
with:
Code:
boolean isPrimaryKey = isPrimaryKey(criteria, cq, propertyName);
String op = isLikeEnabled && isString && !isPrimaryKey ? " like " : "=";
And then, add the following method:
Code:
private boolean isPrimaryKey(Criteria criteria, CriteriaQuery cq, Object propertyName) {
EntityPersister meta = cq.getFactory().getEntityPersister(cq.getEntityName(criteria));
return (propertyName!= null && propertyName.equals(meta.getIdentifierPropertyName()));
}
Tell me if these changes worked for you, and if you think it's worth to send the fix to Gavin.
Thanks.