基于 XML Schema 的数据存储方案

对于一些小型项目,需要存储的数据字段和数据量可能都比较小,为了降低项目成本,提高项目的独立性,开发人员希望能不依赖数据库进行开发。本文提供了一种替代数据库进行数据存储的解决方案,该方案是应用 XML Schema 技术加以实现,本文会对实现的方法和细节进行详细的讲解。

引言

对于一些小型的项目,需要存储的数据字段和数据量可能都比较小,为了降低项目成本,或提高项目的独立性,开发人员希望能不依赖数据库进行开发。此时,利用 XML Schema 进行数据存储便是一个非常好的解决方案。利用此方法可以直接把数据存储在 xml 文件中,然后把 xml 文件存放在磁盘的某个位置,这样会使得项目的部署与运行非常的方便。

本文会详细的介绍如何基于 xml schema 进行数据的存储,如何以面象对象的方式对 xml 文件进行操作,以提高项目的开发速度和准确度。 文中还提供了一些详细的代码示例来帮助读者了解开发的技术细节。

Schema 数据存储概述

利用 XML Schema 存储数据的原理就是将数据存储在 schema 所定义的 xml 文档中, 但本文中所讲述的实现方法不会直接面对 xml,而是通过一些类以面象对象的方式实现存取操作,这些类是利用开发工具自动的生成的。本文会以 WID (Websphere Integration Development) 为参考,详细介绍如何构建一个 XML Schema,如何根据 schema 生成 Java 类,以及如何应用这些类进行数据的存取操作。总体来说,应用 schema 进行数据存储大致分为以下几个步骤:

  1. 创建一个合适的 schema 文件,来满足数据的存储需求
  2. 根据 schema 生成 Java 类,应用这些类以面象对象的方式对 xml 文件进行操作
  3. 对 xml 文件的保存。

本文会对上述三个步骤来进行详细的讲解。

Schema 的构建原则

应用 schema 存储数据首先要创建一个 schema 文件,schema 中要包含所有需要存储的数据字段。那么在创建 schema 的时候要考虑这些字段的组织结构,要使得这些字段的结构更为合理,使得后续的操作更为方便。对数据字段应加以分类,然后针对每类数据创建一个结构化对象,就象在数据库中设计表一样,需要决定把哪些字段放在一个表中。在 schema 中,与数据库表相对应的对象是“ Type ”,每个“ Type ”中封装了一类数据字段。以一个简单的库存管理系统为例,为了实现这个系统,大概有五类数据需要存储:货物类别,每日售货记录,供应商信息,送货信息,退货信息。在设计 schema 的时候,要针对每类数据创建一种 Type,以更加清晰的管理每种数据。


Schema 的创建方法

Schema 的创建可以有多种方式,既可以用文本工具手工编写, 也可以借助开发工具更快捷的创建。 WID 当中有一个创建 schema 的功能,可以应用它很方便的创建一个 schema 。在 WID 菜单中选择 : new – > other,在出现的对话框中,选择 : XML – > XML Schema,根据图中所示的步骤可以创建一个 schema 文件。下图所示为一个已经创建完成的 schema 文件,该 schema 中包含了上文提到的库存管理系统的五类数据(本文后续部分会以此 schema 为例进行讲解)。


图 1. schema 编辑器
schema 编辑器

上图所示为 WID 中的 schema 编辑器,它有左右两个编辑域:Elements 和 Types 。

1. Types 域是用来定义结构化对象的,把一些数据字段封装在其中。在编辑域中点击右键可以选择增加或删除一个 Type 。双击一个已经创建的 Type 会进入到 Type 的编辑器来对 Type 进行编辑。如下图所示:


图 2. type 编辑器
图 2. type 编辑器

在此编辑器中可以为一个 Type 添加或删除字段,修改字段类型。字段类型可以是基本类型,如 string,date ;也可以是已经定义的其他 Type 类型,如 Category 对象中的 supplier 元素的类型就是一个已定义的 Type 类型。 Type 编辑器提供了一个 Properties 域,可以利用它对字段方便的设置多种属性。

在图 1 所示的 Type 中有一个叫 StorageInfo 的 Type,它与其他五个 Type 有所不同,其他五个 Type 是用来封装数据字段的结构化对象,而 StorageInfo 是用来封装这五个 Type 的,而且 StorageInfo 这个对象是必不可少的。在封装这五个 Type 的时候要结合实际情况对他们组织一个合适的结构。可能只是简单的把它们放在第一层,也可能按照引用关系来排列一个多层次的结构。下图所示为 StorageInfo 的组织结构,本例中是把五个 Type 放在了第一层:


图 3. 对象组织结构
图3. 对象组织结构

2.Elements 域是用来为 schema 定义一个根对象,其他的对象是通过这个根对象来获取的,但 Elements 域中的根对象只是一个简单简单的接口,需要为它指定一定 Types 域中的对象。图 1 中所示的 StorageInfo 对象便是供 Elements 域中的 StorageInfo 使用的。根对象的定义如下图所示:


图 4. 根对象组织结构
图4. 根对象组织结构 

