iBatis.NET使用ODP.NET的注意事项

 

上一篇文章《.NET程序员看Oracle数据类型》总结了一些Oracle的常用数据类型和.NET类型的映射关系,这篇文章讨论如何实现它。我使用的数据库驱动是ODP.NET,数据访问框架是iBatis.NET 

一、              数值类型:

我把iBatis.NET自带的数值类型的TypeHandler全部重写了,因为这里面有这么几个问题:

1、    ODP.NET不支持SByteUInt16UInt32UInt644种类型的参数值。事实上,在ODP.NETOracleDbType枚举中,也没有这4种类型的枚举;而如果使用System.Data.DbType枚举中的这4种类型的枚举,或者不设置参数的OracleDbType属性和DbType属性而直接给Value属性赋值,也会出现异常。

 

2、    对于输出参数,如果给参数的OracleDbType属性进行了赋值,则参数的值将是ODP.NET中定义的OracleDecimalODP.NETDeveloper’s Guide中是这么说的:

ODP.NET allows applications to obtain an output parameter as either a .NET Framework type or an ODP.NET type. The application can specify which type to return for an output parameter by setting the DbType property of the output parameter (.NET type) or the OracleDbType property (ODP.NET type) of the OracleParameter object. For example, if the output parameter is set as a DbType.String type by setting the DbType property, the output data is returned as a .NET String type. On the other hand, if the parameter is set as an OracleDbType.Char type by setting the OracleDbType property, the output data is returned as an OracleString type. If both DbType and OracleDbType properties are set before the command execution, the last setting takes affect.

问题在于iBatis.NET的数值类型的TypeHandler的实现代码中,直接使用了Convert类来进行转换,而OracleDecimal类型并不支持这个转换。

 

3、    对于NUMBER类型的字段,即使你确实是为存储Single/Double类型设置了精度和小数位数,但是读取时Oracle返回的类型可能是DecimalODP.NETDeveloper’s Guide中是这么说的:

Certain methods and properties of the OracleDataReader object require ODP.NET to map a NUMBER column to a .NET type based on the precision and scale of the column. These members are:

Item property

GetFieldType method

GetValue method

GetValues method

ODP.NET determines the appropriate .NET type by considering the following .NET types in order, and selecting the first .NET type from the list that can represent the entire range of values of the column:

System.Byte

System.Int16

System.Int32

System.Int64

System.Single

System.Double

System.Decimal

If no .NET type exists that can represent the entire range of values of the column, then an attempt is made to represent the column values as a System.Decimal type. If the value in the column cannot be represented as System.Decimal, then an exception is raised.

For example, consider two columns defined as NUMBER(4,0) and NUMBER(10,2). The first .NET types from the previous list that can represent the entire range of values of the columns are System.Int16 and System.Double, respectively. However, consider a column defined as NUMBER(20,10). In this case, there is no .NET type that can represent the entire range of values on the column, so an attempt is made to return values in the column as a System.Decimal type. If a value in the column cannot be represented as a System.Decimal type, then an exception is raised.

但是Oracle在判断使用哪个.NET数值类型时,却不是完全精确的根据.NET数值类型的范围来确定的,因此你认为是应该返回Single/Double却有可能实际返回的类型是DecimaliBatis.NETSingleTypeHandlerDoubleTypeHandlerNullableSingleTypeHandlerNullableDoubleTypeHandler的实现代码中使用了强制类型转换,则就有可能发生异常。

当然,对于BINARY_FLOAT/BINARY_DOUBLE字段,其返回的类型一定是Single/Double,这可以放心的使用DataReaderGetFloat/GetDouble方法。但是需要在映射中明确指定dbTypeBinaryFloat/BinaryDouble

 

二、              字符类型:

iBatis.NET中实现的字符类型的TypeHandler,能很好的工作于ODP.NET驱动。

需要说明的只有一点,在使用StringTypeHandler读写CLOB/NCLOB字段时,最好在映射中明确指定dbTypeClob/NClob

 

三、              日期类型:

iBatis.NET中实现的日期类型的TypeHandler,也存在的一定的问题:

1、    iBatis.NET中的TimeSpanTypeHandlerNullableTimeSpanTypeHandler,是将TimeSpan值的Ticks属性作为Int64类型的值存储到数据库中。而实际上ODP.NET直接支持存储TimeSpan值,对应的Oracle类型是INTERVAL DAY TO SECOND。当然,iBatis.NET需要支持多种数据库,为了通用性,这样处理是合理的。但是我既然确定只使用ODP.NET,就应该根据其实际情况来作出修改。

 

2、    .NET 2.0 SP1开始,引入了一个新的DateTimeOffset结构,该结构包括一个 DateTime 值以及一个 Offset 属性,后者用于确定当前 DateTimeOffset 实例的日期和时间与协调世界时 (UTC) 之间的差值。而ODP.NET中也正好有TIMESTAMP WITH TIME ZONE类型,支持在存储时间的同时存储时区。所以为它们两者建立一个专门的TypeHanlder是合理的。

 

3、    另外需要说明的是,Oralce中的DATE类型不能存储小数秒。如果需要存储DateTime结构中的小数秒,可以使用TIMESTAMP类型,或者TIMESTAMP WITH LOCAL TIME ZONE类型(存储本地时间戳时)。

 

四、              二进制类型:

iBatis.NET中实现的二进制类型的TypeHandler,能很好的工作于ODP.NET驱动。

和字符类型类似,在使用ByteArrayTypeHandler读写BLOB字段时,最好在映射中明确指定dbTypeBlob

 

五、              布尔类型:

