Hibernate类型映射

官方文档参照的是:

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驱动有很大关系的。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值