惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Threat Research - Cisco Blogs
V2EX - 技术
V2EX - 技术
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
S
Schneier on Security
I
InfoQ
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The GitHub Blog
The GitHub Blog
S
Security @ Cisco Blogs
O
OpenAI News
W
WeLiveSecurity
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
人人都是产品经理
人人都是产品经理
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
NISL@THU
NISL@THU
T
Tailwind CSS Blog
V
Visual Studio Blog
PCI Perspectives
PCI Perspectives
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Jina AI
Jina AI
D
DataBreaches.Net
B
Blog RSS Feed
N
News and Events Feed by Topic
N
News and Events Feed by Topic
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
腾讯CDC
Latest news
Latest news
V
Vulnerabilities – Threatpost
Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
V
V2EX
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
Help Net Security
Help Net Security

博客园 - 曲滨*銘龘鶽

IE8 隐藏命令行参数 学习 ASP.NET mvc 第一天、也可能是最后一天 Visual Studio 2008 开启被遗忘的 “同步类视图”功能、附带一个MSDN 的 bug 实战 IE8 开发人员工具 windows 桌面开发 - 子类化控件(不用任何WinAPI),演示拦截Button的WM_LBUTTONDBLCLK 在真实项目中使用第三方或开源代的代码,组件,中间件,框架的基本规则 你有?项目设计开发阶段,甲方经常的要求看程序,而经常在做【假界面】【假程序】的情况吗? Windows Live Writer 测试 文档共享:罗斯文2007 (Northwind 2007),数据库文件,中文版本、英文版、英文表结构中文数据版 本机 MSDN Sorry, no topics were found for the selected link 问题,及临时解决方案 2008-9-6 文档共享:罗斯文2007 (Northwind 2007),数据库,微软最新的 Access 2007 样列数据库分析(中文/英文) IE7,ie8说爱你不容易,企业应用困局 如何:在 Winform 动态启动、控制台命令行? 如何:使 comboBox 输入状态变成 readonly 方式;TextBox 只读时的效果; 设计模式学习:Model View Presenter (MVP) C# 直接执行、调用本机代码、汇编代码 shell Native Code OneDay “屏幕任务”小软件 TaskScreen_080408 Oracle 使用数据泵 expdp impdp 导入导出数据库“表空间”文件 适合编程开发用的"宋体"和"新宋体"
C# 操作 XML 数据库类型、Oracle XMLType
曲滨*銘龘鶽 · 2008-09-02 · via 博客园 - 曲滨*銘龘鶽

这次学习要做的事情

1、设计一个C# 类来生成 XML Schema (XML 架构)

先设计类,然后由类生成表可能是有很多人蒙昧以求的;正好和 ORM 相反

最主要的 XML 架构可以用来约束数据库中的;XML 的有效性

2、基于上一步的 XML Schema 我们创建一个数据表、并包含这个和上边 C# 类对应的的 XML 类型;

3、插入一些数据;

4、改变 C# 的类重新生成 Schema 在更新数据库中的 Schema

这步假设业务变更看看,更改如何进行,是否方便等

5、用 C# 写一个网页,显示这些数据;

6、阶段性总结

好开始:

一、设计一个C# 类来生成 XML Schema类代码:

类如下

namespace Model
{
    
    
/// <summary>
    
/// 电话
    
/// </summary>
    public class Phone
    {
        
string _code;
        
/// <summary>
        
/// 电话号
        
/// </summary>
        public string Code
        {
            
get { return _code; }
            
set { _code = value; }
        }
        PhoneType _type;
        
public PhoneType Type
        {
            
get { return _type; }
            
set { _type = value; }
        }        
        
    }
    
/// <summary>
    
/// 电话类型
    
/// </summary>
    public enum PhoneType
    {
        [XmlEnum(Name 
= "未知")] //XML 序列化用的名称
        Unknown,
        [XmlEnum(Name 
= "移动")]
        Mobile,
        [XmlEnum(Name 
= "固定")]
        Fixed,
        
        
        
    }
    
/// <summary>
    
/// 电话集合
    
/// </summary>
    [XmlRoot("Phones")]
    
public class Phones : List<Phone>
    {
        
public void Add(string code, PhoneType type)
        {
            
base.Add(new Phone() { Code = code, Type = type });
        }
        
    }
}

