使用数据库为mysql5.1.6,hibernate版本为4.3.11.Final。
遇到问题:在使用hibernate的SQLQuery执行sql查询时,执行count语句或者查询数据库中的bigint(20)类型数据,返回的BigInteger类型,不是Long类型。然而在使用hibernate的Query执行hql查询时,返回的都是Long类型。甚至于使用原生的JDBC方法,获取的结果类型也是Long类型。代码如下:
User类
@Entity
@Table(name = "user", catalog = "newsclient_hnxxt", uniqueConstraints = @UniqueConstraint(columnNames = {
"schoolId", "userId" }))
public class User implements java.io.Serializable {
// Fields
private Long userId;
private String username;
private String name;
private String password;
private String mobile;
private Integer schoolId;
private String orderType;
private String role;
private Integer status;
private Timestamp createTime;
private Timestamp updateTime;
private Timestamp loginTime;
private Date expireTime;
private String schoolName;
//省略constructor,省略getter、setter,shoolName是临时属性@Transient
}
dao层
count方法:
/**
* 执行sql count查询
* @param sql
* @param values
* @return
*/
protected long countSqlResult(final String sql, final Map<String, ?> values) {
String countSql = prepareCountSql(sql);
//Hibernate Query执行count,返回结果类型为Long,而SQLQuery执行count返回类型为BigInter????
Long count = (Long)createSQLQuery(countSql, values).uniqueResult();
return count;
}
findPage方法:
/**
* 按sql分页查询.
*
* @param page 分页参数.
* @param sql sql语句.
* @param values 命名参数,按名称绑定.
*
* @return 分页查询结果, 附带结果列表及所有查询输入参数.
*/
@SuppressWarnings("unchecked")
public <X> Page<X> findPage(Class<X> clasz,final Page<X> page, final String sql, final Map<String, ?> values) {
Assert.notNull(page, "page不能为空");
Query q = createSQLQuery(sql, values);
if ("Map".equals(clasz.getSimpleName())) {
q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
} else {
q.setResultTransformer(Transformers.aliasToBean(clasz));
}
if (page.isAutoCount()) {
long totalCount = countSqlResult(sql, values);
page.setTotalCount(totalCount);
}
setPageParameterToQuery(q, page);
List result = q.list();
page.setResult(result);
return page;
}
service层
测试调用方法:
public Page<User> findPage(Page<User> page) {
StringBuffer sb = new StringBuffer();
sb.append("select u.userId, s.name as schoolName ");
sb.append("from User u, School s ");
sb.append("where u.schoolId = s.id and u.userId = 13323712806");
Map<String, Object> params = null;
return userDao.findPage(User.class, page, sb.toString(), params);
}
count报错信息
java.lang.ClassCastException: java.math.BigInteger cannot be cast to java.lang.Long
findPage报错
IllegalArgumentException occurred while calling setter for property [com.zlstudy.entity.User.userId (expected type = java.lang.Long)]; target = [com.zlstudy.entity.User@4c0a66bc], property value = [13323712806]
Caused by: java.lang.IllegalArgumentException: argument type mismatch
原因及解决方法:
1、使用addScalar,为查询列指定返回类型
Query q = createSQLQuery(sql, values).addScalar("userId", StandardBasicTypes.LONG);
这是网上给出的最普遍的解决方法,这样只能针对某个返回类做特殊处理。但是我的findPage方法是在PagingHibernateDao类,作为通用方法,不想针对某个返回类进行特殊处理,否则就不能够通用。
2、自定义方言类,修改org.hibernate.dialect.Dialect的构造方法,hibernate使用自定义方言类
查看hibernate的源代码,hibernate对于未指定的返回类型的查询列,会根据ResultSet.getMetaData().getColumnType(...)的sqlType编号匹配相应的类型,匹配的类型放在放在方言类org.hibernate.dialect.Dialect中。
hibernate部分源码:
org.hibernate.loader.Loader.class类
/**
* Execute given <tt>PreparedStatement</tt>, advance to the first result and return SQL <tt>ResultSet</tt>.
*/
protected final ResultSet getResultSet(
final PreparedStatement st,
final RowSelection selection,
final LimitHandler limitHandler,
final boolean autodiscovertypes,
final SessionImplementor session)
throws SQLException, HibernateException {
try {
ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( st );
rs = wrapResultSetIfEnabled( rs , session );
if ( !limitHandler.supportsLimitOffset() || !LimitHelper.useLimit( limitHandler, selection ) ) {
advance( rs, selection );
}
if ( autodiscovertypes ) {
autoDiscoverTypes( rs );
}
return rs;
}
catch ( SQLException sqle ) {
session.getTransactionCoordinator().getJdbcCoordinator().release( st );
throw sqle;
}
}
该方法执行PreparedStatement,通过autoDiscoverTypes( rs )方法自动匹配查询列的类型。
org.hibernate.loader.custom.JdbcResultMetadata类:
public Type getHibernateType(int columnPos) throws SQLException {
int columnType = resultSetMetaData.getColumnType( columnPos );
int scale = resultSetMetaData.getScale( columnPos );
int precision = resultSetMetaData.getPrecision( columnPos );
int length = precision;
if ( columnType == Types.CHAR && precision == 0 ) {
length = resultSetMetaData.getColumnDisplaySize( columnPos );
}
return factory.getTypeResolver().heuristicType(
factory.getDialect().getHibernateTypeName(
columnType,
length,
precision,
scale
)
);
}
org.hibernate.dialect.Dialect类
// register hibernate types for default use in scalar sqlquery type auto detection
registerHibernateType( Types.BIGINT, StandardBasicTypes.BIG_INTEGER.getName() );
hibernate将java.sql.Types.BIGINT对应为StandardBasicTypes.BIG_INTEGER。
自定义Dialect类,修改java.sql.Types.BIGINT对应的hibernate类型,然后hibernate使用自定义的Dialect。
public class LocalMySQL5InnoDBDialect extends MySQL5InnoDBDialect {
public LocalMySQL5InnoDBDialect() {
super();
registerHibernateType( Types.BIGINT, StandardBasicTypes.LONG.getName() );
}
}
自定义Dialect类,修改了hibernate框架本身的类型匹配,目前还未测试出对于其他地方有什么影响,也许这种修改会给自己挖个坑。