database的XML Schema描述(200分)

  • 主题发起人 曹晓钢
  • 开始时间

曹晓钢

Unregistered / Unconfirmed
GUEST, unregistred user!
近来对生成机很感兴趣。
要使用生成机(Generator),以下简称Gen,必须是建立在一个确定的编码基础和规范之上的。
先从头说一下问题的由来。
编写数据库相关的程序,以往在非面向对象编程的时代,是通过直接调用SQL语句来实现的。
现在是需要编写相关的类来操作数据库。每一个类可以对应一个Java类,那么这些类操作数据库
的基本代码是相同,唯一不同的是property和get/set方法。(暂且不考虑特定的附加函数)除此之外,
还要考虑类和类之间的关系,比如A和B 是1:N关系等等。
为了最好的描述database,第一个想到的当然就是SQL语句
比如
Create table PO (
PO_NO VARCHAR2(20) NOT NULL,
CUSTOMER VARCHAR(20) NOT NULL,
PO_DATE DATETIME,
QTY NUMBER,
PRIMARY KEY PK_PO (PO_NO)
)
可以看到,如果程序来处理这样的SQLscript会有不小的困难,比如不同的数据库的字段类型是不同的。
在这种情况下,XML Schema是最好的一个选择。由于Schema标准直接支持数据类型,所以带来了一些便利。
而且由于XML本身是很容易扩充的,加上诸如validate regexp pattern等等也很容易。
这时,我们面临着如何选择XML schema的问题。
第一个想到的就是有没有这样的标准。很可惜,我到现在都没有找到相关的标准。
第二选择就是自己实现。缺点是不便于交流。
第三是看现有的一些工具。
现在就汇报一下我试验过的工具。
1,XML Spy 版本4.1
XML Spy支持从DBMS中直接generate XML Schema.
如下是一个例子:
代码:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<xsd:element name="PO_MASTER">
		<xsd:complexType>
			<xsd:sequence>
				<xsd:element name="PO_NO">
					<xsd:simpleType>
						<xsd:restriction base="xsd:string">
							<xsd:maxLength value="20"/>
						</xsd:restriction>
					</xsd:simpleType>
				</xsd:element>
				<xsd:element name="SUPPLIER_CODE">
					<xsd:simpleType>
						<xsd:restriction base="xsd:string">
							<xsd:maxLength value="10"/>
						</xsd:restriction>
					</xsd:simpleType>
				</xsd:element>
				<xsd:element name="PO_DATE" type="xsd:dateTime"/>
				<xsd:element name="SHIPPING_MODE">
					<xsd:simpleType>
						<xsd:restriction base="xsd:string">
							<xsd:maxLength value="10"/>
						</xsd:restriction>
					</xsd:simpleType>
				</xsd:element>
				<xsd:element name="SUPPLIER_ADDRESS">
					<xsd:simpleType>
						<xsd:restriction base="xsd:string">
							<xsd:maxLength value="10"/>
						</xsd:restriction>
					</xsd:simpleType>
				</xsd:element>
				<xsd:element name="ORDER_TYPE" type="xsd:decimal"/>
				<xsd:element name="CUST_CODE">
					<xsd:simpleType>
						<xsd:restriction base="xsd:string">
							<xsd:maxLength value="10"/>
						</xsd:restriction>
					</xsd:simpleType>
				</xsd:element>
			</xsd:sequence>
		</xsd:complexType>
	</xsd:element>