3个类:电话、电话类型(枚举)、电话类型集合;

 Phones 类生成 Schema  ,用 .net sdk xsd.exe 或自己写代码都可以生成的、我就不多说了;

Schema  这东西如果纯手写也是劳动量巨大的;谢谢.net 为我们提供这个功能吧;

Schema 如下 :

<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  
<xs:element name="Phones" nillable="true" type="ArrayOfPhone" />
  
<xs:complexType name="ArrayOfPhone">
    
<xs:sequence>
      
<xs:element minOccurs="0" maxOccurs="unbounded" name="Phone" nillable="true" type="Phone" />
    
</xs:sequence>
  
</xs:complexType>
  
<xs:complexType name="Phone">
    
<xs:sequence>
      
<xs:element minOccurs="0" maxOccurs="1" name="Code" type="xs:string" />
      
<xs:element minOccurs="1" maxOccurs="1" name="Type" type="PhoneType" />
    
</xs:sequence>
  
</xs:complexType>
  
<xs:simpleType name="PhoneType">
    
<xs:restriction base="xs:string">
      
<xs:enumeration value="未知" />
      
<xs:enumeration value="移动" />
      
<xs:enumeration value="固定" />
    
</xs:restriction>
  
</xs:simpleType>
</xs:schema>

二、我们创建一个数据表、并包含这个和上边 C# 类对应的的 XML 类型;

2.1 注册架构:

BEGIN
    
-- 注册架构
  DBMS_XMLSCHEMA.registerschema(schemaurl => 'http://www.OracleDemo.com/Phones.xsd',
                                schemadoc 
=> '<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Phones" nillable="true" type="ArrayOfPhone" />
  <xs:complexType name="ArrayOfPhone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="Phone" nillable="true" type="Phone" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="Phone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="Code" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="Type" type="PhoneType" />
    </xs:sequence>
  </xs:complexType>
  <xs:simpleType name="PhoneType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="未知" />
      <xs:enumeration value="移动" />
      <xs:enumeration value="固定" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>
',
                                
                                local     
=> TRUE,
                                gentypes  
=> TRUE,
                                genbean   
=> FALSE,
                                gentables 
=> TRUE);END;
--会建立 xml 描述的【Oracle自定义类型】
--
 如果用Oracle 工具查看 Types 下会出现一些如 Phone***_T,phone***_coll,ArrayOfPhone***_T 类似名称的 【Oracle自定义类型】
--
 结构就和xml schema 是一样
--
 gentables => TRUE 还会建立一些表;
--
 如 create table Phones721_TAB of SYS.XMLTYPE --物理表
--
 create table SYS_NTyIVemDaJQXqHZgjqYv+haQ== of Phone711_T --自定义类型表

 2.2 创建表

CREATE TABLE XML_USER_INFO (
 NPK 
integer,
 
USER_NAME NVARCHAR2(50),
 Phones XMLType,
 
primary key (NPK)
)
XMLTYPE 
COLUMN Phones STORE AS OBJECT RELATIONAL          --以对象关系方式建立,而不是二进制
XMLSCHEMA "http://www.OracleDemo.com/Phones.xsd"
ELEMENT "Phones"
VARRAY Phones.XMLDATA."Phone" STORE 
AS TABLE XML_USER_INFO_XMLNT01 --将xml中 Phones/Phone 定义为一个数组嵌套表
/
-- 返回:成功
--
 这时 oracle 还会建立一个 XML_USER_INFO_XMLNT01 的【嵌套表】

我建立了一个叫 XML_USER_INFO 的表,这个东西假设为一个用户信息表(真正的用户信息不可能这么少列的)

Oracle 建立XMLType 时可以指定以二进制或对象关系方式建立xml 类型,

我这里选择 STORE AS OBJECT RELATIONAL 据说可以提高性能,等待考证

下一面我要给 Phones/Phone 下加一些约束所以将 Phone 定义为一个数组嵌套表;说白了就是定义一个表里面都放置 Phone 类型

2.3 定义约束

