项目中使用统一的接口,调用hibernate进行sql查询。
使用SqlQuery查询,返回结果为List<Map<String,Object>>。发现结果中的时间只有年月日,没有时分秒。
查询资料后知道,造成这个现象的原因是使用的oracle10g的驱动ojdbc14.jar造成的。如果使用9i或者11g的驱动则没有这个问题。
在看到这个原因后,我对hibernate的数据映射原理产生了兴趣,跟踪源代码看到了问题的最终产生来源。
(1)SqlQuery的list()方法默认调用以下
List org.hibernate.impl.SessionImpl.listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) throws HibernateException
(2)在这里创建了CustomLoader loader = new CustomLoader( customQuery, getFactory() );
最终调用loader.list(this, queryParameters);
(3)继续跟进,调用到CustomLoader 父类Loader的
private List listIgnoreQueryCache(SessionImplementor session, QueryParameters queryParameters) {
return getResultList( doList( session, queryParameters ), queryParameters.getResultTransformer());
}
其中doListdoList( session, queryParameters )为查询结果集,并初次包装。
(4)最终跟进到Loader的private List doQuery(
final SessionImplementor session,
final QueryParameters queryParameters,
final boolean returnProxies) throws SQLException, HibernateException 方法。在这里可以看到jdbc的一些操作了。
final PreparedStatement st = prepareQueryStatement( queryParameters, false, session );
final ResultSet rs = getResultSet( st, queryParameters.hasAutoDiscoverScalarTypes(), queryParameters.isCallable(), selection, session );
for ( count = 0; count < maxRows && rs.next(); count++ ) {
。。。
Object result = getRowFromResultSet(
rs,
session,
queryParameters,
lockModesArray,
optionalObjectKey,
hydratedObjects,
keys,
returnProxies
);
results.add( result );
。。。
}
(5)默认的映射解析函数为CustomLoader 的protected void autoDiscoverTypes(ResultSet rs)
Metadata metadata = new Metadata( getFactory(), rs );
。。。
for ( int i = 0; i < rowProcessor.columnProcessors.length; i++ ) {
rowProcessor.columnProcessors[i].performDiscovery( metadata, types, aliases );
}
默认的映射使用CustomLoader 的 内部类ScalarResultColumnProcessor。此处的Metadata为查询结果集的元数据,得到的类为oracle.jdbc.driver.OracleResultSetMetaData。继续向下看就能发现最终的错误原因了。
(6)ScalarResultColumnProcessor中根据metadata获取的类型为date类型。而在ScalarResultColumnProcessor最终取值时调用的是DateTypeDescriptor.getExtractor方法。其中部分实现为
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getDate( name ), options );
}
由此可以看到,最终从rs中使用getDate方法获取值。如此获取的值就只有日期没有时间。
所有的过程都看完之后,可以说造成这个问题的原因在于metadata中返回的数据类型为date了,按照getDate获取造成的丢失时间。
解决办法:
1.添加自定义映射
sqlQuery.addScalar(columnAlias, type);
2.sql语句中使用to_char,直接查询出字符再做其他操作。
3.获取连接是添加属性
prop.setProperty("oracle.jdbc.V8Compatible","true");
Connection connection=DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl", prop);
正在尝试将这个参数添加到c3p0的配置文件中,还没有成功。
4.修改hibernate源码,没有尝试。对hibernate的架构还不熟悉,在没有大量的测试资源的情况下,感觉不太靠谱。