最近需要用到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; }