ALTER TABLE XML_USER_INFO_XMLNT01 

       ADD constraint PK_XML_USER_INFO_XMLNT01 primary key (NESTED_TABLE_ID, "Code") 

就是每个人的电话号码不能重复; 数据库里的东西要是没有约束是很郁闷的,这里也试验一下这个问题;

constraint PK_XML_USER_INFO_XMLNT01 可以省略,因为有名字比较容易从异常中看出到底是什么列出错,我一般都会加上这个

除非一个表就一个主键;

 遗憾的是集合类型的元素、不能加外键(就是说如果是 Phones 的属性是可以加外键的 Phone属性,如电话号码,类型什么的就不行,必须和数据库表一行能对应上的才可以加外键否则只能用 schema 约束);

 外键的例子以后在说吧;

2.4 本步骤总结

比较满意、虽然SQL 代码很多不过,schema 是类生成的、能够节省一些设计时间,不过学习成本是必须的;

XMLType 的 schema 并不是必须的,不过没有约束,关系对数据库来说,时间长了是不好维护的尤其是后来人;

三、插入一些数据

3.1 插入数据

INSERT INTO XML_USER_INFO VALUES (1,'用户1'

    ,XMLType(

'<Phones xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:www.w3.org/2001/XMLSchema">

 <Phone>

    <Code>13940588000</Code>

    <Type>移动</Type>

 </Phone>

 <Phone>

    <Code>024-22222222-1</Code>

    <Type>固定</Type>

 </Phone>

 <Phone>

    <Code>8788888</Code>

    <Type>未知</Type>

 </Phone>

</Phones>

'

    )

)

/

3.2 测试约束有效性(在Type改小灵通)

INSERT INTO XML_USER_INFO VALUES (2,'用户2'
    ,XMLType(
'<Phones xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:www.w3.org/2001/XMLSchema">
  <Phone>
    <Code>13940588000</Code>
    <Type>移动</Type>
  </Phone>
  <Phone>
    <Code>13940588000</Code>
    <Type>固定</Type>
  </Phone>
  <Phone>
    <Code>8788888</Code>
    <Type>小灵通</Type>
  </Phone>
</Phones>
'
    )
)
-- 返回错误:ORA-31038: enumeration 值无效: "小灵通"
--
 证明 PhoneType 的 XMLSchema 约束是有效的(废话);
/

….插入我就不多写了都这模样

注意:大家可以看出xmltype这里的数据,就是 Phones 对象xml序列化后的的样子

也就是说我们可以比较方便把类直接插入数据库,如果用平面表这里要执行 1*n次的 insert

代码例子我就不提供了先,因为太简单了 XMLType( :XMLString ) 然后给对象序列化了

给到 :XMLString 参数里就可以了;

3.3 本步骤总结

如果开发的话,可以节省一些语句;尤其适合那种,子表数据一次插入很少的情况

而且用 select 读取的时候,应该可以直接反序列化为对象

能省去往实体类赋值的代码量;

四、改变 C# 的类重新生成 Schema 在更新数据库中的 Schema

假设我们的业务更改:那个业务也不可能不变的对吧,所以测试是否容易修改也是必要的

假设,【电话】加入了说明属性、【电话类型】加入了小灵通;集合类没更改;

4.1 类的更改

public class Phone
{
    
string _code;public string Code
    {
        
get { return _code; }
        
set { _code = value; }
    }
    PhoneType _type;
    
public PhoneType Type
    {
        
get { return _type; }
        
set { _type = value; }
    }
    
    
//新加入 
    string _make;
    
public string
 Make
    {
        
get { return
 _make; }
        
set { _make =
 value; }
    }

    
}
public enum PhoneType
{
    [XmlEnum(Name 
= "未知")]
    Unknown,
    [XmlEnum(Name 
= "移动")]
    Mobile,
    [XmlEnum(Name 
= "固定")]
    Fixed,
    [XmlEnum(Name 
= "小灵通")] //新加入
    PHS
    
    
}

4.2 重新生成 xsd

方法在上面说过了

4.3 数据库更改

declare 
   
