C# 操作 XML 数据库类型、Oracle XMLType

  1. xml 类型很久就有了一直没有关注,有时间正好看看;
  2. 这次学习要做的事情
  3. 1、设计一个C# 类来生成 XML Schema (XML 架构)
  4. 先设计类,然后由类生成表可能是有很多人蒙昧以求的;正好和 ORM 相反
  5. 最主要的 XML 架构可以用来约束数据库中的;XML 的有效性
  6. 2、基于上一步的 XML Schema 我们创建一个数据表、并包含这个和上边 C# 类对应的的 XML 类型;
  7. 3、插入一些数据;
  8. 4、改变 C# 的类重新生成 Schema 在更新数据库中的 Schema
  9. 这步假设业务变更看看,更改如何进行,是否方便等
  10. 5、用 C# 写一个网页,显示这些数据;
  11. 6、阶段性总结
  12. 好开始:
  13. 一、设计一个C# 类来生成 XML Schema类代码:
  14. 类如下
  15. namespace Model
  16. {
  17.     
  18.     /// <summary>
  19.     /// 电话
  20.     /// </summary>
  21.     public class Phone
  22.     {
  23.         string _code;
  24.         /// <summary>
  25.         /// 电话号
  26.         /// </summary>
  27.         public string Code
  28.         {
  29.             get { return _code; }
  30.             set { _code = value; }
  31.         }
  32.         PhoneType _type;
  33.         public PhoneType Type
  34.         {
  35.             get { return _type; }
  36.             set { _type = value; }
  37.         }        
  38.         
  39.     }
  40.     /// <summary>
  41.     /// 电话类型
  42.     /// </summary>
  43.     public enum PhoneType
  44.     {
  45.         [XmlEnum(Name = "未知")] //XML 序列化用的名称
  46.         Unknown,
  47.         [XmlEnum(Name = "移动")]
  48.         Mobile,
  49.         [XmlEnum(Name = "固定")]
  50.         Fixed,
  51.         
  52.         
  53.         
  54.     }
  55.     /// <summary>
  56.     /// 电话集合
  57.     /// </summary>
  58.     [XmlRoot("Phones")]
  59.     public class Phones : List<Phone>
  60.     {
  61.         public void Add(string code, PhoneType type)
  62.         {
  63.             base.Add(new Phone() { Code = code, Type = type });
  64.         }
  65.         
  66.     }
  67. }
  68. 3个类:电话、电话类型(枚举)、电话类型集合;
  69. 以 Phones 类生成 Schema  ,用 .net sdk 的 xsd.exe 或自己写代码都可以生成的、我就不多说了;
  70. Schema  这东西如果纯手写也是劳动量巨大的;谢谢.net 为我们提供这个功能吧;
  71. Schema 如下 :
  72. <xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  73.   <xs:element name="Phones" nillable="true" type="ArrayOfPhone" />
  74.   <xs:complexType name="ArrayOfPhone">
  75.     <xs:sequence>
  76.       <xs:element minOccurs="0" maxOccurs="unbounded" name="Phone" nillable="true" type="Phone" />
  77.     </xs:sequence>
  78.   </xs:complexType>
  79.   <xs:complexType name="Phone">
  80.     <xs:sequence>
  81.       <xs:element minOccurs="0" maxOccurs="1" name="Code" type="xs:string" />
  82.       <xs:element minOccurs="1" maxOccurs="1" name="Type" type="PhoneType" />
  83.     </xs:sequence>
  84.   </xs:complexType>
  85.   <xs:simpleType name="PhoneType">
  86.     <xs:restriction base="xs:string">
  87.       <xs:enumeration value="未知" />
  88.       <xs:enumeration value="移动" />
  89.       <xs:enumeration value="固定" />
  90.     </xs:restriction>
  91.   </xs:simpleType>
  92. </xs:schema>
  93. 二、我们创建一个数据表、并包含这个和上边 C# 类对应的的 XML 类型;
  94. 2.1 注册架构:
  95. BEGIN
  96.     -- 注册架构
  97.   DBMS_XMLSCHEMA.registerschema(schemaurl => 'http://www.OracleDemo.com/Phones.xsd',
  98.                                 schemadoc => '<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  99.   <xs:element name="Phones" nillable="true" type="ArrayOfPhone" />
  100.   <xs:complexType name="ArrayOfPhone">
  101.     <xs:sequence>
  102.       <xs:element minOccurs="0" maxOccurs="unbounded" name="Phone" nillable="true" type="Phone" />
  103.     </xs:sequence>
  104.   </xs:complexType>
  105.   <xs:complexType name="Phone">
  106.     <xs:sequence>
  107.       <xs:element minOccurs="0" maxOccurs="1" name="Code" type="xs:string" />
  108.       <xs:element minOccurs="1" maxOccurs="1" name="Type" type="PhoneType" />
  109.     </xs:sequence>
  110.   </xs:complexType>
  111.   <xs:simpleType name="PhoneType">
  112.     <xs:restriction base="xs:string">
  113.       <xs:enumeration value="未知" />
  114.       <xs:enumeration value="移动" />
  115.       <xs:enumeration value="固定" />
  116.     </xs:restriction>
  117.   </xs:simpleType>
  118. </xs:schema>',
  119.                                 
  120.                                 local     => TRUE,
  121.                                 gentypes  => TRUE,
  122.                                 genbean   => FALSE,
  123.                                 gentables => TRUE);
  124. END;
  125. --会建立 xml 描述的【Oracle自定义类型】
  126. -- 如果用Oracle 工具查看 Types 下会出现一些如 Phone***_T,phone***_coll,ArrayOfPhone***_T 类似名称的 【Oracle自定义类型】
  127. -- 结构就和xml schema 是一样
  128. -- gentables => TRUE 还会建立一些表;
  129. -- 如 create table Phones721_TAB of SYS.XMLTYPE --物理表
  130. -- create table SYS_NTyIVemDaJQXqHZgjqYv+haQ== of Phone711_T --自定义类型表
  131.  2.2 创建表
  132. CREATE table XML_USER_INFO (
  133.  NPK integer,
  134.  USER_NAME NVARCHAR2(50),
  135.  Phones XMLType,
  136.  primary key (NPK)
  137. )
  138. XMLTYPE COLUMN Phones STORE AS OBJECT RELATIONAL          --以对象关系方式建立,而不是二进制
  139. XMLSCHEMA "http://www.OracleDemo.com/Phones.xsd"
  140. ELEMENT "Phones"
  141. VARRAY Phones.XMLDATA."Phone" STORE AS table XML_USER_INFO_XMLNT01 --将xml中 Phones/Phone 定义为一个数组嵌套表
  142. /
  143. -- 返回:成功
  144. -- 这时 oracle 还会建立一个 XML_USER_INFO_XMLNT01 的【嵌套表】
  145. 我建立了一个叫 XML_USER_INFO 的表,这个东西假设为一个用户信息表(真正的用户信息不可能这么少列的)
  146. oracle 建立XMLType 时可以指定以二进制或对象关系方式建立xml 类型,
  147. 我这里选择 STORE AS OBJECT RELATIONAL 据说可以提高性能,等待考证
  148. 下一面我要给 Phones/Phone 下加一些约束所以将 Phone 定义为一个数组嵌套表;说白了就是定义一个表里面都放置 Phone 类型
  149. 2.3 定义约束
  150. ALTER table XML_USER_INFO_XMLNT01 
  151.        ADD constraint PK_XML_USER_INFO_XMLNT01 primary key (NESTED_TABLE_ID, "Code"
  152. 就是每个人的电话号码不能重复; 数据库里的东西要是没有约束是很郁闷的,这里也试验一下这个问题;
  153. constraint PK_XML_USER_INFO_XMLNT01 可以省略,因为有名字比较容易从异常中看出到底是什么列出错,我一般都会加上这个
  154. 除非一个表就一个主键;
  155.  遗憾的是集合类型的元素、不能加外键(就是说如果是 Phones 的属性是可以加外键的 Phone属性,如电话号码,类型什么的就不行,必须和数据库表一行能对应上的才可以加外键否则只能用 schema 约束);
  156.  外键的例子以后在说吧;
  157. 2.4 本步骤总结
  158. 比较满意、虽然sql 代码很多不过,schema 是类生成的、能够节省一些设计时间,不过学习成本是必须的;
  159. XMLType 的 schema 并不是必须的,不过没有约束,关系对数据库来说,时间长了是不好维护的尤其是后来人;
  160. 三、插入一些数据
  161. 3.1 插入数据
  162. INSERT INTO XML_USER_INFO VALUES (1,'用户1'
  163.     ,XMLType('<Phones xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:www.w3.org/2001/XMLSchema">
  164.  <Phone>
  165.     <Code>13940588000</Code>
  166.     <Type>移动</Type>
  167.  </Phone>
  168.  <Phone>
  169.     <Code>024-22222222-1</Code>
  170.     <Type>固定</Type>
  171.  </Phone>
  172.  <Phone>
  173.     <Code>8788888</Code>
  174.     <Type>未知</Type>
  175.  </Phone>
  176. </Phones>'
  177.     )
  178. )
  179. /
  180. 3.2 测试约束有效性(在Type改小灵通)
  181. INSERT INTO XML_USER_INFO VALUES (2,'用户2'
  182.     ,XMLType('<Phones xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:www.w3.org/2001/XMLSchema">
  183.   <Phone>
  184.     <Code>13940588000</Code>
  185.     <Type>移动</Type>
  186.   </Phone>
  187.   <Phone>
  188.     <Code>13940588000</Code>
  189.     <Type>固定</Type>
  190.   </Phone>
  191.   <Phone>
  192.     <Code>8788888</Code>
  193.     <Type>小灵通</Type>
  194.   </Phone>
  195. </Phones>'
  196.     )
  197. )
  198. -- 返回错误:ORA-31038: enumeration 值无效: "小灵通"
  199. -- 证明 PhoneType 的 XMLSchema 约束是有效的(废话);
  200. /
  201. ….插入我就不多写了都这模样
  202. 注意:大家可以看出xmltype这里的数据,就是 Phones 对象xml序列化后的的样子
  203. 也就是说我们可以比较方便把类直接插入数据库,如果用平面表这里要执行 1*n次的 insert
  204. 代码例子我就不提供了先,因为太简单了 XMLType( :XMLString ) 然后给对象序列化了
  205. 给到 :XMLString 参数里就可以了;
  206. 3.3 本步骤总结
  207. 如果开发的话,可以节省一些语句;尤其适合那种,子表数据一次插入很少的情况
  208. 而且用 select 读取的时候,应该可以直接反序列化为对象
  209. 能省去往实体类赋值的代码量;
  210. 四、改变 C# 的类重新生成 Schema 在更新数据库中的 Schema
  211. 假设我们的业务更改:那个业务也不可能不变的对吧,所以测试是否容易修改也是必要的
  212. 假设,【电话】加入了说明属性、【电话类型】加入了小灵通;集合类没更改;
  213. 4.1 类的更改
  214. public class Phone
  215. {
  216.     string _code;
  217.     public string Code
  218.     {
  219.         get { return _code; }
  220.         set { _code = value; }
  221.     }
  222.     PhoneType _type;
  223.     public PhoneType Type
  224.     {
  225.         get { return _type; }
  226.         set { _type = value; }
  227.     }
  228.     
  229.     //新加入 
  230.     string _make;
  231.     public string Make
  232.     {
  233.         get { return _make; }
  234.         set { _make = value; }
  235.     }
  236.     
  237. }
  238. public enum PhoneType
  239. {
  240.     [XmlEnum(Name = "未知")]
  241.     Unknown,
  242.     [XmlEnum(Name = "移动")]
  243.     Mobile,
  244.     [XmlEnum(Name = "固定")]
  245.     Fixed,
  246.     [XmlEnum(Name = "小灵通")] //新加入
  247.     PHS
  248.     
  249.     
  250. }
  251. 4.2 重新生成 xsd
  252. 方法在上面说过了
  253. 4.3 数据库更改
  254. declare 
  255.    -- 旧Schema
  256.   oldSchemaDoc nvarchar2(2000) := '<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  257.   <xs:element name="Phones" nillable="true" type="ArrayOfPhone" />
  258.   <xs:complexType name="ArrayOfPhone">
  259.     <xs:sequence>
  260.       <xs:element minOccurs="0" maxOccurs="unbounded" name="Phone" nillable="true" type="Phone" />
  261.     </xs:sequence>
  262.   </xs:complexType>
  263.   <xs:complexType name="Phone">
  264.     <xs:sequence>
  265.       <xs:element minOccurs="0" maxOccurs="1" name="Code" type="xs:string" />
  266.       <xs:element minOccurs="1" maxOccurs="1" name="Type" type="PhoneType" />
  267.     </xs:sequence>
  268.   </xs:complexType>
  269.   <xs:simpleType name="PhoneType">
  270.     <xs:restriction base="xs:string">
  271.       <xs:enumeration value="未知" />
  272.       <xs:enumeration value="移动" />
  273.       <xs:enumeration value="固定" />
  274.     </xs:restriction>
  275.   </xs:simpleType>
  276. </xs:schema>';
  277.  -- 新Schema
  278.   newSchemaDoc nvarchar2(2000) := '<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  279.   <xs:element name="Phones" nillable="true" type="ArrayOfPhone" />
  280.   <xs:complexType name="ArrayOfPhone">
  281.     <xs:sequence>
  282.       <xs:element minOccurs="0" maxOccurs="unbounded" name="Phone" nillable="true" type="Phone" />
  283.     </xs:sequence>
  284.   </xs:complexType>
  285.   <xs:complexType name="Phone">
  286.     <xs:sequence>
  287.       <xs:element minOccurs="0" maxOccurs="1" name="Code" type="xs:string" />
  288.       <xs:element minOccurs="1" maxOccurs="1" name="Type" type="PhoneType" />
  289.       <xs:element minOccurs="0" maxOccurs="1" name="Make" type="xs:string" />
  290.     </xs:sequence>
  291.   </xs:complexType>
  292.   <xs:simpleType name="PhoneType">
  293.     <xs:restriction base="xs:string">
  294.       <xs:enumeration value="未知" />
  295.       <xs:enumeration value="移动" />
  296.       <xs:enumeration value="固定" />
  297.       <xs:enumeration value="小灵通" />
  298.     </xs:restriction>
  299.   </xs:simpleType>
  300. </xs:schema>';
  301.  -- 变量存储 xmldiff xml差异结果
  302.  diffXMLDoc clob;
  303.  -- url
  304.  v_schema_url nvarchar2(255) :=  'http://www.OracleDemo.com/Phones.xsd';
  305. begin 
  306.    --生成差异结果
  307.   select xmldiff(xmltype(oldSchemaDoc),xmltype(newSchemaDoc)).getClobVal() into diffXMLDoc from dual;
  308.   -- 11g 新增加的原地更改函数,性能比原来那个 DBMS_XMLSCHEMA.copyEvolve
  309.   -- 概念 执行原地 XML 模式演变 http://www.oracle.com/technology/global/cn/obe/11gr1_db/datamgmt/xmldb2_a/xmldb2_a.htm
  310.   DBMS_XMLSCHEMA.inPlaceEvolve( v_schema_url ,  xmltype(diffXMLDoc)); 
  311. end;
  312. 流程就是拿 旧的Schema 和 新的Schema 比较生成一个差异结果  select xmldiff...这里,然后调用 DBMS_XMLSCHEMA.inPlaceEvolve
  313. 更新 Schema 很简单函数就用到两个,代码没多少就是2个Schema 占地方;
  314. 4.4 在插入一条数据看看
  315. --- 在插入一条
  316. INSERT INTO XML_USER_INFO VALUES (3,'用户3'
  317.     ,XMLType('<Phones xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  318.   <Phone>
  319.     <Code>13930588003</Code>
  320.     <Type>移动</Type>    
  321.   </Phone>
  322.   <Phone>
  323.     <Code>024-32222223-1</Code>
  324.     <Type>固定</Type>
  325.     <Make>公司</Make>
  326.   </Phone>
  327.   <Phone>
  328.     <Code>024-3788883</Code>
  329.     <Type>小灵通</Type>
  330.     <Make>公司</Make>
  331.   </Phone>
  332. </Phones>'
  333.     )
  334. )
  335. /
  336. 4.5 本步骤总结
  337. 更改还是比较方便的;数据库更改在所难免可以快速的更改才是王道;不过例子比较片面,大待大规模测试
  338. 五、用 C# 写一个网页,显示这些数据;
  339. 5.1 显示效果
  340. 显示效果如下(砢碜点啊)
  341. NPK
  342.  用户名
  343.  电话信息
  344. 1
  345.  用户1
  346.  【13940588000;移动;】、【024-22222222-1;固定;】、【12345678;未知;】
  347. 2
  348.  用户2
  349.  【13920588002;移动;】、【024-22222222-1;固定;】、【2788882;未知;】
  350. 3
  351.  用户3
  352.  【024-32222223-1;固定;公司】、【024-3788883;小灵通;公司】、【13930588003;移动;】
  353. 4
  354.  用户4
  355.  【13940588004;移动;个人】、【024-42222224-1;固定;公司】、【024-4788884;小灵通;公司】、【024-4788844;固定;个人】
  356. 5
  357.  用户5
  358.  【13950588005;移动;个人】、【024-52222225-1;固定;公司】、【024-5788885;小灵通;公司】、【024-5788845;固定;个人】
  359. 5.2 代码
  360. //用户信息(XML_USER_INFO 是一样的) 
  361. public class UserInfo
  362.     {
  363.         int _NPK;
  364.         public int NPK
  365.         {
  366.             get { return _NPK; }
  367.             set { _NPK = value; }
  368.         }
  369.         string _userName;
  370.         public string UserName
  371.         {
  372.             get { return _userName; }
  373.             set { _userName = value; }
  374.         }
  375.         
  376.         Phones _phones;
  377.         public Phones Phones
  378.         {
  379.             get { return _phones; }
  380.             set { _phones = value; }
  381.         }
  382.     }
  383. //数据操作代码 (很简单就是,把数据全差出来)
  384. public class UserInfoDAL
  385. {
  386.     public static List<UserInfo> GetUserInfos()
  387.     {
  388.         //我本身是想用 ODP.net 不过为了让大家看懂就先用 ms 的 OracleClient
  389.         //其实  ODP.net 操作 oracle 更方便点;直接支持 xmlType 的不用转换
  390.         
  391.         //GETSTRINGVAL 是 Oracle xmlType 类的一个函数,以string 形式返回xml 对象数据
  392.         const string SQL = "SELECT t.NPK,t.USER_NAME, t.PHONES.GETSTRINGVAL() as  PHONES FROM XML_USER_INFO t order by 1";
  393.         List<UserInfo> models = new List<UserInfo>();
  394.         string connstring = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
  395.         
  396.         using (OracleConnection conn = new OracleConnection(connstring))
  397.         {
  398.             conn.Open();
  399.             using (OracleCommand cmd = new OracleCommand(SQL, conn))
  400.             {
  401.                 
  402.                 using (OracleDataReader reader = cmd.ExecuteReader())
  403.                 {
  404.                     while (reader.Read())
  405.                     {
  406.                         DataTable dt = reader.GetSchemaTable();
  407.                         
  408.                         int npk = Convert.ToInt32(reader["NPK"]); 
  409.                         
  410.                         string userName = reader["USER_NAME"].ToString();
  411.                         
  412.                         //读取 XML 类型
  413.                         string phonesXml = reader["PHONES"].ToString();
  414.                         
  415.                         //这里是我自己扩展的全局的 string 对象的方法,就是把 string 通过XML反序列化为对象实例
  416.                         Phones ps = phonesXml.XMLToObject<Phones>();    
  417.                         
  418.                         models.Add(new UserInfo() { NPK = npk, UserName=userName, Phones = ps });
  419.                     }
  420.                 }
  421.             }
  422.         }
  423.         return models;
  424.     }
  425. }
  426. //网页的代码
  427. <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="ObjectDataSource1">
  428.     <Columns>
  429.         <asp:BoundField DataField="NPK" HeaderText="NPK" SortExpression="NPK" />
  430.         <asp:BoundField DataField="UserName" HeaderText="用户名" SortExpression="UserName" />
  431.         <asp:BoundField DataField="Phones" HeaderText="电话信息" SortExpression="Phones" />
  432.     </Columns>
  433. </asp:GridView>
  434. <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
  435.     SelectMethod="GetUserInfos" TypeName="WebApplication1.UserInfoDAL">
  436. </asp:ObjectDataSource>
  437. -- 可能有人会问;你的网页【电话信息】那列怎么显示成那个样子的?
  438. -- 其实很简单;重写 Phones 类的 ToString() 想怎么显示都可以的,我这里就不贴代码了
  439. -- 如果有人需要说一声,因为不属于本片博文的技术讨论范围就不贴上了;
  440. 5.3 本步骤总结
  441. 读取操作也是很方便的,比如在做分页的时候,就比连接2个表(用户表、电话表)要方便的多
  442. 而且返回数据,可以直接转换为对象更加方便了;不过数据量大的子表就不太适合了这种方式
  443. 6、总结
  444. 目前看来
  445. XMLType 比起平面表更加类似 class 类
  446. 因为.net 可以直接通过类生成Schema 在生成 XMLType 数据库表,能节省一些数据表的设计时间,改完对象直接更改数据库就可以了
  447. 遗憾:C# 视乎生成不了Schema 的很多约束属性如限制字段长度的约束
  448. 还有集合内的类型,加不了外键比如本来我要是把 电话类型放到一个平面表里
  449. 就加不上外键了;
  450. 处理那些树形的数据比较适合;
  451. 如一个主记录、关联n个子记录的情况
  452. 查询插入都是比较方便的,直接可以序列化或反序列化为对象或xml 能节省些代码;
  453. 遗憾:XML 序列化可能慢点,不过可以使操作更明晰,如果直接操作 xml 的话太头痛了代码也乱
  454. 终于写完了,贴了这么多代码,园子里用Oracle 的不多,可能有人看不懂,不过问我就好了
  455. 我可以解答你,大家共同学习进步吧!
  456. 不过本人公司基本都是玩 oracle 没有时间去玩 sqlserver 抱歉了先
  457. 如果有时间在做个 sqlserver 的例子吧!
  458. 或者出一个 sqlserver 和 oracle 的 xml 类型对比性测试,如果有人愿意和我做这次试验,我可以提供 oracle 的测试机和代码
  459. 我对sqlserver 不是很熟悉的,已经n年没用了,如果我自己出对比测试,怕糟蹋了 sqlserver 
  460. 我家的计算机是 AMD6000 +、4G内存 这种测试应该是没啥问题的,跑些不是超级大的应用还是可以的;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值