XML万能数据库设计

使用unity开发存取本地数据一般用xml,来实现跨平台的数据存取。为什么不用sqlite我就不解释了,谁用谁知道。

好进入正题,如果你了解hibernate,应该知道他是针对model层数据持久化操作的利器。什么意思呢,也就是说任意对象的增删改查它都帮你做了,你需要做的就是配置一下即可。使用时直接调用提供的接口。Java,C#都有这样的利器。

但是hibernate一般用于web应用,需要处理大量的实体类,虽然用unity开发的游戏也需要对一些类进行持久化操作,但是用hibernate还是太专业了,大材小用不说,好像并不怎么适用。配置什么的应该很烦。但是还想一劳永逸,只写一个操作适用所有对象的增删改查怎么办?自己动手丰衣足食。

首先要知道hibernate是怎么实现的类的自动拆装的,反射。OK,接下来就简单了,知道了方案,接下来就是具体的实现了。楼主看了下C#的反射,没想到实在是太好用了!

好,整理一下基本思路,类的名称作为表名,类的成员变量作为列名,成员变量的值作为值,进行存取。等等,xml又不是sql,这表,列,值代表什么意思呢?如果你学过xml的话,应该知道根,元素。(小白先去学基础知识吧),所以我们存取对象就是把这个对象的名称作为根,字段作为子根进行存取。

以上是xml实现基础,如何做到万能,还需要结合反射,通过反射可以获取的信息:类的名称,类的成员变量,值,等等。所以,存取对象的时候,先将这个类进行解析,获取类名,他的成员变量,以及值,存起来,然后插入的时候,以类名为根,成员变量作为元素,值作为元素的值插入。因为xml存的是字符,所以类型在存的时候都转成字符串。取的时候再将字符串转成相应的类型,封装成对象即可。

不知道听不听的懂,代码是最通俗的,好,进入正题。

首先,你要处理的是泛型对象,这样在操作的时候不需要强转。先写个接口声明一下。插入的时候直接传对象,查找的时候也传对象(默认按ID查找,需要在代码中对ID赋值),更新有两种,一是更新指定的列,二是更新对象的所有列。删除直接传对象。

using System.Collections.Generic;
using System;
public interface DataBase {void Insert<T>(T t);T Select<T>(T t);List<T> SelectAll<T>(T t);void Update<T>(T t,string key);void Update<T>(T t);void Delete<T>(T t);void CreateData(string path);
}

然后,写个静态的类,使用这个万能xml,代码也很简单,如果有疑惑,我稍后解答,例如为什么写成静态的?

using System.Collections.Generic;
using System;
using UnityEngine;
public class MyDataBase
{private static DataBase database;public static string RESDATAPATH = Application.streamingAssetsPath;//源数据目录private static string DATAPATH = Application.dataPath;//数据目录static MyDataBase() {#if UNITY_ANDROIDDATAPATH = Application.persistentDataPath;#endifdatabase = new XmlDataBase(DATAPATH);}public static void Insert<T>(T t){database.Insert(t);}public static T Select<T>(T t){return database.Select(t);}public static List<T> SelectAll<T>(T t){return database.SelectAll<T>(t);}public static void Update<T>(T t, string key){database.Update(t, key);}public static void Update<T>(T t){database.Update(t);}public static void Delete<T>(T t){database.Delete(t);}}

