Unity Sqlite的使用与简单封装

2024-09-03 06:58

本文主要是介绍Unity Sqlite的使用与简单封装,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

并不是要介绍网上一搜一大片的Sqlite在Unity中的用法,而是想介绍下不一样的封装。另外对Unity和C#中的反射也并不熟练,所以难免疏漏。

Unity中使用Sqlite

所需文件

Mono.Data.Sqlite.dll
Mono.Data.dll
Sqlite3.dll       (32位/64位)

前两个文件在 \Unity\Editor\Data\MonoBleedingEdge\lib\mono\2.0 这个目下可以找到

后一个文件Sqlite 可以在这儿下载 https://sourceforge.net/projects/sqlitebrowser/ 需要注意下载对应的32位或者64位

将上面的三个文件拷贝至文件夹 Asset\Plugins 下。如果是用在Android上,还需要一个 libsqlite3.so 文件,放在 Asset\Plugins\Android\ 下,文件可以在下面的链接的文章中下载到。http://blog.csdn.net/qinyuanpei/article/details/46812655#t1

不知道为啥编辑器没有自动添加引用,所以使用时记得添加下面的几个引用。

:unity`SqliteConnection' could not be found.
using Mono.Data.Sqlite; :The type ornamespace name `Exception' could not be found.
using System;:The type ornamespace name `SqliteDataReader' could not be found.
using System.IO;

简单使用

如果只是想简单的用用,下面的代码是一个使用示例。

来源:http://answers.unity3d.com/questions/743400/database-sqlite-setup-for-unity.html

    string conn = "URI=file:" + Application.dataPath + "/PickAndPlaceDatabase.s3db"; //Path to database.SqliteConnection dbconn;dbconn =  new SqliteConnection(conn);dbconn.Open(); //Open connection to the database.SqliteCommand dbcmd = dbconn.CreateCommand();string sqlQuery = "SELECT value,name, randomSequence " + "FROM PlaceSequence";dbcmd.CommandText = sqlQuery;SqliteDataReader reader = dbcmd.ExecuteReader();while (reader.Read()){int value = reader.GetInt32(0);string name = reader.GetString(1);int rand = reader.GetInt32(2);Debug.Log( "value= "+value+"  name ="+name+"  random ="+  rand);}reader.Close();reader = null;dbcmd.Dispose();dbcmd = null;dbconn.Close();dbconn = null;}

封装 DbUtil (反射)

如何在Unity上使用用上Sqlite 在这里不做过多的讨论,因为上面的两个链接里已经有详细的描述了。这里讨论下另外的写法。

保存数据时,我并不希望每次保存数据时都去写一遍Sql,而是希望更简洁的操作。我不想去关心我是在操作那个类的数据类,里面有哪些字段 等等的封装。

比如创建两张不同的表:

dbUtil.CreateTable(new Pen());
dbUtil.CreateTable(new Person());

插入一条记录:

dbUtil.Insert(new Pen() { Id = 1, name = "钢笔1", color = "red", size = 1.123 });

插入链表:

dbUtil.InsertList(personList);
dbUtil.InsertList(penList);

不需要再去关心是哪个类,实体类有哪些字段等,就保存到数据库了。以前做Android开发的时候有很多开源框架可以使用,比如 xUtils 等。在Unity这用Sqlite似乎需求比较少,所以自己动手写个简单点的。用反射似乎效率会比较低,反射虽然慢,但对我们来说足以够快了。况且 数据库操作不都是放到另外的线程里去的么?

Column.cs

首先要完成上面的操作,重要的一点就是拼接Sql语句,那就要先拿到类的相关信息。Sqlite 支持四中类型,blob用的少,不管了。另外三个 int对应integer,string对应text,double对应real。代码如下:

namespace Liangddyy.Util
{/// <summary>/// 反射字段/// </summary>public class Column{//Column Valueprivate readonly MyProperty property;public string columnName;public Type columnType;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(double)){double v = Convert.ToDouble(value);property = new MyProperty<double>();((MyProperty<double>) property).SetValue(v);}else if (ColumnType == typeof(int)){int v = Convert.ToInt32(value);property = new MyProperty<int>();((MyProperty<int>) property).SetValue(v);}}public string TableType{get{if (IsPrimaryKey)return "INTEGER PRIMARY KEY";return To_TableType(columnType);}}public bool IsPrimaryKey{// 转换小写get{return (columnName.ToLower().StartsWith("id") || columnName.ToLower().StartsWith("_id")) &&(columnType == typeof(int));}}public object ColumnValue{get{if (property == null) return null;if (columnType == typeof(double))return ((MyProperty<double>) property).GetValue();if (columnType == typeof(string))return ((MyProperty<string>) property).GetValue();if (columnType == typeof(int))return ((MyProperty<int>) property).GetValue();return null;}}public string To_TableType(Type type){if (type == typeof(double)) return ColType.REAL.ToString();if (type == typeof(int)) return ColType.INTEGER.ToString();if (type == typeof(string)) return ColType.TEXT.ToString();throw new Exception("Wrong Type");}}/// <summary>/// 枚举数据库Sqlite支持的几种类型/// </summary>public enum ColType{INTEGER,TEXT,REAL,BLOB}public class MyProperty{}public class MyProperty<T> : MyProperty{private readonly bool _isValue;private bool _changing;private T _value;public MyProperty(){
#if UNITY_FLASH_isValue = false;
#else_isValue = typeof(T).IsValueType;
#endif}public MyProperty(T value): this(){_value = value;}public bool IsOfType(Type t){return t == typeof(T);}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);elsechanged = ((value == null) && (_value != null)) ||((value != null) && (_value == null)) ||((_value != null) && IsClassDifferent(value));if (changed)_value = value;_changing = false;}}
}

DbUtil.cs

注意事项写在后面。

namespace Liangddyy.Util
{/// <summary>///     自己写的Sqlite封装类/// </summary>public class DbUtil{
#if UNITY_ANDROIDprivate static string path = "URI=file:" + Application.dataPath;
#endif
#if UNITY_IPHONEprivate static string path = "data source=" + Application.persistentDataPath;
#endif
#if UNITY_STANDALONE_WINprivate static readonly string path = "data source=" + Application.dataPath;
#endifprivate static readonly string dbName = "/test01.db";private static DbUtil dbUtil;private SqliteConnection dbConnection;private SqliteCommand dbCmd;private SqliteTransaction dbTransaction;private SqliteDataReader dbReader;public static DbUtil getInstance(){if (dbUtil == null)dbUtil = new DbUtil(path + dbName);return dbUtil;}private DbUtil(string connectionStr){try{dbConnection = new SqliteConnection(connectionStr);}catch (Exception e){Debug.LogError(e.Message);}}public SqliteDataReader ExecuteQuery(string queryString){dbCmd = dbConnection.CreateCommand();dbCmd.CommandText = queryString;dbReader = dbCmd.ExecuteReader();return dbReader;}public int ExecuteSql(string sqlStr){if (dbConnection.State == ConnectionState.Closed)dbConnection.Open();dbCmd = dbConnection.CreateCommand();dbCmd.CommandText = sqlStr;return dbCmd.ExecuteNonQuery();}private int ExecuteSql(SqliteCommand sqlCmd){return dbCmd.ExecuteNonQuery();}public void CreateTable<T>(T arg){this.ExecuteSql(GetCmdCreateTable(arg));}public void Insert<T>(T record){dbCmd = dbConnection.CreateCommand();this.ExecuteSql(GetCmdInsert<T>(record, dbCmd));}public void InsertList<T>(List<T> Records){dbTransaction = dbConnection.BeginTransaction();try{for (var i = 0; i < Records.Count; i++)Insert(Records[i]);dbTransaction.Commit();}catch (Exception){dbTransaction.Rollback();throw;}}public void InsertArray<T>(T[] Records){dbTransaction = dbConnection.BeginTransaction();try{for (var i = 0; i < Records.Length; i++)Insert(Records[i]);dbTransaction.Commit();}catch (Exception){dbTransaction.Rollback();throw;}}public void Update<T>(T arg){dbCmd = dbConnection.CreateCommand();this.ExecuteSql(GetCmdUpdate(arg, dbCmd));}/// <summary>///     关闭数据库连接 销毁连接/// </summary>public void CloseConnection(){//销毁Commandif (dbCmd != null)dbCmd.Cancel();dbCmd = null;//销毁Readerif (dbReader != null)dbReader.Close();dbReader = null;//销毁Connectionif (dbConnection != null)dbConnection.Close();dbConnection = null;}/// <summary>/// 更新的sql语句/// </summary>/// <typeparam name="T"></typeparam>/// <param name="arg">The argument.</param>/// <param name="cmd">The command.</param>/// <returns></returns>private SqliteCommand GetCmdUpdate<T>(T arg, SqliteCommand cmd){Type type = arg.GetType();var TableName = type.Name;Column[] Columns = GetColumnsByType(arg);if ((Columns == null) || (Columns.Length < 1)){Debug.LogError("Type is Wrong");return null;}Column key = null;Column[] normal = null;//Debug.Log("Now Insert:" + TableName);var RowLength = Columns.Length;normal = GetNormalColumn(Columns);cmd.CommandText = "UPDATE " + TableName + " SET " + normal[0].columnName + " = @" + normal[0].columnName;for (var i = 1; i < normal.Length; i++)cmd.CommandText += "," + normal[i].columnName + " = @" + normal[i].columnName;key = GetPrimaryColumn(Columns);cmd.CommandText += " WHERE " + key.columnName + " = @" + key.columnName + ";";for (var i = 0; i < RowLength; i++)cmd.Parameters.Add(new SqliteParameter("@" + Columns[i].columnName, Columns[i].ColumnValue));return cmd;}/// <summary>///     获取主键/// </summary>/// <param name="Columns">The columns.</param>/// <returns></returns>private Column GetPrimaryColumn(Column[] Columns){foreach (var item in Columns)if (item.IsPrimaryKey) return item;return null;}/// <summary>///     获取普通字段/// </summary>/// <param name="Columns">The columns.</param>/// <returns></returns>private Column[] GetNormalColumn(Column[] Columns){var tmp = new List<Column>();foreach (var item in Columns)if (!item.IsPrimaryKey)tmp.Add(item);return tmp.ToArray();}/// <summary>///     获取类的相关字段信息。/// </summary>/// <param name="arg">The argument.</param>/// <returns></returns>private Column[] GetColumnsByType<T>(T arg){FieldInfo[] fieldsThis =arg.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);FieldInfo[] fieldsBase =arg.GetType().BaseType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);FieldInfo[] fields = new FieldInfo[fieldsBase.Length + fieldsThis.Length];Array.Copy(fieldsBase, 0, fields, 0, fieldsBase.Length);Array.Copy(fieldsThis, 0, fields, fieldsBase.Length, fieldsThis.Length);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;var Value = fields[i].GetValue(arg);tableType[i] = new Column(Cur_Type, Cur_Name, Value);}return tableType;}/// <summary>/// 建表Sql/// </summary>/// <typeparam name="T"></typeparam>/// <param name="arg">The argument.</param>/// <returns></returns>private string GetCmdCreateTable<T>(T arg){Type type = arg.GetType();string TableName = type.Name;Column[] columns = GetColumnsByType(arg);if ((columns == null) || (columns.Length < 1)){Debug.LogError("Type is Wrong");//todo 异常处理return null;}Debug.Log("NowCreate:" + TableName);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;}/// <summary>/// 获取一条插入sql语句/// </summary>/// <typeparam name="T"></typeparam>/// <param name="record">The record.</param>/// <param name="cmd">The command.</param>/// <returns></returns>private SqliteCommand GetCmdInsert<T>(T record, SqliteCommand cmd){Type type = record.GetType();string TableName = type.Name;Column[] columns = GetColumnsByType(record);if ((columns == null) || (columns.Length < 1)){Debug.LogError("Type is Wrong");return null;}int RowLength = columns.Length;cmd.CommandText = "INSERT INTO " + TableName + "(" + columns[0].columnName;for (var i = 1; i < RowLength; i++)cmd.CommandText += "," + columns[i].columnName;cmd.CommandText += ") VALUES (@" + columns[0].columnName;for (var i = 1; i < RowLength; i++)cmd.CommandText += ",@" + columns[i].columnName;cmd.CommandText += ");";for (var i = 0; i < RowLength; i++)cmd.Parameters.Add(new SqliteParameter("@" + columns[i].columnName, columns[i].ColumnValue));return cmd;}}
}

注意事项

1 不同平台下数据库文件的路径不同,所以要判断一下。

#if UNITY_ANDROIDprivate static string path = "URI=file:" + Application.dataPath;
#endif
#if UNITY_IPHONEprivate static string path = "data source=" + Application.persistentDataPath;
#endif
#if UNITY_STANDALONE_WINprivate static readonly string path = "data source=" + Application.dataPath;
#endif

2 获取当前类的字段信息

GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

获取父类的字段信息(BaseType)

arg.GetType().BaseType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

主要注意的是:

BindingFlags.Public 只能拿到public字段,而往往我们写实体类时,都是私有属性,然后Get/Set 所以必须加上BindingFlags.NonPublic 才能获取私有属性。

3 拼接Sql语句时切忌直接用String 拼接。

否则可能会有下面类似的报错:

SqliteException: SQLite error Insufficient parameters supplied to the command ...

正确写法类似于下面或者上面贴出的代码中拼接Sql部分。

command.CommandText = "INSERT INTO [User] (UserPK ,Name ,Password   ,Category ,ContactFK) VALUES ( @UserPK , @Name , @Password ,        @Category , @ContactFK)";
command.Parameters.Add(new SqliteParameter("@Name", "Has"));
...

优雅的使用

2050387-7a46d7badc54bcc5.png
BaiduShurufa_2016-12-9_15-22-22

(截图是因为这里不必要的代码很长,这个代码又没有什么意思。)

建了三个类,为了区别,另外两个类属性一个是private,一个是public。(Get/Set隐藏了)。

随便绑定个脚本Start里放上下面的代码 测试一下

DbUtil dbUtil = DbUtil.getInstance();dbUtil.CreateTable(new Person());//建表dbUtil.CreateTable(new Pen());dbUtil.Insert(new Pen() { Id = 1, name = "钢笔1", color = "red", size = 1.123 });//插入dbUtil.Insert(new Person() { Id = 1, Name = "liangddyy", Email = "hehe" });List<Pen> penList = new List<Pen>();List<Person> personList = new List<Person>();for (int i = 5; i < 20; i++){Pen pen = new Pen() {Id = i, name = "钢笔1", color = "red", size = 1.123};Person person = new Person() {Id = i, Name = "liang", Email = "你好"};personList.Add(person);penList.Add(pen);}dbUtil.InsertList(penList);//插入链表dbUtil.InsertList(personList);dbUtil.Update(new Person() { Id =  1, Name = "梁先生", Email = "这条记录被修改了哈哈" });dbUtil.CloseConnection();

打开 Sqlite数据库文件

2050387-ebc805d91c9fb1d1.png
123
2050387-2bf5215d859c18f8.png
122

搞定。
另外,我用的Sqlite可视化工具是 Navicat Premium,各种数据库都能连接。神器。

需完善的地方

  1. 查询部分。因为DbModel还没写。
  2. 异常处理。这个不太擅长。
  3. SaveOrUpdate() 和SaveOrUpdateList() 方法。
  4. Android里面比较流行用注解的方式,C#貌似是用自定义特性么?总之是想实现通过标注的方式来获取字段的特征,如是否非空等或者说通过实体类自定义字段名等。

有时间再完善吧。

链接:http://539go.com/2016/12/09/Unity-Sqlite%E7%9A%84%E4%BD%BF%E7%94%A8%E4%B8%8E%E7%AE%80%E5%8D%95%E5%B0%81%E8%A3%85/

这篇关于Unity Sqlite的使用与简单封装的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1132348

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu2289(简单二分)

虽说是简单二分,但是我还是wa死了  题意:已知圆台的体积,求高度 首先要知道圆台体积怎么求:设上下底的半径分别为r1,r2,高为h,V = PI*(r1*r1+r1*r2+r2*r2)*h/3 然后以h进行二分 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#includ

usaco 1.3 Prime Cryptarithm(简单哈希表暴搜剪枝)

思路: 1. 用一个 hash[ ] 数组存放输入的数字,令 hash[ tmp ]=1 。 2. 一个自定义函数 check( ) ,检查各位是否为输入的数字。 3. 暴搜。第一行数从 100到999,第二行数从 10到99。 4. 剪枝。 代码: /*ID: who jayLANG: C++TASK: crypt1*/#include<stdio.h>bool h

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]