Java 类的生成

为了实现以面象对象的方式操作 XML,需要根据 schema 来生成 Java 类。在 WID 中,根据 Schema 生成 Java 类非常简单,右健单击 schema 文件,在出现的菜单中选择 Generate -> Java,之后会出现一个对话窗口,如下图所示:


图 5. Java 类生成器
图5. Java类生成器

点击图中的 Next,会提示选择用来存放生成 Java 类的位置,之后点击 Finish,这样 Java 类便生成了。下图所示便为生成的所有的 Java 类。在 script. 目录下面的类,以及 impl 和 util 两个目录中包含了这些类,应用这些类可以轻松的操作 XML 文件。


图 6. Java 类结构
图 6. Java 类结构

由 WID 所生的类看起来似乎比较复杂,但它们有各自的用途,知道了它们的用途后便可以对它清晰的分类:

  1. Type 所对应的 Bean 类

    WID 会为图 1 中所示的每个 Type 定义一个相应的 JavaBean,例如:Catetory.java,DailyRecord.java,DeliverInfo.java 分别对应 schema 中的 Catetory,DailyRecord,DeliverInfo 。但这些类都只是接口,其中只有 getter,setter 方法的定义,并无具体实现。

  2. Bean 的实现类

    Impl 目录中放的是 Bean 的实现类,对每一个接口都会有一个具体的实现。

  3. DocumentRoot

    它表示 schema 的根对象,通过这个类可以得到 schema 中所有其它的对象,如 Category,DeliverInfo 等。

  4. ScriptFactory

    用来创建所有的 Bean 对象,如 createStorageInfo(),createDeliverInfo().

  5. Util 类

    Util 目录中有六个类,但只有其中的 ScriptResourceUtil.java 类会被经常应用,其他的五个类很少会用,所以不必太在意他们。 ScriptResourceUtil 是一个比较重要的类,会用它进行把 xml 文件加载到内存并转化成 Java 对象,同是也要用它把 Java 对象保存到 Xml 文件中。


应用讲解

1. 获取根对象

在本文开头已阐述过,被保存的数据最终是存放在一个 xml 文件中,而且对 xml 文件的操作可以应用面象对象的方式进行。要实现以面象对象的方式操作 xml,首先要对 xml 文件加载并加以转换,然后获取根对象,这样便可取得所有其他的对象,对它们进行修改、删除或创建。获取根对象的代码示例如下,附件中包含了完整的代码,可以下载以供参考:


清单 1. 获取文档根对象
				public StorageInfo loadStorageInfo() { 
 StorageInfo storageInfo = null; 
 try { 

 // 文件输入流指定到存储数据的 xml 文件
 InputStream is = 
  new FileInputStream ("D:/storageInfo/storageInfo.xml"); 

 // 利用 ScriptResourceUtil 来从输入流中加载 xml 文件,
 // 它会自动的 把 xml 文件转换为 DocumentRoot 对象 
 DocumentRoot root = 
  ScriptResourceUtil.getInstance().load(is); 

 // 从 DocumentRoot 中可以得到根对象,也就是 :StorageInfo 
 storageInfo = root.getStorageInfo(); 
 } catch (Exception e) { 
 e.printStackTrace(); 
 } 
 return storageInfo; 
 }

得到 StorageInfo 对象之后,便可以以面象对象的方式进行数据的添加,删除,修改等操作。

2. 数据的操作

得到了根对象之后便可以进行数据的增删改等操作。下面的示例讲解了具体的应用。

添加一个 Category 记录


清单 2. 增加对象
				public void addCatetory(StorageInfo storage) { 

 // 首先获取 ScriptFactory,它是用来生成各种对象的工厂类 
 ScriptFactory factory = ScriptFactory.eINSTANCE; 

 // 利用工厂类创建一个新的 Category 对象 
 Category category = factory.createCategory(); 
				 
 // 为对象中的字段赋值 
 category.setCostPrice(500); 
 category.setDiscountPrice(400); 
 category.setGoodsAmount(10000); 
 category.setName("Clothes"); 

 // 将新 Category 对象添加到根对象 StorageInfo 中 
 storage.getCategory().add(category); 

 }

修改对象

根据某个条件修改对象。例如想要修改名称为“ Clothes ”的一个 Category 记录,可以按如下示例进行操作:


