?
準備
系統環境 xp+.net2.0+oracle9i
?
表結構(由于是測試,表結構隨便建了一張) XX?
字段名 |
類型 |
ID |
VARCHAR2(70) |
TEST |
CLOB |
?
?
測試
?
方式 1 :直接將 CLOB 的值拼寫在 SQL 語句中。
代碼:
- string?id?=?Guid.NewGuid().ToString();
- OracleCommand?cmd?=?Conn.CreateCommand();
- cmd.CommandText?=?"insert?into?xx(id,test)?values('"?+?id?+?"','"?+?data?+?"')";//?data是一個變量,存儲你要插入的字符串
- cmd.ExecuteNonQuery();
情況分析 :
?? ? 當d ata 的長度大于 4000 時報錯 (ORA-01704: 文字字符串過長 ) ,小于或等于 4000 時正常插入。
原因分析:
? ? 之所以會出現長度大于 4000 時報錯,是因為 Oracle 中有 SQL 語句中兩個單引號之間的字符數不能大于 4000 的限制。'" + data + "' data在sql語句之間,當data的值大于4000個字節時就會報錯。
解決辦法:
?? 這種方式比較棘手,但有更好的方式,下邊會講到。
?
方式 2 :采用參數形式。
代碼:
- string?id?=?Guid.NewGuid().ToString();
- OracleCommand?cmd?=?Conn.CreateCommand();
- cmd.CommandText?=?"insert?into?xx(id,test)?values('"?+?id?+?"',:p1)";
- OracleParameter?p1?=?new?OracleParameter("p1",?OracleType.Clob);
- p1.Value?=?data;?//?data是一個變量,存儲你要插入的字符串
- cmd.Parameters.Add(p1);
- cmd.ExecuteNonQuery();
情況分析 :
??? 采用這種方式能夠正常插入。所以推薦用這種方式。
原因分析:
??無
解決辦法:
? ? 無
?
方式 3 :采用參數形式,但是參數類型寫為 OracleType. NVarChar
代碼:
- string?id?=?Guid.NewGuid().ToString();
- OracleCommand?cmd?=?Conn.CreateCommand();
- cmd.CommandText?=?"insert?into?xx(id,test)?values('"?+?id?+?"',:p1)";
- OracleParameter?p1?=?new?OracleParameter("p1",?OracleType.?NVarChar);
- p1.Value?=?data;?//?data是一個變量,存儲你要插入的字符串?
- cmd.Parameters.Add(p1);
- cmd.ExecuteNonQuery();
情況分析 :
??? 為什么要寫這種方式,因為這種方式和采用 NHibernate 的方式很相似,先看看在這種方式會產生什么情況。當 data 的字節數在 0-2000 之間時正常插入,大于 4000 時也正常插入,但在 2000-4000 時則失敗,報錯 (ORA-01461: 僅可以插入 LONG 列的 LONG 值賦值 )
原因分析:
?? 沒有采用對應的 Oracle 類型。
解決辦法:
? ? 采用OracleType.Clob
?
?
下邊采用NHibernate插入數據,NHibernate具體怎用不在本次討論范圍。
NHibernate采用的版本為1.2.1.4000。
下邊大至把簡要配置寫下。
?
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
? <configSections>
??? <section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
? </configSections>
? <nhibernate>
??? <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
??? <add key="hibernate.connection.driver_class" value="NHibernate.Driver.OracleClientDriver" />
??? <add key="hibernate.connection.isolation" value="ReadCommitted"/>
? ??<add key="hibernate.dialect" value="NHibernate.Dialect.Oracle9Dialect" />
??? <add key="hibernate.connection.connection_string"
???????? value="Data Source=Orcl_192.168.0.232;User ID =icqs_test;Password=icqs_test" />
??? <add key="show_sql" value="true" />
??? <add
?????????? key="hibernate.adonet.batch_size"
?????????? value="100"
?????? />
? </nhibernate>
</configuration>
?
xx.cs
- using ?System;
- using ?System.Collections.Generic;
- using ?System.Text;
- namespace ?Test.Enties
- {
- ????[Serializable]
- ????public?class?Xx
- ????{
- ????????public?Xx()
- ????????{
- ????????}
- ????????private?string?id;
- ????????public?virtual?string?Id
- ????????{
- ????????????get?{?return?id;?}
- ????????????set?{?id?=?value;?}
- ????????}
- ????????public?virtual?string?Test
- ????????{
- ????????????get?{?return?test;?}
- ????????????set?{?test?=?value;?}
- ????????}
- ????????private?string?test;
- ????}
- }
xx.hbm.xml
<?xml version="1.0" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Test.Enties" assembly="Test">
? <class name="Xx" table="xx" lazy="true">
?
??? <id name="Id" column="id" type="String">
????? <generator class="assigned"/>
??? </id>
??? <property column="test" type="StringClob" name="Test" length="2147483647" />
?
? </class>
</hibernate-mapping>
?
說明:
<add key="hibernate.connection.driver_class" value="NHibernate.Driver.OracleClientDriver" />這里的驅動用的NHibernate.Driver.OracleClientDriver,其實是對微軟的OracleClient的封裝啦,其實內部還是調用微軟的OracleClient的東東。引用System.Data.OracleClient.dll即可OracleClient。
?
做好上邊的配置后,便有了以下的方式
方式 4 :采用 NHibernate
代碼:
- string?id?=?Guid.NewGuid().ToString();
- Xx?xx?=?new?Xx();
- xx.Test?=?data;?//?data是一個變量,存儲你要插入的字符串
- xx.Id?=?id;
- ISession?session?=?SessionFactory.OpenSession();
- session.Save(xx);
- session.Flush();
情況分析 :
?? 當 data 的字節數在 0-2000 之間時正常插入,大于 4000 時也正常插入,但在 2000-4000 時則失敗,報錯 (ORA-01461: 僅可以插入 LONG 列的 LONG 值賦值 ). 情況和方式 3 的情況一樣。
原因分析:
??? NHibernate在用OracleClient映射StringClob時,設置參數類型為OracleType.?NVarChar,導致插入有BUG。網上有人推測是OracleClient的BUG所致,理由是換用OracleDataAccess即可解決。
?
為什么說NHibernate將參數類型設置為OracleType.NVarChar呢?看下邊
?
?
- 找到NHibernate的源代碼,把它加入你的工程。記得不要移動NHibernate位置直接加入工程,直接在NHibernate的安裝目錄引用進來。
?
?
?
?? 2. 在Test解決方案中添加NHibernate的項目引用。
?
經過上邊兩個步驟我們就可以跟蹤調試NHibernate了
?
- 跟蹤代碼session.Save(xx);看看它究竟做了啥。
當我們跟進CommandSetBatchingBatcher時,可以得到以下信息(如圖中的調試信息)。CurrentBatch類型是OracleClientCommandSet,OracleClientCommandSet看源碼得知是對微軟的OracleCommandSet的封裝,因為這個類 internal sealed class ,所以我們的程序里是找不到這個類的,不過 NHibernate 通過反射使用了它的功能。OracleCommandSet 可能用作批處理的,就是一次處理多個SQL語句的,不是太了解,誰知道請指教。
?
?
CommandSetBatchingBatcher的源碼
- internal?class?OracleClientCommandSet?:?DbCommandSet<OracleConnection,?OracleCommand>
- ????{
- ????????private?static?System.Type?oracleCmdSetType;
- ????????static?OracleClientCommandSet()
- ????????{
- ????????????Assembly?sysDataOracleClient?=?Assembly.Load("System.Data.OracleClient,?Version=2.0.0.0,?Culture=neutral,?PublicKeyToken=b77a5c561934e089");
- ????????????oracleCmdSetType?=?sysDataOracleClient.GetType("System.Data.OracleClient.OracleCommandSet");
- ????????????Debug.Assert(oracleCmdSetType?!=?null,?"Could?not?find?OracleCommandSet!");
- ????????}
- ????????protected?override?object?CreateInternalCommandSet()
- ????????{
- ????????????return?Activator.CreateInstance(oracleCmdSetType,?true);
- ????????}
- }
跟蹤CurrentBatch可以看到
CommandText:
declare
type refcursortype is ref cursor;
begin
INSERT INTO z3 (test, id) VALUES (:p2, :p3);
:r1_4 := sql%rowcount;
end;
?
這里的p2就是我們的Clob類型字段的參數啦。
再看p2的OracleType是NVarChar,是不是有點明白啦,對了, 跟我們3一樣,參數類型錯掉了。
?
?
?
解決辦法:
?? 使用 NHibernate 的自定義類型,不是太會,幸好網上有高人提供代碼,在此想高人致謝。這樣我們通過自定義類型來設置正確的OracleType即可。在項目中添加兩個類。
?? PatchForOracleLobField.cs
- using?System;
- using?System.Collections.Generic;
- using?System.Data;
- using?System.Text;
- using?NHibernate;
- using?NHibernate.SqlTypes;
- using?NHibernate.UserTypes;
- namespace?Test.type
- {
- ????public?abstract?class?PatchForOracleLobField?:?IUserType
- ????{
- ????????public?PatchForOracleLobField()
- ????????{
- ????????}
- ????????public?bool?IsMutable
- ????????{
- ????????????get?{?return?true;?}
- ????????}
- ????????public?System.Type?ReturnedType
- ????????{
- ????????????get?{?return?typeof(String);?}
- ????????}
- ????????public?SqlType[]?SqlTypes
- ????????{
- ????????????get
- ????????????{
- ????????????????return?new?SqlType[]?{?NHibernateUtil.String.SqlType?};
- ????????????}
- ????????}
- ????????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;
- ????????}
- ????}
- }
?
OracleClobField.cs
- using ?System;
- using ?System.Collections.Generic;
- using ?System.Data;
- using ?System.Data.OracleClient;
- using ?System.Text;
- namespace ?Test.type
- {
- ????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.OracleType?=?OracleType.Clob;// 關鍵就這里啦
- ????????????????????param.IsNullable?=?true;
- ????????????????}
- ????????????}
- ????????????NHibernate.NHibernateUtil.StringClob.NullSafeSet(cmd,?value,?index);
- ????????}
- ????}
- }
?
然后在映射文件中修改類型即可。
Com.Dic.Icqs.Entities.Type.OracleClobField,Com.Dic.Icqs.Entities
修改前:
<property column="test" type="StringClob" name="Test" length="2147483647" />
修改后:
<property column="test" type="Test.type.OracleClobField, Test " name="Test" length="2147483647" />
Test.type.OracleClobField是類的完整名,Test 即OracleClobField所在的程序集。
?
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