</xsd:schema>
XML spy正确的生成了所有字段的声明和类型,但是令人失望的是,他没有正确的生成
Index的信息。所有的主键,外键都丢失了。从XML spy的文档看,他只有在dbms是access和
其他几个数据库的时候才能正确的得到Index信息。
2, Power designer 版本8
Power design 版本8可以从数据库中reverse engineer出physical模型,然后生成XML schema 模型。
结果是:
<?xml version = "1.0"?>
<schema name="PhysicalDataModel_1.xml">
<element name="PO_MASTER">
<element name="PO_NO" type="string"/>
<element name="SUPPLIER_CODE" type="string"/>
<element name="PO_DATE" type="date"/>
<element name="SHIPPING_MODE" type="string"/>
<element name="SUPPLIER_ADDRESS" type="string"/>
<element name="ORDER_TYPE" type="double"/>
<element name="CUST_CODE" type="string"/>
</element>
</schema>
结果倒是比较容易看懂,问题是他也没有生成index。而且语法也有些过时。
3, PowerDesigner 版本9
我试验的是版本9的eval版本。
结果是:
<?xml version = "1.0" encoding="UTF-8"?>
<xsd:schema name="PhysicalDataModel_1.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="PO_MASTER">
<xsd:element name="PO_NO" type="xsd:string"/>
<xsd:element name="SUPPLIER_CODE" type="xsd:string"/>
<xsd:element name="PO_DATE" type="xsd:date"/>
<xsd:element name="SHIPPING_MODE" type="xsd:string"/>
<xsd:element name="SUPPLIER_ADDRESS" type="xsd:string"/>
<xsd:element name="ORDER_TYPE" type="xsd:double"/>
<xsd:element name="CUST_CODE" type="xsd:string"/>
</xsd:element>
</xsd:schema>
可惜仍然没有Index 生成。这一次开始使用xsd空间了。
鉴于手头就这么几个工具,我也没有找到更好的结果。
如果各位也对这个部分感兴趣,请说一下您的结果。
 
我不知道你用的是什么方式,但是我用xml spy 对 SQL server 是可以导出PK 和 FK的 :)
 
oracle 的索引构架,没有能被odbc, ole db 方式识别出来。
 
唉,XML spy靠近MS阵营,估计只对Access/SQL server管用
除了以上我提到的之外,还有jakarta Torque,也是个生成机,我还没有试验他dump出来的
schema如何,不过我看它的例子里的Java代码生成的实在是不怎么样,不实用。
还不如我们自己做的工具呢。
 
我觉得 SQL script 也由 Generator 生成。
当我们进行概念设计、逻辑设计和数据模型设计后,得到了类关系图。
(最近我发现用 UML 做 Data modeling 很好)
把实体类、关系类表达成 XML,然后用 Generator 生成 SQL 和 java bean, factory ...
这样就解决您的问题了,因为PK, FK 都是您定义的。
 
XML 格式的问题我也比较了一些可选方案:
1、DTD 描述法
著名的 Java Architecture for XML Binding (JAXB) 就是用此法
http://java.sun.com/xml/jaxb/index.html
2、schema 描述法
就是上面曹大侠想用的方法。问题是:目前尚未发现有统一的行业规范,
更加严重的是,无论哪一种都很难读懂。
因此,我提出了“实例 XML 描述法” - XML By Example
上面曹大侠的例子就是:
<PO>
<PO_NO _pk="yes" null="no">VARCHAR2(20)</PO_NO>
<CUSTOMER>VARCHAR(20)</CUSTOMER>
<PO_DATE>DATETIME</PO_DATE>
<QTY>NUMBER</QTY>
</PO>
这个看起来要容易得多了吧。用个 XSLT 把这段 XML,转换成 SQL,Java 类是很容易的。
我简单把这个思路写成了文章《用XSLT生成源代码》,它已经被《程序员》杂志录用。
http://www.richsearch.com/dfwlib/xsl4srcdoc.zip
http://www.richsearch.com/dfwlib/xsl4src.zip
 
最后,如果您已经建好了数据库怎么办?
那么我们来个 database reverse engine,得到 XML by Example
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1002026
 
不错,SQL 是可以很方便的由XML 生成,但是这是在数据库定义已经有XML格式存在的前提下。
我现在就想要这个schema的格式。
我没用过 Rose,我用power designer,在设计的时候,并非是直接生成SQL,而是概念模型,
包括某个Entity的定义和各个Entity之间的关系。应该说这个Entity-relation就是最本质的
ER模型了,至于输出成为XML 或者SQL script都是等价的。有了ER关系,再生成XML schema和SQL
或者先生成SQL再声称XML schema是一样的。
而PowerDesigner我测试它的Concept Model生成的Object Model,选择object language是XML schema的话,
会丢了index的信息。
据Seasky提供的XML spy 4.3资料显示,它可以正确地读取Oracle的index信息。
我正在做这个试验。
 