因为Oracle中不支持布尔类型,所以iBatis.NET中预定义的BooleanTypeHandler也就完全没有用了。

在上一篇文章中,我提到存储”Y”/”N””T”/”F”1/0三种方式。我个人比较喜欢的是存储1/0,原因是如果需要,可以转成枚举。所以我自己写了一个OneZeroBooleanTypeHandler来取代预定义的BooleanTypeHandler作为Boolean类型的默认TypeHandler,另外写了两个可选的TrueFalseBooleanTypeHandlerCallbackYesNoBooleanTypeHandlerCallback作为备选。

 

六、              其他类型:

1.         枚举类型:

.NET枚举的基础类型(UnderlyingType)可以是SByteUInt16UInt32UInt64,基于数值类型中同样的原因,我把预定义的EnumTypeHandler重写了。

 

2.         可序列化的类型:

序列化只能定义为TypeHandlerCallBack,因为没有一个固定的类型。这里的逻辑,只是在写入时序列化,读取时反序列而已。

 

3.         Guid

Oralce不支持Guid类型,因此预定义的GuidTypeHandler也没法使用。我重写了该TypeHanlder,把Guid对象转换为字符串存储到数据库中。

        

4.         XmlDocument

XmlDocument也是常用的一种数据类型,Oracle也支持Xml文档的存储,但是和我们这里说的不是一回事。这里的做法是把XmlDocument转换为字符串存入CLOB/NCLOB,或转换为字节数组存入BLOB

转换的方法也很简单,使用StringReader/StringWriter可以与字符串转换,使用MemoryStream可以和字节数组转换。


 

照例用表格总结一下:

 

.NET类型

Oracle类型

OracleDbType

(必须在映射中指定用粗体表示)

TypeHandler

(自定义用粗体表示)

Byte

NUMBER(3)

Byte

ByteTypeHandler

NullableByteTypeHandler

SByte

NUMBER(3)

ByteInt16(根据数值范围)

SByteTypeHandler

NullableSByteTypeHandler

Int16

NUMBER(5)

Int16

Int16TypeHandler

NullableInt16TypeHandler

UInt16

NUMBER(5)

Int16Int32(根据数值范围)

UInt16TypeHandler

NullableUInt16TypeHandler

Int32

NUMBER(10)

Int32

Int32TypeHandler

NullableInt32TypeHandler

UInt32

NUMBER(10)

Int32Int64(根据数值范围)

UInt32TypeHandler

NullableUInt32TypeHandler

Int64

NUMBER(20)

Int64

Int64TypeHandler

NullableInt64TypeHandler

UInt64

NUMBER(20)

Int64Decimal(根据数值范围)

UInt64TypeHandler

NullableUInt64TypeHandler

Single

FLOAT(24)

Single

SingleTypeHandler

NullableSingleTypeHandler

Single

BINARY_SINGLE

BinaryFloat

Double

DOUBLE PRECISION

Double

DoubleTypeHandler

NullableDoubleTypeHandler

Double

BINARY_DOUBLE

BinaryDouble

Decimal

NUMBER

Decimal

DecimalTypeHandler

NullableDecimalTypeHandler

Char

VARCHAR2(1 Char)

NVARCHAR2(1)

Char

NChar

CharTypeHandler

NullableCharTypeHandler

String

VARCHAR2(n char)

NVARCHAR2(n)

CLOB

NCLOB

Varchar2

NVarchar2

Clob

NClob

StringTypeHandler

DateTime

DATE

TIMESTAMP

TIMESTAMP WITH LOCAL TIME ZONE

Date

TimeStamp

TimeStampLTZ

DateTimeTypeHandler

NullableDateTimeTypeHandler

DateTimeOffset

TIMESTAMP WITH TIME ZONE

TimeStampTZ

DateTimeOffsetTypeHandler

NullableDateTimeOffsetTypeHandler

TimeSpan

INTERVAL DAY TO SECOND

IntervalDS

TimeSpanTypeHandler

NullableTimeSpanTypeHandler

Byte[]

RAW

BLOB

Raw

Blob

ByteArrayTypeHandler

Boolean

NUMBER(1)1/0

INTEGER(便于扩展)

Int32

OneZeroBooleanTypeHandler

NullableOneZeroBooleanTypeHandler

Boolean

VARCHAR2(1)’T’/’F’

Varchar2

TrueFalseBooleanTypeHandlerCallback

NullableTrueFalseBooleanTypeHandlerCallback

Boolean

VARCHAR2(1)’Y’/’N’

Varchar2

YesNoBooleanTypeHandlerCallback

NullableYesNoBooleanTypeHandlerCallback

枚举类型

NUMBER(n)(存储基础类型的值)

INTEGER(便于扩展)

Byte/Int16/Int32/Int64

EnumTypeHandler

枚举类型

VARCHAR2(n char)NVARCHAR2(n)(存储常量名称)

Varchar2/Nvarchar2

可序列化类型

BLOB

Blob

SerializableTypeHandlerCallBack

Guid

Varchar2(38)ToString()方法)

Varchar2

GuidTypeHandler

NullableGuidTypeHandler

XmlDocument

NCLOB/CLOBSaveTextWriter

NClob/Clob

XmlDocumentTypeHandler

XmlDocument

BLOBSaveStrema

Blob

 


参考资料:

1、 iBatis.NET源代码。我下载的版本是513437

2、 Oracle11g Data Provider for .NET (11.1.0.6.20) Developer's Guide .pdf》。

posted on 2009-06-09 09:13  qmxle  阅读(2590)  评论(11编辑  收藏  举报

导航