-- 旧Schema
  oldSchemaDoc nvarchar2(2000) := '<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Phones" nillable="true" type="ArrayOfPhone" />
  <xs:complexType name="ArrayOfPhone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="Phone" nillable="true" type="Phone" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="Phone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="Code" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="Type" type="PhoneType" />
    </xs:sequence>
  </xs:complexType>
  <xs:simpleType name="PhoneType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="未知" />
      <xs:enumeration value="移动" />
      <xs:enumeration value="固定" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>
';
 
-- 新Schema
  newSchemaDoc nvarchar2(2000) := '<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Phones" nillable="true" type="ArrayOfPhone" />
  <xs:complexType name="ArrayOfPhone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="Phone" nillable="true" type="Phone" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="Phone">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="Code" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="Type" type="PhoneType" />
      <xs:element minOccurs="0" maxOccurs="1" name="Make" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
  <xs:simpleType name="PhoneType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="未知" />
      <xs:enumeration value="移动" />
      <xs:enumeration value="固定" />
      <xs:enumeration value="小灵通" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>
';
 
-- 变量存储 xmldiff xml差异结果
 diffXMLDoc clob;
 
 
-- url
 v_schema_url nvarchar2(255) :=  'http://www.OracleDemo.com/Phones.xsd';
begin 
   
--生成差异结果
  select xmldiff(xmltype(oldSchemaDoc),xmltype(newSchemaDoc)).getClobVal() into diffXMLDoc from dual;
  
-- 11g 新增加的原地更改函数,性能比原来那个 DBMS_XMLSCHEMA.copyEvolve
  -- 概念 执行原地 XML 模式演变 http://www.oracle.com/technology/global/cn/obe/11gr1_db/datamgmt/xmldb2_a/xmldb2_a.htm
  DBMS_XMLSCHEMA.inPlaceEvolve( v_schema_url ,  xmltype(diffXMLDoc)); 
end;

流程就是拿 旧的Schema 新的Schema 比较生成一个差异结果  select xmldiff...这里,然后调用 DBMS_XMLSCHEMA.inPlaceEvolve

更新 Schema 很简单函数就用到两个,代码没多少就是2个Schema 占地方;

4.4 在插入一条数据看看

--- 在插入一条
INSERT INTO XML_USER_INFO VALUES (3,'用户3'
    ,XMLType(
'<Phones xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Phone>
    <Code>13930588003</Code>
    <Type>移动</Type>    
  </Phone>
  <Phone>
    <Code>024-32222223-1</Code>
    <Type>固定</Type>
    <Make>公司</Make>
  </Phone>
  <Phone>
    <Code>024-3788883</Code>
    <Type>小灵通</Type>
    <Make>公司</Make>
  </Phone>
</Phones>
'
    )
)
/

4.5 本步骤总结

更改还是比较方便的;数据库更改在所难免可以快速的更改才是王道;不过例子比较片面,大待大规模测试

五、用 C# 写一个网页,显示这些数据;

5.1 显示效果

显示效果如下(砢碜点啊)

NPK

用户名

电话信息

1

用户1

【13940588000;移动;】、【024-22222222-1;固定;】、【12345678;未知;】

2

用户2

【13920588002;移动;】、【024-22222222-1;固定;】、【2788882;未知;】

3

用户3

【024-32222223-1;固定;公司】、【024-3788883;小灵通;公司】、【13930588003;移动;】

4

用户4

【13940588004;移动;个人】、【024-42222224-1;固定;公司】、【024-4788884;小灵通;公司】、【024-4788844;固定;个人】

5

用户5

【13950588005;移动;个人】、【024-52222225-1;固定;公司】、【024-5788885;小灵通;公司】、【024-5788845;固定;个人】

5.2 代码

//用户信息(XML_USER_INFO 是一样的)

public class UserInfo
    {
        
int _NPK;public int NPK
        {
            
get { return _NPK; }
            
set { _NPK = value; }
        }
        
string _userName;
        
public string UserName
        {
            
get { return _userName; }
            
set { _userName = value; }
        }
        
        Phones _phones;
        
public Phones Phones
        {
            
get { return _phones; }
            
set { _phones = value; }
        }

    }

//数据操作代码 (很简单就是,把数据全差出来)

