Richie

Sometimes at night when I look up at the stars, and see the whole sky just laid out there, don't you think I ain't remembering it all. I still got dreams like anybody else, and ever so often, I am thinking about how things might of been. And then, all of a sudden, I'm forty, fifty, sixty years old, you know?

使用NHibernate, Oracle Clob/NClob无法插入、乱码问题

服务器Oracle 9i,客户端Oracle 10g。

问题1:
NHibernate配置的Driver为NHibernate.Driver.OracleClientDriver,存入Clob、NClob字段,值比较大时报错,错误信息
中文:ORA-01461: 仅可以为插入 LONG 列的 LONG 值赋值
英文:ORA-01461: can bind a LONG value only for insert into a LONG column

Google一下有很多人碰到这个问题,有人猜测是MS System.Data.OracleClient的一个Bug,方法是改用Oracle提供的Oracle.DataAccess。

解决方法:
下载安装ODAC (Oracle Data Access Components),将NHibernate的Driver配置为NHibernate.Driver.OracleDataClientDriver,确保运行目录下有Oracle.DataAccess.dll文件。

问题2:
使用Oracle.DataAccess之后可以向一个NClob字段中插入长文本,但文本有的情况下会变成乱码。具体表现是输入一些中文单词,保存后正常;输入英文字符,保存后正常;输入一段中英文混杂的html,保存后变成乱码。
排除了服务器、客户端Oracle字符集设置问题。跟踪NHibernate,在调用IDbCommand执行SQL语句时,参数中的值是正常的,因此排除了程序中对文本的编码、解码问题。

经过测试,使用下面的方式存入NClob的值不会变为乱码:
using Oracle.DataAccess.Client;

OracleConnection con = new OracleConnection("......");
con.Open();
OracleTransaction tran 
= con.BeginTransaction();
OracleCommand command 
= con.CreateCommand();
command.CommandType 
= CommandType.Text;
command.CommandText 
= "update cms_template set temp_content=:p where temp_id=4";
OracleParameter param 
= command.CreateParameter();
param.ParameterName 
= ":p";
param.Value 
= this.textBox1.Text;
param.OracleDbType 
= OracleDbType.NClob;
command.Parameters.Add(param);
command.ExecuteNonQuery();
tran.Commit();
con.Close();
最关键的一句是将param.OracleDbType设置为OracleDbType.NClob,这样Oracle.DataAccess就知道怎样正确处理这个参数了。

NHibernate为了兼容多数据库,统一使用IDbParameter接口,对于NHibernate内部来讲这个问题不好解决,我的方法是为CLob、NClob类型的属性实现一个NHibernate.UserTypes.IUserType,在它的NullSafeSet方法中,修改IDbCommand中该字段对应的IDbParameter的OracleDbType值,具体实现如下:
public abstract class PatchForOracleLobField : IUserType
{
    
public PatchForOracleLobField()
    {
    }

    
public bool IsMutable
    {
        
get { return true; }
    }

    
public Type ReturnedType
    {
        
get { return typeof(String); }
    }

    
public SqlType[] SqlTypes
    {
        
get { return new SqlType[] { new SqlType(DbType.String) }; }
    }

    
public object DeepCopy(object value)
    {
        
return value;
    }

    
public new bool Equals(object x, object y)
    {
        
return x == y;
    }

    
public int GetHashCode(object x)
    {
        
return x.GetHashCode();
    }

    
public object Assemble(object cached, object owner)
    {
        
return DeepCopy(cached);
    }

    
public object Disassemble(object value)
    {
        
return DeepCopy(value);
    }

    
public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        
return NHibernate.NHibernateUtil.StringClob.NullSafeGet(rs, names[0]);
    }

    
public abstract void NullSafeSet(IDbCommand cmd, object value, int index);

    
public object Replace(object original, object target, object owner)
    {
        
return original;
    }
}

public class OracleClobField : PatchForOracleLobField
{
    
public override void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        
if (cmd is OracleCommand)
        {
            
//CLob、NClob类型的字段,存入中文时参数的OracleDbType必须设置为OracleDbType.Clob
            
//否则会变成乱码(Oracle 10g client环境)
            OracleParameter param = cmd.Parameters[index] as OracleParameter;
            
if (param != null)
            {
                param.OracleDbType 
= OracleDbType.Clob;
                param.IsNullable 
= true;
            }
        }
        NHibernate.NHibernateUtil.StringClob.NullSafeSet(cmd, value, index);
    }
}

public class OracleNClobField : PatchForOracleLobField
{
    
public override void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        
if (cmd is OracleCommand)
        {
            
//CLob、NClob类型的字段,存入中文时参数的OracleDbType必须设置为OracleDbType.Clob
            
//否则会变成乱码(Oracle 10g client环境)
            OracleParameter param = cmd.Parameters[index] as OracleParameter;
            
if (param != null)
            {
                param.OracleDbType 
= OracleDbType.NClob;
                param.IsNullable 
= true;
            }
        }
        NHibernate.NHibernateUtil.StringClob.NullSafeSet(cmd, value, index);
    }
}

posted on 2007-09-13 02:27  riccc  阅读(5212)  评论(4编辑  收藏  举报

导航