你的这个其实就是我说的,自己定义XML schema。
自己定义一个并不是难事,我也有一个...这个是例子:
代码:
    <DatabaseType>MySQL</DatabaseType>
    <Table Catalog="" Generate="1" JavaName="Contact" Name="CONTACT"
        Remarks="" Schema="" Type="TABLE">
        <Column JavaName="toolID" Name="TOOLID">
            <TypeName>int</TypeName>
            <DataType>4</DataType>
            <Size>11</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault>0</ColumnDefault>
            <Remarks/>
            <Nullable>true</Nullable>
            <CharOctetLength>11</CharOctetLength>
            <OrdinalPosition>1</OrdinalPosition>
        </Column>
        <Column JavaName="contactID" Name="CONTACTID">
            <TypeName>int</TypeName>
            <DataType>4</DataType>
            <Size>11</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault>0</ColumnDefault>
            <Remarks/>
            <Nullable>true</Nullable>
            <CharOctetLength>11</CharOctetLength>
            <OrdinalPosition>2</OrdinalPosition>
        </Column>
        <Column JavaName="name" Name="NAME">
            <TypeName>varchar</TypeName>
            <DataType>12</DataType>
            <Size>64</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>64</CharOctetLength>
            <OrdinalPosition>3</OrdinalPosition>
        </Column>
        <Column JavaName="email" Name="EMAIL">
            <TypeName>varchar</TypeName>
            <DataType>12</DataType>
            <Size>255</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>255</CharOctetLength>
            <OrdinalPosition>4</OrdinalPosition>
        </Column>
        <Column JavaName="company" Name="COMPANY">
            <TypeName>varchar</TypeName>
            <DataType>12</DataType>
            <Size>255</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>255</CharOctetLength>
            <OrdinalPosition>5</OrdinalPosition>
        </Column>
        <Column JavaName="title" Name="TITLE">
            <TypeName>varchar</TypeName>
            <DataType>12</DataType>
            <Size>255</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>255</CharOctetLength>
            <OrdinalPosition>6</OrdinalPosition>
        </Column>
        <Column JavaName="country" Name="COUNTRY">
            <TypeName>varchar</TypeName>
            <DataType>12</DataType>
            <Size>100</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>100</CharOctetLength>
            <OrdinalPosition>7</OrdinalPosition>
        </Column>
        <Column JavaName="city" Name="CITY">
            <TypeName>varchar</TypeName>
            <DataType>12</DataType>
            <Size>100</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>100</CharOctetLength>
            <OrdinalPosition>8</OrdinalPosition>
        </Column>
        <Column JavaName="address" Name="ADDRESS">
            <TypeName>varchar</TypeName>
            <DataType>12</DataType>
            <Size>255</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>255</CharOctetLength>
            <OrdinalPosition>9</OrdinalPosition>
        </Column>
        <Column JavaName="phone" Name="PHONE">
            <TypeName>varchar</TypeName>
            <DataType>12</DataType>
            <Size>100</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>100</CharOctetLength>
            <OrdinalPosition>10</OrdinalPosition>
        </Column>
        <Column JavaName="notes" Name="NOTES">
            <TypeName>text</TypeName>
            <DataType>-1</DataType>
            <Size>65535</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>65535</CharOctetLength>
            <OrdinalPosition>11</OrdinalPosition>
        </Column>
        <Column JavaName="createdBy" Name="CREATEDBY">
            <TypeName>int</TypeName>
            <DataType>4</DataType>
            <Size>11</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>11</CharOctetLength>
            <OrdinalPosition>12</OrdinalPosition>
        </Column>
        <Column JavaName="createdDate" Name="CREATEDDATE">
            <TypeName>datetime</TypeName>
            <DataType>93</DataType>
            <Size>19</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>19</CharOctetLength>
            <OrdinalPosition>13</OrdinalPosition>
        </Column>
        <Column JavaName="lastModifiedBy" Name="LASTMODIFIEDBY">
            <TypeName>int</TypeName>
            <DataType>4</DataType>
            <Size>11</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>11</CharOctetLength>
            <OrdinalPosition>14</OrdinalPosition>
        </Column>
        <Column JavaName="lastModifiedDate" Name="LASTMODIFIEDDATE">
            <TypeName>datetime</TypeName>
            <DataType>93</DataType>
            <Size>19</Size>
            <DecimalDigits>0</DecimalDigits>
            <NumberPrecRadix>10</NumberPrecRadix>
            <ColumnDefault/>
            <Remarks/>
            <Nullable>false</Nullable>
            <CharOctetLength>19</CharOctetLength>
            <OrdinalPosition>15</OrdinalPosition>
        </Column>
        <PrimaryKey Name="TOOLID">
            <KeySeq>1</KeySeq>
            <PKName>TOOLID</PKName>
        </PrimaryKey>
        <PrimaryKey Name="CONTACTID">
            <KeySeq>2</KeySeq>
            <PKName>CONTACTID</PKName>
        </PrimaryKey>
    </Table>