public class UserInfoDAL
{
    
public static List<UserInfo> GetUserInfos()
    {
        
//我本身是想用 ODP.net 不过为了让大家看懂就先用 ms 的 OracleClient
        
//其实  ODP.net 操作 oracle 更方便点;直接支持 xmlType 的不用转换
        
        
//GETSTRINGVAL 是 Oracle xmlType 类的一个函数,以string 形式返回xml 对象数据
        const string SQL = "SELECT t.NPK,t.USER_NAME, t.PHONES.GETSTRINGVAL() as  PHONES FROM XML_USER_INFO t order by 1";

        List

<UserInfo> models = new List<UserInfo>();
        
string connstring = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;using (OracleConnection conn = new OracleConnection(connstring))
        {
            conn.Open();
            
using (OracleCommand cmd = new OracleCommand(SQL, conn))
            {
                
                
using (OracleDataReader reader = cmd.ExecuteReader())
                {
                    
while (reader.Read())
                    {
                        DataTable dt 
= reader.GetSchemaTable();
                        
                        
int npk = Convert.ToInt32(reader["NPK"]); 
                        
                        
string userName = reader["USER_NAME"].ToString();
                        
                        
//读取 XML 类型
                        string phonesXml = reader["PHONES"].ToString();
                        
                        
//这里是我自己扩展的全局的 string 对象的方法,就是把 string 通过XML反序列化为对象实例
                        Phones ps = phonesXml.XMLToObject<Phones>();    
                        
                        models.Add(
new UserInfo() { NPK = npk, UserName=userName, Phones = ps });
                    }
                }
            }
        }
return models;

    }
}

//网页的代码

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="ObjectDataSource1">
    
<Columns>
        
<asp:BoundField DataField="NPK" HeaderText="NPK" SortExpression="NPK" />
        
<asp:BoundField DataField="UserName" HeaderText="用户名" SortExpression="UserName" />
        
<asp:BoundField DataField="Phones" HeaderText="电话信息" SortExpression="Phones" />
    
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
    SelectMethod
="GetUserInfos" TypeName="WebApplication1.UserInfoDAL">
</asp:ObjectDataSource>

-- 可能有人会问;你的网页【电话信息】那列怎么显示成那个样子的?

-- 其实很简单;重写 Phones 类的 ToString() 想怎么显示都可以的,我这里就不贴代码了

-- 如果有人需要说一声,因为不属于本片博文的技术讨论范围就不贴上了;

5.3 本步骤总结

读取操作也是很方便的,比如在做分页的时候,就比连接2个表(用户表、电话表)要方便的多

而且返回数据,可以直接转换为对象更加方便了;不过数据量大的子表就不太适合了这种方式

6、总结

目前看来

XMLType 比起平面表更加类似 class

因为.net 可以直接通过类生成Schema 在生成 XMLType 数据库表,能节省一些数据表的设计时间,改完对象直接更改数据库就可以了

遗憾:C# 视乎生成不了Schema 的很多约束属性如限制字段长度的约束

还有集合内的类型,加不了外键比如本来我要是把 电话类型放到一个平面表里

就加不上外键了;

处理那些树形的数据比较适合;

如一个主记录、关联n个子记录的情况

查询插入都是比较方便的,直接可以序列化或反序列化为对象或xml 能节省些代码;

遗憾:XML 序列化可能慢点,不过可以使操作更明晰,如果直接操作 xml 的话太头痛了代码也乱

终于写完了,贴了这么多代码,园子里用Oracle 的不多,可能有人看不懂,不过问我就好了

我可以解答你,大家共同学习进步吧!

不过本人公司基本都是玩 oracle 没有时间去玩 sqlserver 抱歉了先

如果有时间在做个 sqlserver 的例子吧!

或者出一个 sqlserver 和 oracle 的 xml 类型对比性测试,如果有人愿意和我做这次试验,我可以提供 oracle 的测试机和代码

我对sqlserver 不是很熟悉的,已经n年没用了,如果我自己出对比测试,怕糟蹋了 sqlserver

我家的计算机是 AMD6000 +、4G内存 这种测试应该是没啥问题的,跑些不是超级大的应用还是可以的;

很晚了,错字明天在挑吧 呵呵;