清单 3. 修改对象
public void modifyCatetory(StorageInfo storage) { 

 // 得到所有的 Category 对象 
 List categories = storage.getCategory(); 

 // 应用循环得到指定的 Category 对象 
 for(int i=0; i

删除对象

根据某个条件删除一个对象。例如想要删除名称为“ Clothes ”的一个 Category 记录,可以按如下示例进行操作:


清单 4. 删除对象
public void deleteCatetory(StorageInfo storage) { 

 // 得到所有的 Category 对象 
 List categories = storage.getCategory(); 
 Category category = null; 

 // 应用循环得到指定的 Category 对象 
 for(int i=0; i

3. 对象存储

在对根对象 StorageInfo 对象操作完成后,需要对其进行持久化,以免数据的丢失。数据的持久化也就是将 StorageInfo 转换为 xml 并加以保存,这个步骤同样是应用 ScriptResourceUtil 这个类加以实现,见如下示例:


清单 5. 对象存储
public void saveStorageInfo(StorageInfo storageInfo) { 
 try { 
 
 // 创建一个 DocumentRoot 对象 
 DocumentRoot docRoot = ScriptFactory.eINSTANCE.createDocumentRoot(); 
				 
 // 将需要保存的 StorageInfo 设定在 DocumentRoot 中 
 docRoot.setStorageInfo(storageInfo); 

 // 定义一个输出流 ,ScriptResourceUtil 会把 StorageInfo 写到输出 流中 

 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
 ScriptResourceUtil.getInstance().save(docRoot,baos); 

 // 通过 FileOutputStream 把 StorageInfo 写到 xml 文件中 
 FileOutputStream fos = new FileOutputStream( "D:/storageInfo/storageInfo.xml"); 
 fos.write(baos.toByteArray()); 
 } catch (Exception e) { 
 e.printStackTrace(); 
  } 
  }

XML 文件的加密

前面的示例中是把数据的内容直接存放到了 xml 文件中,这个文件是可读的,如果这不慎外泄可能会造成机密数据的泄露,可能会给公司造成一定的损失。所以避免这种情况,需要把 xml 文件进行加密,别人即使得到了 xml 文件,也无法理解其中的内容。在加密 xml 文件时,可以采用 base64 的方式,如下示例是经过修改的代码,它在保存和加载时都是针对一个已加密的 xml 文件。

1. 从加密的 Xml 文件获取根对象

因为 xml 文件在保存的时候已经应用 Base64 对其进行了加密,所以在加载文件的时候需要对其进行解密。


清单 6. 从加密的 XML 文件获取根对象
				public static StorageInfo loadStorageInfo() { 
 StorageInfo storageInfo = null; 
 try { 
 InputStream is = new FileInputStream("D:\\storageInfo\\storageInfo.xml"); 

 // 应用 Base64 对加密过的内容进行解密 
 byte[] contents = new BASE64Decoder().decodeBuffer(is); 
 ByteArrayInputStream bais = new ByteArrayInputStream(contents); 
 DocumentRoot docRoot = ScriptResourceUtil.getInstance().load(bais); 
 storageInfo = docRoot.getStorageInfo(); 

 } catch (Exception e) { 
 e.printStackTrace(); 
 } 
 return storageInfo; 
 
 }

2. 将对象保存到加密的 XML 文件中

保存的时候用 Base64 对内容进行加密


清单 7. 将对象保存在加密的 XML 文件中
public static void saveStorageInfo(StorageInfo storageInfo) { 
 try { 
 DocumentRoot docRoot = ScriptFactory.eINSTANCE.createDocumentRoot(); 
 docRoot.setStorageInfo(storageInfo); 
 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
 ScriptResourceUtil.getInstance().save(docRoot,baos); 
 
 // 应用 Base64 对内容进行加密 
 byte[] encryptContents = new 
   BASE64Encoder().encode(baos.toByteArray()).getBytes(); 
 FileOutputStream fos = new FileOutputStream( "D:/storageInfo/storageInfo.xml"); 
 fos.write(encryptContents); 

 } catch (Exception e) { 
 e.printStackTrace(); 
 
 } 
 }

历史数据的归档

应用程序在运行一段时间后,保存的数据会越来越多,保存数据的 xml 文件也会随之越来越大。如果 xml 文件过大会影响程序的执行效率,延长程序的响应时间,因此每隔一定的时间应该对一些历史数据进行归档。比如本文中的“每日售货记录”。这个记录每天都会产生几百甚至几千条数据,那么隔一段时间应该对这些数据进行归档。对这些数据进行归档比较简单,通过写一段代码便可以实现。如下是代码示例:


清单 8. 归档历史数据
public void archiveHistoryData(StorageInfo storageInfo){ 
 try { List historyData = new ArrayList(); 

 // 根据销售时间找到需要归档的 DailyRecord 对象 
 List dailyRecords = storageInfo.getDailyRecord(); 
 for(int i=0; i

注意事项

XML 文件的存储位置不是一程不变的,因此在程序开发时,不要把 xml 文件存储路径直接写在代码中,而是应该把它放到配置文件中,这样当变更 xml 存储位置时,只需要改变一下配置文件中的路径就可以了,不必修改代码。

在设计 schema 时应尽量保持 schema 的简结,不要设计太深的层次,否则在操作时会带来不必要的麻烦。

应该定期做一下历史数据的归档,以减小 xml 文件大小,来提高程序的执行效率。

结束语

在一些情况下适当利用 schema 存储方案不但能使项目更加独立,不必依赖数据库的支持,也可以提高程序的执行效率。 虽然利用 XML 存储数据并不是一个新颖的方法,但是本文中所讲的内容会让读者了解到一个新的 xml 存储数据的实现方法。



来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14751907/viewspace-580641/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/14751907/viewspace-580641/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值