使用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的值不会变为乱码:
NHibernate为了兼容多数据库,统一使用IDbParameter接口,对于NHibernate内部来讲这个问题不好解决,我的方法是为CLob、NClob类型的属性实现一个NHibernate.UserTypes.IUserType,在它的NullSafeSet方法中,修改IDbCommand中该字段对应的IDbParameter的OracleDbType值,具体实现如下:
问题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就知道怎样正确处理这个参数了。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();
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 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 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);
}
}
{
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);
}
}