  好,下面就是xml的存取操作了,不怎么难,关键是一些方法的运用。

using UnityEngine;
using System.Xml;
using System.Collections.Generic;
using System.IO;
using System;
public class XmlDataBase : DataBase
{private string path = "/DataBase/GameData.xml";private  string Myroot = "MyData";private  XmlDocument xmlDoc = new XmlDocument();public static string ObjectID="ID";public static string PATH;private void ReadFile(string path) {PATH = path + this.path;if (!File.Exists(PATH))//如果指定的路径不存在
        {if (!File.Exists(MyDataBase.RESDATAPATH + this.path))//如果不存在源文件
            {Directory.CreateDirectory(MyDataBase.RESDATAPATH + "/DataBase");File.CreateText(PATH);CreateData((MyDataBase.RESDATAPATH + this.path));Application.Quit();}else {Directory.CreateDirectory(path + "/DataBase");File.CreateText(PATH);File.Copy(MyDataBase.RESDATAPATH + this.path, PATH);File.Delete(MyDataBase.RESDATAPATH + this.path);}}else{xmlDoc.Load(PATH);}}public XmlDataBase(){//这是一个XML数据库,基于XML的对象的存取,改查操作。//注意,数据对象的ID需要以字母开头,且不能有特殊字符。//TextAsset textAsset = (TextAsset)Resources.Load(file, typeof(TextAsset));
ReadFile(path);//读取默认路径
    }public XmlDataBase(string path){ReadFile(path);//指定读取默认路径
    }public void CreateData(string path){XmlDocument xmlDoc = new XmlDocument();XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);XmlNode root = xmlDoc.CreateElement(Myroot);xmlDoc.AppendChild(xmlDeclaration);xmlDoc.AppendChild(root);xmlDoc.Save(path);}public void Insert<T>(T obj){//插入一个对象//对象有字段,属性值//利用数据的特点,即名称不同,优化查询速度,具体的做法为,将属性值作为子根操作。
ObjectPara<T> OP = new ObjectPara<T>(obj);//
XmlNode root = xmlDoc.SelectSingleNode(Myroot);//查找<game data>
XmlNode Myroots = root.SelectSingleNode(OP.ObjName);//查询对象表if (Myroots == null) {Myroots = xmlDoc.CreateElement(OP.ObjName);root.AppendChild(Myroots);}//以对象表为根,创建一个以ID为根的子根XmlNode node = Myroots.SelectSingleNode(OP.GetKeyValue(ObjectID).ToString());if (node != null) {//如果待插入的对象已经存在,则将此数据删除后,再重新插入Delete<T>(obj);Insert<T>(obj);return;}XmlElement xe1 = xmlDoc.CreateElement(OP.GetKeyValue(ObjectID).ToString());//创建一个<data>节点for (int i = 0; i < OP.cols.Length; i++){XmlElement xe = xmlDoc.CreateElement(OP.cols[i]);xe.InnerText = OP.values[i];xe1.AppendChild(xe);}if (Myroots == null){Myroots = xmlDoc.CreateElement(OP.ObjName);root.AppendChild(Myroots);}else{Myroots.AppendChild(xe1);}//添加到<bookstore>节点中
        xmlDoc.Save(PATH);OP = null;}public List<T> SelectAll<T>(T obj){List<T> list = new List<T>();ObjectPara<T> OP = new ObjectPara<T>(obj);XmlNode root = xmlDoc.SelectSingleNode(Myroot);//查找<game data>
XmlNode Myroots = root.SelectSingleNode(OP.ObjName);//查询对象表
XmlNodeList nodes = Myroots.ChildNodes;//获取对象的所有子对象foreach (XmlNode node in nodes)//
        {XmlNodeList lis = node.ChildNodes;//获取每个对象的字段T o = obj;int count = lis.Count;string[] values = new string[count];for (int i = 0; i < lis.Count; i++) {values[i] = lis.Item(i).InnerText.Trim();}o = OP.GetObject(values);list.Add(o);}return list;}public T Select<T>(T obj){ObjectPara<T> OP = new ObjectPara<T>(obj);XmlNode root = xmlDoc.SelectSingleNode(Myroot);//查找<game data>
XmlNode Myroots = root.SelectSingleNode(OP.ObjName);//查询对象表
XmlNode node = Myroots.SelectSingleNode(OP.GetKeyValue(ObjectID).ToString());//获取指定列为根的根元素if (node != null){XmlNodeList list = node.ChildNodes;//获取根的所有字段int count = list.Count;string[] values = new string[count];for (int i = 0; i < list.Count; i++){//成员变量//将遍历出的值赋予数组
values[i] = list.Item(i).InnerText.Trim();}obj = OP.GetObject(values);}else {obj = default(T);MyTool.P(213);} return obj;}public void Update<T>(T obj){ObjectPara<T> OP = new ObjectPara<T>(obj);//
XmlNode root = xmlDoc.SelectSingleNode(Myroot);//查找<game data>
XmlNode Myroots = root.SelectSingleNode(OP.ObjName);//查询对象表
XmlNode node = Myroots.SelectSingleNode(OP.GetKeyValue(ObjectID).ToString());if (node != null){XmlNodeList xnl = node.ChildNodes;for (int i = 0; i < xnl.Count;i++ ){//成员变量//将遍历出的值赋予数组
xnl.Item(i).InnerText = OP.values[i];}xmlDoc.Save(PATH);}}public void Update<T>(T obj, string key){//插入一个对象//对象有字段,属性值//利用数据的特点,即名称不同,优化查询速度,具体的做法为,将属性值作为子根操作。
ObjectPara<T> OP = new ObjectPara<T>(obj);//
XmlNode root = xmlDoc.SelectSingleNode(Myroot);//查找<game data>
XmlNode Myroots = root.SelectSingleNode(OP.ObjName);//查询对象表
XmlNode node = Myroots.SelectSingleNode(OP.GetKeyValue(ObjectID).ToString());if (node != null){//找到名为name的对象XmlNode xl = node.SelectSingleNode(key);//找到字段xl.InnerText = OP.GetKeyValue(key).ToString();xmlDoc.Save(PATH);}}public void Delete<T>(T obj){ObjectPara<T> OP = new ObjectPara<T>(obj);XmlNode root = xmlDoc.SelectSingleNode(Myroot);//查找<game data>
XmlNode Myroots = root.SelectSingleNode(OP.ObjName);//查询对象表
XmlNode node = Myroots.SelectSingleNode(OP.GetKeyValue(ObjectID).ToString());if (node != null){node.RemoveAll();Myroots.RemoveChild(node);xmlDoc.Save(PATH);}}}