我是想做到尽量的标准。
apache的torque 的schema 也许可以作为标准。
描述在这里:
http://jakarta.apache.org/turbine/torque/schema-reference.html
 
《程序员》2002年第五期将登我的文章,不过改了标题:
(66页) 应用XML技术辅助程序设计 作者:孙以义
本文将介绍用XML来表达类的属性、并且用XSLT来自动生成相应的数据库script、
VBScript class以及JavaBean的源程序,以期简化和方便软件的设计工作。另外,
本文使用实例化XML来驱动XSLT生成源程序的方法,具有更加易于理解和操作的特点。
(我对《程序员》编辑的这一句话概要总结很满意,言简意骇)
我也没有用 ROSE, 画图我用的是 ArgoUML, XML 用 notepad 写的。
我觉得 index 不重要,重要的是 primary key 和 foreign key 的定义。
 
您说的自定义 XML schema 和我说的 XML By Example 有本质的区别:
您有试图用标准的 tag: DatabaseType、Table、Column、TypeName ....
而我采用的是 Entity 及其属性作为 Tag,完全不同啊。
 
笔误,我说的index就是包括了key的。
关键是PK,这是最基础的,FK其实我想你也没用到,FK是描述对象之间联系的。
 
你的那个,我看就是SQL的一个等价XML描述嘛。
 
PK 的作用是做 findByKey,update,delete。
FK 也很重要要,我有一个 pattern,叫 loadBy,就用到 FK,
比如 order 有个 FK 叫 customerId,根据这个 FK,就能生成 order.loadByCustomerId
这么一个很实用的函数(实现了 UML 中的 navigation)。
 
order.loadByCustomerId是返回一个Collection,对吗?
 
正确,order.loadByCustomerId 是返回一个 Collection (Vector)
我的 XML By Example 绝对不是 SQL 的等价XML描述。
我可以定义嵌套对象,我的 XSLT 已经能够处理下面的 XML。
<order>
<orderId>int</orderId>
<customer>
<customerId>int</customerId>
<customer>
</order>
 
这个东东搞出来以后,不知有何用处?
是为了象老孙那样“用XSLT来自动生成相应的数据库script、
VBScript class以及JavaBean的源程序,以期简化和方便软件的设计工作”吗?
 
老左啊,要了解用处请先读一下这篇文章:
http://www.javaworld.com/javaworld/jw-02-2002/jw-0201-xslt.html
然后看看下面贴子中:关于框架(framework)和自动代码的 比较。
http://www.delphibbs.com/delphibbs/dispq.asp?lid=831468
实际应用中,比如 新版大富翁练功场,我只要定义一些 XML,用我们在这里讨论的办法,
80%以上 的 Java Bean,Factory class,Adapter class,JSP 和 XSL 源程序都能自动生成出来。
源程序自动生成!
下面这段程序,我没有打一个字,而是用 XSLT 生成的(包括注释)。
package com.delphibbs.lgc.factory;
/**
* Factory Class: ProjectFactory
* Generated by Lgc Auto code tool on [date]
*
* Copyright (C) 1999-2002 Yiyi Sun. All rights reserved.
* This software is the proprietary information of richsearch.com
* Use is subject to license terms.
*/

