官方文档参照的是:
hibernate-distribution-3.6.0.Final\documentation\manual\zh-CN\pdf\hibernate_reference.pdf
源代码参照的是:
hibernate-3.3.1.GA-src\core\src\main\java\org\hibernate\...
在配置hibernate的时候,虽然配置项很多,但是看似只有方言和hibernate类型映射有些关联,那么就先从方言入手
先来看个时间类型的,以Oracle9iDialect类为例,其中有如下方法:
public class Oracle9iDialect extends Oracle8iDialect {
...
protected void registerDateTimeTypeMappings() {
registerColumnType( Types.DATE, "date" );
registerColumnType( Types.TIME, "date" );
registerColumnType( Types.TIMESTAMP, "timestamp" );
}
然后再来看看Oracle8iDialect类:
public class Oracle8iDialect extends Dialect {
...
protected void registerDateTimeTypeMappings() {
registerColumnType( Types.DATE, "date" );
registerColumnType( Types.TIME, "date" );
registerColumnType( Types.TIMESTAMP, "date" );
}
经过观察,这些方言都引入了一个类:
import java.sql.Types;
来看看jdk是怎么描述这个类的:
定义用于标识一般 SQL 类型(称为 JDBC 类型)的常量的类。
...
static int DATE
标识一般 SQL 类型 DATE 的 Java 编程语言中的常量(有时称为类型代码)。
说明hibernate维护了一个java类型到jdbc类型的对应关系,以此类推
接下来看看registerDateTimeTypeMappings这个方法,Dialect类中并没有这个方法,该方法是在Oracle8iDialect类初始化时加入的,Oracle9iDialect类重写了该方法
(估计是应为每个数据库的日期类型都不一样,所有不会放到共同父类Dialect中):
public class Oracle8iDialect extends Dialect {
public Oracle8iDialect() {
super();
registerCharacterTypeMappings();
registerNumericTypeMappings();
registerDateTimeTypeMappings();
...
}
......
但是Oracle8iDialect类和Oracle9iDialect类中都没有重写该方法调用的registerColumnType方法,那么我们看看Dialect类中该方法是如何实现的:
private final TypeNames hibernateTypeNames = new TypeNames();
...
protected void registerColumnType(int code, String name) {
typeNames.put( code, name );
}
我们再来看看这个typeNames是个什么东西(没有import该类,那么说明和方言是在同一个包路径下):
public class TypeNames {
private HashMap weighted = new HashMap();
private HashMap defaults = new HashMap();
/**
* get default type name for specified type
* @param typecode the type key
* @return the default type name associated with specified key
*/
public String get(int typecode) throws MappingException {
String result = (String) defaults.get( new Integer(typecode) );
if (result==null) throw new MappingException("No Dialect mapping for JDBC type: " + typecode);
return result;
}
/**
* get type name for specified type and size
* @param typecode the type key
* @param size the SQL length
* @param scale the SQL scale
* @param precision the SQL precision
* @return the associated name with smallest capacity >= size,
* if available and the default type name otherwise
*/
public String get(int typecode, int size, int precision, int scale) throws MappingException {
Map map = (Map) weighted.get( new Integer(typecode) );
if ( map!=null && map.size()>0 ) {
// iterate entries ordered by capacity to find first fit
Iterator entries = map.entrySet().iterator();
while ( entries.hasNext() ) {
Map.Entry entry = (Map.Entry)entries.next();
if ( size <= ( (Integer) entry.getKey() ).intValue() ) {
return replace( (String) entry.getValue(), size, precision, scale );
}
}
}
return replace( get(typecode), size, precision, scale );
}
private static String replace(String type, int size, int precision, int scale) {
type = StringHelper.replaceOnce(type, "$s", Integer.toString(scale) );
type = StringHelper.replaceOnce(type, "$l", Integer.toString(size) );
return StringHelper.replaceOnce(type, "$p", Integer.toString(precision) );
}
/**
* set a type name for specified type key and capacity
* @param typecode the type key
*/
public void put(int typecode, int capacity, String value) {
TreeMap map = (TreeMap)weighted.get( new Integer(typecode) );
if (map == null) {// add new ordered map
map = new TreeMap();
weighted.put( new Integer(typecode), map );
}
map.put(new Integer(capacity), value);
}
/**
* set a default type name for specified type key
* @param typecode the type key
*/
public void put(int typecode, String value) {
defaults.put( new Integer(typecode), value );
}
}
说明hibernate就是依靠TypeNames类中的2个map来维护上面提到的对应关系。
再来看一个Integer类型的:
Oracle9iDialect类中没有该方法的描述,来看下Oracle8iDialect类
protected void registerNumericTypeMappings() {
registerColumnType( Types.BIT, "number(1,0)" );
registerColumnType( Types.BIGINT, "number(19,0)" );
registerColumnType( Types.SMALLINT, "number(5,0)" );
registerColumnType( Types.TINYINT, "number(3,0)" );
registerColumnType( Types.INTEGER, "number(10,0)" );
...
}
虽然整体的设计难窥其一二,但是通过这个"number(10,0)",基本确定了:
hibernate通过TypeNames类中的HashMap维护了java数据类型和jdbc类型之间的对应关系(使用不同的方言或是jdbc驱动,对应关系会略有不同)。
java程序通过hibernate访问数据库(数据库的特殊sql类型对java程序来说是不可见的,oracle的varchar2类型或是mysql的varchar类型对java程序来说都是String类型),而hibernate通过jdbc驱动访问数据库。jdbc驱动则对底层数据库使用的sql类型进行了封装,向上为hibernate提供jdbc类型接口,hibernate通过jdbc类型接口得到返回的jdbc类型后,根据自身维护的java类型与jdbc类型之间的对应关系,返回java类型。
也即对同一个数据库的相同数据库类型,取到的值是和使用的数据库方言以及jdbc驱动有很大关系的。