伺服器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);
}
}