因为xml的结构我们要动态生成的,不需要专门针对某一个类写操作语句。如何动态就靠解析类完成了。

好,下面是这个解析类,也就是用反射,本身并不难,关键是如何运用。特别声明的是,因为我操作的数据包含数组,而且数组只用到了字符数组,整型数组,以及浮点数组,所以只写了这三个数组的解析。其他类型数组你看需求自己定义:

using System;
using System.Reflection;
using UnityEngine;
using System.Collections.Generic;
using System.Runtime.InteropServices;
public class ObjectPara<T>
{public string ObjName;//待解析的类的名称public string[] cols;//类的成员变量public string[] values;//成员变量对应的值public PropertyInfo[] info;//反射获取类的属性public Type objtype;//待解析类型public T obj;//泛型对象public T GetObject(string[] values){T _obj = obj;for (int i = 0; i < info.Length; i++){if (objtype.GetProperty(cols[i]).PropertyType.IsArray){Type type = objtype.GetProperty(cols[i]).PropertyType;string[] vale = values[i].Split(new char[] { ',' });if (type == typeof(string[])){info[i].SetValue(_obj, vale, null);}else if (type == typeof(int[])){int[] va = new int[vale.Length];for (int j = 0; j < va.Length; j++){va[j] = int.Parse(vale[j]);}info[i].SetValue(_obj, va, null);}else if (type == typeof(float[])){float[] va = new float[vale.Length];for (int j = 0; j < va.Length; j++){va[j] = float.Parse(vale[j]);}info[i].SetValue(_obj, va, null);}//MyTool.P(values[i]);
            }else{info[i].SetValue(_obj, Convert.ChangeType(values[i], info[i].PropertyType), null);}}return _obj;}public ObjectPara(){}public ObjectPara(T obj){this.obj = obj;AnalyzeObject();}public void AnalyzeObject()//解析对象
    {//获取对象的名称,字段,以及值,以字符串数组的形式存储objtype = obj.GetType();ObjName = objtype.Name;info = objtype.GetProperties();cols = new string[info.Length];values = new string[info.Length];for (int i = 0; i < info.Length; i++){cols[i] = info[i].Name;string value = "";if (objtype.GetProperty(cols[i]).PropertyType.IsArray){if (objtype.GetProperty(cols[i]).GetValue(obj, null) != null){Type type = objtype.GetProperty(cols[i]).PropertyType;if (type == typeof(string[])){MyTool.P(value);string[] ob = objtype.GetProperty(cols[i]).GetValue(obj, null) as string[];if (ob != null){foreach (var o in ob){value += o + ",";}//value = value.Substring(0, value.Length-2);value = value.TrimEnd(new char[] { ',' });}}else if (type == typeof(int[])){int[] ob = objtype.GetProperty(cols[i]).GetValue(obj, null) as int[];if (ob != null){foreach (var o in ob){value += o + ",";}//value = value.Substring(0, value.Length-2);value = value.TrimEnd(new char[] { ',' });}}else if (type == typeof(float[])){float[] ob = objtype.GetProperty(cols[i]).GetValue(obj, null) as float[];if (ob != null){foreach (var o in ob){value += o + ",";}//value = value.Substring(0, value.Length-2);value = value.TrimEnd(new char[] { ',' });}}}}else{value = Convert.ToString(objtype.GetProperty(cols[i]).GetValue(obj, null));}//MyTool.P(value);values[i] = value;}}public System.Object GetKeyValue(string col){return (objtype.GetProperty(col).GetValue(obj, null));}
}

好了,以上就是设计万能xml的部分,如何使用呢?

很简单,你先写一个类,这个类很简单,或者说为了简单,只有一个成员变量。这样可以统一对所有对象进行操作,

public class GameData  {private string iD;public string ID{get { return iD; }set { iD = value; }}
}

然后随便写一个实体类,例如Person

public class MyPerson :GameData{private string name;public string Name{get { return name; }set { name = value; }}private int sort;public int Sort{get { return sort; }set { sort = value; }}private float life;public float Life{get { return life; }set { life = value; }}
}

接下来就是一个简单的测试类了,我就不传代码了。各位自己测试:

MyPerson p = new Myperson();

p.ID="lihua";

……

MyDataBase.Insert(p);添加

MyDataBase.Delete(p);删除

……

注意一下的问题,不要以为复制完代码就可以运行,主要是目录路径问题,如果你实在搞不定,直接先在Assent目录下建立一个路径DataBase,然后再建一个GameData.xml。好,接下来看你发挥。

后记:路径弄得有问题,建议不要使用上面提到的检测数据文件的方法,可以在进入游戏时检测并初始化数据库。相关的代码也很简单,主要是使用平台判断,www类,以及协程的使用。

using UnityEngine;
using System.Collections;
using System.IO;
public class CheckData : MonoBehaviour
{public string resData;public string gameData;public string path = "/GameData.xml";// Use this for initializationvoid Awake(){resData = Application.streamingAssetsPath + path;
#if UNITY_ANDROIDgameData = Application.persistentDataPath+path;#elsegameData = Application.dataPath + path;
#endifStartCoroutine(ReadData(gameData));}IEnumerator ReadData(string path){if (!File.Exists(path)){
#if UNITY_ANDROIDWWW www = new WWW(resData);yield return www;if(www.isDone) {File.WriteAllBytes(path,www.bytes);}
#elseFile.Copy(resData, path);
#endifApplication.LoadLevel(1);}else {Application.LoadLevel(1);}yield return 0;}}

转载于:https://www.cnblogs.com/jqg-aliang/p/4597135.html

XML万能数据库设计相关推荐

  1. MySQL查询与数据库设计 #万能公式 #思维导图 #MySQL查询 #数据库设计规范 #第二部分

    MySQL查询 零.MySQL查询万能公式 一.查询 1)查询表中记录 2)给表取名 3)去重(distinct) 二.条件查询 1)比较运算符 2)逻辑运算符 3)模糊查询 like(用通配符) r ...

  2. 基于单个xml的数据库

    用户觉得怎么方便,我们就怎么弄 1.好处,无需另外安装数据库引擎, 数据库引擎和应用程序整合. 单文件修改容易,用记事本打开即可修改 查找容易,用windows 查找 2.应用程序前台直接加载xml文 ...

  3. 数据库设计笔记——概述(一)

    数据库 数据库(DataBase,DB)是长期存储在计算机内,有组织的,可共享的数据集合.是存放数据的仓库,并且有组织,有管理. 数据库管理系统 数据库管理系统(Database Management ...

  4. 数据库设计方法、规范与技巧

    本文链接: http://www.openphp.cn/index.php/art.../100/index.html 一.数据库设计过程 数据库技术是信息资源管理最有效的手段.数据库设计是指对于一个 ...

  5. 数据库设计指南【转】

    Page 1 © CNET Networks Inc. 2002 www.zdnet.com.cn/developer 数据库设计指南 如果把企业的数据比做生命所必需的血液,那么数据库的设计就是应用中 ...

  6. sql取系统时间减一小时_Java秒杀系统实战系列-整体业务流程介绍与数据库设计...

    本篇文章是"Java秒杀系统实战系列文章"的第三篇,本文将主要介绍秒杀系统的整体业务流程,并根据相应的业务流程进行数据库设计,最终采用Mybatis逆向工程生成相应的实体类Enti ...

  7. 自己开发的“万能数据库查询分析器”终于有了较大的成果

    经历4年的研究.开发.测试,个人创作了"万能数据库查询分析器",中文版本<DB 查询分析器>,英文版本<DB Query Analyzer>.并在2006年 ...

  8. 巨高兴,自己的“万能数据库查询分析器”中英文 3.01版本 已经在国内6大软件下载网站发布

    几个月前开始构思,寻思着从"万能数据库查询分析器"中英文 3.01 版本开始起,一方面,去除原来试用31天的时间限制,用户永久试用:另一方面,尝试提供免费注册的方式,只要用户协助完 ...

  9. ssm+bootsrap人力资源考勤系统-JAVA【数据库设计、源码、开题报告】

    摘要 计算机信息技术的快速发展使得企业的传统办公方式不能够在满足我们实际工作中的各种需要,于是企业办公自动化应运而生.办公自动化主要是利用计算机网络技术,使用各种设备结合软件系统来协助完成各种工作任务 ...

最新文章

  1. Spring集成spymemcached
  2. 胆固醇竟是新冠最隐蔽同伙!协助病毒入侵细胞,普林斯顿、哈佛医学院首次观察到全过程...
  3. Linux 下文件打包和压缩
  4. Java内存模型深度解析:final--转
  5. HTML制作搞笑照片,40张创意搞笑的照片PS效果
  6. Java黑皮书课后题第7章:*7.12(倒置数组)7.7节中的reverse方法通过复制到新数组实现倒置。改写方法将参数中传递的数组倒置,并返回该数组。编写一个测试程序,输入10个数字,倒置它们并显示
  7. 项目中使用EasyPOI完成监控设备历史数据的导出
  8. Cisco 3550-EMI 交 换 机 配 置 教 程
  9. 81. 搜索旋转排序数组 II(014)二分查找+思路+详解+二种做法
  10. 如何利用脚本方法清除文本框中汉字,从而保留英文字母和数字等
  11. 鱼c工作室小甲鱼的水平_历历万乡 | 得两代帝王喜爱,又上过国际舞台!深冬就去浙个“年鱼福气”加持的小村子吧!...
  12. 计算机网络中TCP连接管理,计算机网络 TCP协议
  13. asm数据文件迁移(os–asm)
  14. Pandavan LED Document
  15. 贴片铝电解电容封装的说明
  16. 设计模式之路 | 外观模式
  17. JetPack之DataStore源码笔记
  18. 离散信号(八)| 离散傅里叶变换DFT性质(圆周移位、圆周卷积)
  19. Big Mart Sales prediction 商场销售预测分析项目
  20. SDN跟网络虚拟化的完美结合

热门文章

  1. 这就是数据分析之数据分析入门
  2. 星露谷物语json_《星露谷物语》1.5版本更新细节公开 已上线PC
  3. bee 字符串转int_beego中gbk和utf8编码转换问题
  4. Wireshark使用入门
  5. 软考网络管理员学习笔记1之第一章计算机硬件基础
  6. Android屏幕大小和密度对照表,以及px、dip、sp等像素单位的解释
  7. a*算法matlab代码_MATLAB数学建模算法的代码模板大全
  8. Cookie、token、session的区别是什么?
  9. 随心测试_软测基础_004职责与质量
  10. System.gc()与Object.finalize()的区别