import java.util.Vector;
import com.delphibbs.lgc.bean.*;
public class ProjectFactory extends DBFactory {
/**
* Create Project Object and populate value from "projects"
* Used internally by DBFactory.LoadWithSql function
*
* @return Project Object
* Generated by Lgc Auto code tool on [date]
*/
protected Base createObject() {
Project tmp = new Project();
tmp.setName(getValue("name"));
tmp.setDescription(getValue("description"));
tmp.setModifiedDate(getValue("modifiedDate"));
tmp.setCreatedDate(getValue("createdDate"));
tmp.setProjectId(getValue_int("projectId"));
tmp.setEnabled(getValue_int("enabled"));
return (Base) tmp;
}
/**
* Create Project Object and populate value from "projects" based on primary key
*
* @return Project Object
* Generated by Lgc Auto code tool on [date]
*/
public Project findByKey(int id) {
String sql = "select * from projects where projectId = " + id;
if (loadWithSql(sql).size()>0)
return (Project) objList.elementAt(0);
else
return null;
}
/**
* Create a record in "projects"
*
* @return true if record created successfully, otherwise return false
* Generated by Lgc Auto code tool on [date]
*/
public boolean create(
int projectId,
String name,
String description,
int enabled,
String modifiedDate,
String createdDate
) {
String sql = "insert into projects( " +
"projectId, " +
"name, " +
"description, " +
"enabled, " +
"modifiedDate, " +
"createdDate" +
") values( " +
prepareParam(projectId) + "," +
prepareParam(name) + "," +
prepareParam(description) + "," +
prepareParam(enabled) + "," +
prepareParam(modifiedDate) + "," +
prepareParam(createdDate) +
")";
return executeSql(sql);
}
/**
* Update a record in "projects"
*
* @return true if record updated successfully, otherwise return false
* Generated by Lgc Auto code tool on [date]
*/
public boolean update(
int projectId,
String name,
String description,
int enabled,
String modifiedDate,
String createdDate
) {
String sql = "update projects set " +
"projectId = " + prepareParam(projectId) + ", " +
"name = " + prepareParam(name) + ", " +
"description = " + prepareParam(description) + ", " +
"enabled = " + prepareParam(enabled) + ", " +
"modifiedDate = " + prepareParam(modifiedDate) + ", " +
"createdDate = " + prepareParam(createdDate) +
" where projectId = " + prepareParam(projectId);
return executeSql(sql);
}
/**
* Delete a record in "projects"
*
* @return true if record deleted successfully, otherwise return false
* Generated by Lgc Auto code tool on [date]
*/
public boolean remove(int id) {
String sql = "delete from projects where projectId = " + id;
return executeSql(sql);
}
/**
* Create Project Objects from all records of "projects"
*
* @return Vector of Project Object
* Generated by Lgc Auto code tool on [date]
*/
public Vector load() {
String sql = "select * from projects";
return loadWithSql(sql);
}
/**
* Add additional query patterns
*
* @return Vector of Project Object
* Generated by Lgc Auto code tool on [date]
*/
/*
// Add additional query patterns
public Vector load(
int projectId,
String name,
String description,
int enabled,
String modifiedDate,
String createdDate
) {
String sql = "select * from projects where " +
"projectId = " + prepareParam(projectId) + " and " +
"name = " + prepareParam(name) + " and " +
"description = " + prepareParam(description) + " and " +
"enabled = " + prepareParam(enabled) + " and " +
"modifiedDate = " + prepareParam(modifiedDate) + " and " +
"createdDate = " + prepareParam(createdDate);
return loadWithSql(sql);
}
*/
/**
* Add additional relationship patterns
*
* @return Vector of Project Object
* Generated by Lgc Auto code tool on [date]
*/
/*
// Add additional relationship patterns
public Vector loadBy...(int id) {
String sql = "select * from projects inner join ... on ... where ...Id = " + id;
return loadWithSql(sql);
}
*/
}
 
活活,好家伙
慢慢学习先
 

Similar threads

I
回复
0
查看
607
import
I
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
624
import
I
I
回复
0
查看
494
import
I
顶部