Hibernate version: 3.2.5ga
Mapping documents:
User.hbm.xml
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//END" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="model.dom.User" table="user" >
<id name="id" column="userName" type="string" unsaved-value="null" />
<property name="email" />
<property name="fullName" />
<property name="password" />
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():Using HQL:
Code:
User user = (User) getHibernateTemplate().execute(new HibernateCallback()
{
/* (non-Javadoc)
* @see org.springframework.orm.hibernate3.HibernateCallback#doInHibernate(org.hibernate.Session)
*/
public Object doInHibernate(Session session) throws HibernateException, SQLException
{
return session.createQuery("from User where id = :username").setString("username", id).uniqueResult();
}
});
return user;
Using Spring's HibernateTemplate:
Code:
return (User) org.springframework.orm.hibernate3.support.HibernateDAOSupport.getHibernateTemplate().get(User.class, id);
Spring's HibernateTemplate ends up delegating to org.hibernate.Session.get(Object, Serializeable)
Name and version of the database you are using: MySQL 5.0 I'll preface this by saying that I'm using a case-insensitive collation (utf8_unicode_ci) for a PK column in my User table which is probably not a good idea. That said, I'm running into what I believe to be somewhat of an inconsistency in Hibernate.
The two above methods for finding a User by its primary key seem to be equivalent: HQL Query and a Session.get(). However, I'm finding that if I have a User with username "admin" in my database and I pass "Admin" for the input id, I get slightly different results.
In both case, the record is found because as I said the column is using a case insensitive collation. However, when using the HibernateTemplate.get() call I see that the "id" property of the found User object is "Admin" instead of "admin" as it is in the database. When using HQL the returned object has an id of "admin" as it is in the database.
I believe I have traced this down to org.hibernate.loader.Loader#getKeyFromResultSet():
Code:
/**
* Read a row of <tt>Key</tt>s from the <tt>ResultSet</tt> into the given array.
* Warning: this method is side-effecty.
* <p/>
* If an <tt>id</tt> is given, don't bother going to the <tt>ResultSet</tt>.
*/
private EntityKey getKeyFromResultSet(
final int i,
final Loadable persister,
final Serializable id,
final ResultSet rs,
final SessionImplementor session) throws HibernateException, SQLException {
Serializable resultId;
// if we know there is exactly 1 row, we can skip.
// it would be great if we could _always_ skip this;
// it is a problem for <key-many-to-one>
if ( isSingleRowLoader() && id != null ) {
resultId = id;
}
else {
Type idType = persister.getIdentifierType();
resultId = (Serializable) idType.nullSafeGet(
rs,
getEntityAliases()[i].getSuffixedKeyAliases(),
session,
null //problematic for <key-many-to-one>!
);
final boolean idIsResultId = id != null &&
resultId != null &&
idType.isEqual( id, resultId, session.getEntityMode(), factory );
if ( idIsResultId ) resultId = id; //use the id passed in
}
return resultId == null ?
null :
new EntityKey( resultId, persister, session.getEntityMode() );
}
which simply sets the id of the returned object as the passed in id instead of the one found in the result set.
This is a problem for me because I'm trying to do a Java comparison between the id of the fetched entity and the input id in order to account for the case-insensitive nature of the column.
I have a few questions:
- Why isn't a similar thing being done when I perform the operation using HQL?
- Is there a way to force Session.get() to load the id from the result set instead of using the passed in one?
- Also, what does the method comment mean when it says its: "side-effecty"?