Sqlitekit 封装管理
最近需要用到Sqlite数据库来做一个游戏的数据存储。网上搜了一下,两种方法,一种是自己dll搭建环境有可能还需要编译之类的,我自己是搭建出来了,不过我没采用。
还有一种就是使用sqlitekit插件,他本身是用c#编写的sqlite封装了一套简单的API,供别人使用,而且也没有那些有的没的环境问题。
一、插件快速介绍
其实插件使用起来很简单,导入之后,看那几个Demo就可以了。支持全平台,并且支持内存数据库,加密之类等等的特性。这些就不一一介绍了。
核心API如下
void Test( SQLiteDB db, ref string log ) { SQLiteQuery qr; // // delete table if exists // qr = new SQLiteQuery(db, queryDelete); qr.Step(); qr.Release(); log += "\nTable deleted."; // // create table // qr = new SQLiteQuery(db, queryCreate); qr.Step(); qr.Release(); log += "\nTable created."; // // insert string and blob // qr = new SQLiteQuery(db, queryInsert); qr.Bind(testString); qr.Bind(testBlob); qr.Step(); qr.Release(); log += "\nInsert test string and blob."; // // read strings // string testStringFromSelect = ""; qr = new SQLiteQuery(db, querySelect); while( qr.Step() ) { testStringFromSelect = qr.GetString("str_field"); if( testStringFromSelect != testString ) { throw new Exception( "Test string are not equal!" ); } byte[] testBlobFromSelect = qr.GetBlob("blob_field"); if( testBlobFromSelect.Length != testBlob.Length ) { throw new Exception( "Test blobs are not equal!" ); } for (int i = 0; i < testBlobFromSelect.Length; i++) { if( testBlobFromSelect[i] != testBlob[i] ) { throw new Exception( "Test blobs are not equal!" ); } } } if( testStringFromSelect == "" ) { throw new Exception( "Unknowm problem!" ); } qr.Release(); log += "\nRead and test strings and blobs."; // // // delete table // qr = new SQLiteQuery(db, queryDelete); qr.Step(); qr.Release(); log += "\nTable deleted."; // // if we reach that point it's mean we pass the test! db.Close(); log += "\nDatabase closed!\nTest succeeded!"; }
二、进一步封装
尽管上述的API已经很强大了,但是我们还需要再进一步。
我们经常需要对数据的操作无非就是增删改查,最麻烦是往往就是要根据数据库的表结构生成对应的类在内存中使用,或者内存中的类转化成数据库语言对数据库操作。
这期间往往会造成大量的代码重复。
这里举一个简单的例子
public class MyFirstTable { public int id; public string b; public string c; }
如果我们要把表与数据库交互,保守估计就要四条,增删改查
CREATE TABLE IF NOT EXISTS MyFirstTable (id INTEGER PRIMARY KEY, b TEXT, c TEXT);
INSERT INOT MyFirstTable(id,b,c) VALUS(1,'aa','b');
UPDATE MyFirstTable Set b ='cc',d='dd' WHERE id =1;
……………………………… and so on
这些放在代码中实在是太累述了,如果类一多,写这些就是痛苦的事
这个时候我们可以考虑到了,既然这些语句其实就是那些变量就是内存中的类的变量,我们就在进一步,使用反射来根据类来动态生成数据库语言,从而动态生成Table, 进而操作表的数据。
这样一来,不仅节省了我们大量的时间,而且这些表都是一来内存中的类的,有时候如果我们需要增加几个字段或者减少几个字段,那么我们就不用在数据库中改完再回到代码中改这些类的结构,我们只需要改一下类的结构,然后新的表就出来了。
说干就干,时间很晚了,我讲完思路就直接上代码了,太晚了已经。
核心类
//主要是类反射出来的字段进行一些处理方便形成数据库语言 public class Column { public Type ColumnType; public string TableType { get { if (IsPrimaryKey) return "INTEGER PRIMARY KEY"; else return To_TableType(ColumnType); } } // public bool IsPrimaryKey { get { return ColumnName.ToLower().StartsWith("id") && ColumnType == typeof(int); } } public string To_TableType(Type type) { if (type == typeof(int)) return ColType.INTEGER.ToString(); if (type == typeof(string)) return ColType.TEXT.ToString(); throw new Exception("Wrong Type"); } public string ColumnName; //Column Value MyProperty Property; public object ColumnValue { get { if (Property == null) return null; if (ColumnType == typeof(string)) { return ((MyProperty<string>)(Property)).GetValue(); } else if (ColumnType == typeof(String)) { return ((MyProperty<String>)(Property)).GetValue(); } else if (ColumnType == typeof(int)) { return ((MyProperty<int>)(Property)).GetValue(); } //else if (ColumnType == typeof(Enum)) //{ // return ((MyProperty<int>)(Property)).GetValue(); //} else { return null; } } } public Column(Type ColumnType, string ColumnName, object value) { this.ColumnType = ColumnType; this.ColumnName = ColumnName; if (ColumnType == typeof(string)) { string v = Convert.ToString(value); Property = new MyProperty<string>(); ((MyProperty<string>)(Property)).SetValue(v); } else if (ColumnType == typeof(String)) { String v = Convert.ToString(value); Property = new MyProperty<String>(); ((MyProperty<String>)(Property)).SetValue(v); } else if (ColumnType == typeof(int)) { int v = Convert.ToInt32(value); Property = new MyProperty<int>(); ((MyProperty<int>)(Property)).SetValue(v); } //else if (ColumnType == typeof(Enum)) //{ // int v = Convert.ToInt32(value); // Property = new MyProperty<int>(); // ((MyProperty<int>)(Property)).SetValue(v); //} else { Debuger.LogError("Column Construct Not Right Type :" + ColumnType.ToString()); } } } public class MyProperty { } public class MyProperty<T> : MyProperty { public MyProperty() { #if UNITY_FLASH _isValue = false; #else _isValue = typeof(T).IsValueType; #endif } public bool IsOfType(System.Type t) { return t == typeof(T); } public MyProperty(T value) : this() { _value = value; } public T GetValue() { return _value; } protected virtual bool IsValueDifferent(T value) { return !_value.Equals(value); } private bool IsClassDifferent(T value) { return !_value.Equals(value); } public virtual void SetValue(T value) { if (_changing) return; _changing = true; bool changed; if (_isValue) { changed = IsValueDifferent(value); } else { // Value types are handled differently via cached typeof(T).IsValueType checkup // ReSharper disable CompareNonConstrainedGenericWithNull changed = (value == null && _value != null) || (value != null && _value == null) || (_value != null && IsClassDifferent(value)); // ReSharper restore CompareNonConstrainedGenericWithNull } if (changed) { _value = value; //OnValueChanged(); } _changing = false; } private bool _changing; private T _value; private readonly bool _isValue; }
有了这个核心类,我们就可以直接操作了,还是用之前的那个类,
//数据库字段类型 public enum ColType { INTEGER, TEXT, REAL, BLOB } public class DBUtil<T> { public static Column[] GetColumnsByType(T arg) { FieldInfo[] fields = arg.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); int field_len = fields.Length; if (fields == null || field_len < 1) { return null; } Column[] tableType = new Column[field_len]; for (int i = 0; i < field_len; i++) { string Cur_Name = fields[i].Name; Type Cur_Type = fields[i].FieldType; object Value = fields[i].GetValue(arg); tableType[i] = new Column(Cur_Type, Cur_Name, Value); //Debuger.Log("name : " + tableType[i].ColumnName + " type :" + tableType[i].TableType); } return tableType; } public static void CreateTable(ref SQLiteDB db, T arg) { SQLiteQuery qr; Type type = arg.GetType(); string TableName = type.Name; string queryDelete = "DROP TABLE IF EXISTS " + TableName + ";"; qr = new SQLiteQuery(db, queryDelete); qr.Step(); qr.Release(); Column[] Columns = GetColumnsByType(arg); if (Columns == null || Columns.Length < 1) { Debug.LogError("Type is Wrong"); return; } Debug.Log("NowCreate:" + TableName); //CREATE TABLE IF NOT EXISTS test_values (id INTEGER PRIMARY KEY, str_field TEXT, blob_field BLOB); string queryCreate = queryCreateCommond(TableName, Columns); qr = new SQLiteQuery(db, queryCreate); qr.Step(); qr.Release(); } public static string queryCreateCommond(string TableName, Column[] Columns) { string queryCreate = "CREATE TABLE IF NOT EXISTS " + TableName + "(" + Columns[0].ColumnName + " " + Columns[0].TableType; for (var i = 1; i < Columns.Length; i++) { queryCreate += ", " + Columns[i].ColumnName + " " + Columns[i].TableType; } queryCreate += ");"; return queryCreate; } public static void ArrayInsertTable(ref SQLiteDB db, T[] Records) { for (int i = 0; i < Records.Length; i++) { InsertTable(ref db, Records[i]); } } //注意,主键null自动增加 否则就是要自己控制生成 //自己选择 我这里是自己控制生成的 public static void InsertTable(ref SQLiteDB db, T Record) { SQLiteQuery qr; Type type = Record.GetType(); string TableName = type.Name; Column[] Columns = GetColumnsByType(Record); if (Columns == null || Columns.Length < 1) { Debug.LogError("Type is Wrong"); return; } Debug.Log("Now Insert:" + TableName); //"INSERT INTO test_values (str_field,blob_field) VALUES(?,?);"; string queryInsert = queryInsertCommand(TableName, Columns); qr = new SQLiteQuery(db, queryInsert); foreach (var item in Columns) { //注意,主键null的情况 是可以的 这样一来 就自动增加了 主键 //if (item.IsPrimaryKey) // qr.BindNull(); //else qr.Bind(item.ColumnValue.ToString()); } qr.Step(); qr.Release(); } public static string queryInsertCommand(string TableName, Column[] column) { int RowLength = column.Length; string queryInsert = "INSERT INTO " + TableName + "(" + column[0].ColumnName; for (int i = 1; i < RowLength; i++) { queryInsert += "," + column[i].ColumnName; } queryInsert += ") VALUES (?"; for (int i = 1; i < RowLength; i++) { queryInsert += ",?"; } queryInsert += ");"; return queryInsert; } //目前 根据Id 索引更新 不够完善 public static void UpdateTableAccordingId(ref SQLiteDB db, T arg) { SQLiteQuery qr; Type type = arg.GetType(); string TableName = type.Name; Column[] Columns = GetColumnsByType(arg); if (Columns == null || Columns.Length < 1) { Debug.LogError("Type is Wrong"); return; } Column key = null; Column[] normal = null; Debug.Log("Now Insert:" + TableName); //"UPDATE MyFirstTable SET b = 'bbbb' ,c = 'bbbb' WHERE id = 1;" 如果使用通配符不用加'' string queryUpdate = queryUpdateCommand(TableName, Columns, ref normal, ref key); //Debuger.Log(queryUpdate); qr = new SQLiteQuery(db, queryUpdate); foreach (var item in normal) { qr.Bind(item.ColumnValue.ToString()); } qr.Bind(key.ColumnValue.ToString()); qr.Step(); qr.Release(); } public static string queryUpdateCommand(string TableName, Column[] Columns, ref Column[] normal, ref Column key) { int RowLength = Columns.Length; normal = GetNormalColumn(Columns); //"UPDATE test_tbl set b="****",c ="*****" where id=1;" string queryUpdate = "UPDATE " + TableName + " SET " + normal[0].ColumnName + " = ? "; for (int i = 1; i < normal.Length; i++) { queryUpdate += "," + normal[i].ColumnName + " = ? "; } key = GetPrimaryColumn(Columns); queryUpdate += "WHERE " + key.ColumnName + " = ? ;"; return queryUpdate; } //得到主键字段 static Column GetPrimaryColumn(Column[] Columns) { foreach (var item in Columns) { if (item.IsPrimaryKey) return item; } return null; } //普通字段 static Column[] GetNormalColumn(Column[] Columns) { List<Column> tmp = new List<Column>(); foreach (var item in Columns) { if (!item.IsPrimaryKey) tmp.Add(item); } return tmp.ToArray(); } }
进行一下测ishi
public class InitDataImport : MonoBehaviour { public SQLiteDB db; void Start() { db = new SQLiteDB(); string filename = Application.persistentDataPath + "/" + Global.DataBaseName; //Create DB File Even Not Exits db.Open(filename); } void OnDestroy() { db.Close(); } void OnGUI() { if (GUI.Button(new Rect(10, 10, 100, 70), "Create")) CreateTab(); if (GUI.Button(new Rect(10, 100, 100, 70), "Insert")) InsertTab(); if (GUI.Button(new Rect(10, 190, 100, 70), "Update")) UpdateTab(); if (GUI.Button(new Rect(10, 280, 100, 70), "Select")) SelectTab(); } public class MyFirstTable { public int id; public string b; public string c; } public void CreateTab() { DBUtil<MyFirstTable>.CreateTable(ref db, new MyFirstTable()); } public void InsertTab() { for (int i = 0; i < 10; i++) { DBUtil<MyFirstTable>.InsertTable(ref db, new MyFirstTable() { id = i, b = "zhangsan", c = "zhejiang" }); } } public void UpdateTab() { DBUtil<MyFirstTable>.UpdateTableAccordingId(ref db, new MyFirstTable() { id = 0, b = "lisi", c = "sichuan" }); } }
上一张图,看结果,很简单,比较简单,不是很完善,主要是看思路
相关需要的插件包 与 软件如下:
http://yunpan.cn/csQp2nP9HwpIH 提取码 2e00