官方文档参照的是:
- 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();
- <span style="color:#ff0000;">registerDateTimeTypeMappings();
- </span> ...
- }
但是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("<span style="color:#ff0000;">No Dialect mapping for JDBC type: " + typecode</span>);
- 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驱动有很大关系的。