您的位置:首页 > 数据库 > Oracle

问题:oracle CLOB类型;结果:oracle中Blob和Clob类型的区别

2015-09-01 16:44 661 查看
BLOBCLOB都是大字段类型,BLOB是按二进制来存储的,而CLOB是可以直接存储文字的。其实两个是可以互换的的,或者可以直接用LOB字段代替这两个。
但是为了更好的管理ORACLE数据库,通常像图片、文件、音乐等信息就用BLOB字段来存储,先将文件转为二进制再存储进去。而像文章或者是较长的文字,
就用CLOB存储,这样对以后的查询更新存储等操作都提供很大的方便。

Oracle中Clob类型处理解析

2008-08-09 08:52 18249人阅读 评论(1) 收藏 举报
oraclestringclasssqlassemblygenerator
最近利用NHibernate映射类型为Clob字段在插入数据时发现当字符的字节数(一个半角字符一个字节,一个全角字符两个字节)在2000-4000之间时报错(ORA-01461:仅可以插入LONG列的LONG值赋值)。经过不断查找资料和自己的试验该问题终于得到解决,下边我将自己的心得给大家做一个分享。

准备

系统环境 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();

情况分析:

当data的长度大于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所在的程序集。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: