zoukankan      html  css  js  c++  java
  • XML万能数据库设计

    XML万能数据库设计

     

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

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

    但是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_ANDROID
            DATAPATH = Application.persistentDataPath;
    
    #endif
            database = 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 initialization
        void Awake()
        {
            resData = Application.streamingAssetsPath + path;
    #if UNITY_ANDROID
            gameData = Application.persistentDataPath+path;
    
    #else
            gameData = Application.dataPath + path;
    #endif
            StartCoroutine(ReadData(gameData));
    
    
        }
        IEnumerator ReadData(string path)
        {
    
            if (!File.Exists(path))
            {
    #if UNITY_ANDROID
                WWW www = new WWW(resData);
                yield return www;
                if(www.isDone) {
                   
                    File.WriteAllBytes(path,www.bytes);
                }
    #else
                File.Copy(resData, path);
    #endif
                Application.LoadLevel(1);
            }
            else {
                Application.LoadLevel(1);
            }
            yield return 0;
    
        }
    
    
    }

     

  • 相关阅读:
    C语言寒假大作战01
    C语言I作业12—学期总结
    C语言I博客作业11
    C语言I博客作业10
    C语言I博客作业09
    C语言I博客作业08
    centos安装swoole
    Apache优化:修改最大并发连接数
    centos中安装、升级git
    memcached/memcache安装
  • 原文地址:https://www.cnblogs.com/jqg-aliang/p/4597135.html
Copyright © 2011-2022 走看看