首页 > 学院 > 开发设计 > 正文

Unity3d开发——Sqlite数据库

2019-11-09 16:34:16
字体:
来源:转载
供稿:网友

前言

Sqlite是一个轻量级数据库,在Unity中可以很方便的使用它。比如做AssetBundle资源更新时,可以把AssetBundle的描述信息存放在数据库中;或者将一些数值表生成数据库文件,作为游戏的功能配置文件等等。。。

正文

导入步骤

先到Sqlite官网下载对应你系统的Sqlite库(比如windows64系统,对应 PRecompiled Binaries for Windows sqlite-dll-win64-x64)。

在Unity中Assets目录下新建Plugins/x86_64目录,解压将sqlite3.def和sqlite3.dll放到该目录下。

由于在代码中需要使用System.Data和Mono.Data.Sqlite库,这两个库在Unity3d的安装目录中有。 在unity安装目录/Editor/Data/Mono/lib/mono/2.0下,拷贝System.Data.dll 和 Mono.Data.Sqlite.dll放到Plugins下。

在Player Setting里的 OtherSettings里有个Optimization,下边的API Compatbility Level 选择.NET 2.0。

注意,这里我们只下载了windows上的sqltie库,如果需要在移动端使用则应该去下载对应平台的sqltie库。

此时,工程的目录结构应该是这样的 工程的目录结构

接下来,封装一个Sqlite类,用于对数据库的一些基本操作,增、删、改、查等。

Sqlite.cs

using UnityEngine;using System.Collections;using System.Collections.Generic;using Mono.Data.Sqlite;using System.Data;using System;using System.Reflection;using Object = UnityEngine.Object;namespace Hi{ public class Sqlite { //Sqlite连接前缀 private const string DB_CONNECTION_PREFIX = "URI=file:"; //连接器 private IDbConnection m_dbConn; //查询命令 private IDbCommand m_command; //事物 private IDbTransaction m_dbTrans; public Sqlite(string path) { OpenDataBase(path); } private void OpenDataBase(string path) { m_dbConn = new SqliteConnection(DB_CONNECTION_PREFIX + path); m_dbConn.Open(); m_command = m_dbConn.CreateCommand(); } //查询函数 //IConfig是自定义的配置类接口,最好使数据库中的配置类都继承同一个接口,方便以后扩展 public IEnumerable ExcuteSelectQuery<T>(string sqlQuery) where T : IConfig { return ExcuteSelectQuery(sqlQuery, typeof(T)); } //查询函数,这里使用反射 反序列数据,简化对对象的赋值操作 public IEnumerable ExcuteSelectQuery(string sqlQuery, Type type) { //使用事物 BeginTrans(); //查询语句 m_command.CommandText = sqlQuery; //查询结果 IDataReader reader = m_command.ExecuteReader(); //反序列化操作 所定义的变量 PropertyInfo[] newpropertys = null; PropertyInfo[] oldpropertys = null; bool init = false; while (reader.Read()) { //创建类对象 IConfig config = Activator.CreateInstance(type) as IConfig; for (int i = 0; i < reader.FieldCount; i++) { if (!init) { //newpropertys将类的属性 顺序的对应到数据库中的列名,方便后续的赋值操作 if (newpropertys == null) { newpropertys = new PropertyInfo[reader.FieldCount]; } //获取类的所有属性 if (oldpropertys == null) { oldpropertys = type.GetProperties(BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance); } //当前数据的列名 string filedName = reader.GetName(i); bool find = false; PropertyInfo pro = null; //查找与列名相同的自定义特性名称,相同则把查询到的数据值赋值到对象中 for (int j = 0; j < oldpropertys.Length; j++) { pro = oldpropertys[j]; //获取自定义特性 object[] objs = pro.GetCustomAttributes(typeof(ConfigFieldAttribute), false); if (objs.Length == 0) { continue; } //特性的列名是否与数据库中列名相同 ConfigFieldAttribute cfgfield = objs[0] as ConfigFieldAttribute; if (cfgfield.filedName == filedName) { find = true; break; } } newpropertys[i] = find ? pro : null; } //已经排好序的类属性数组 PropertyInfo info = newpropertys[i]; if (info == null) { continue; } //对象的属性赋值 info.SetValue(config, reader.GetValue(i), null); } if (!init) { init = true; } yield return config; } Commit(); reader.Close(); } //使用事物 public void BeginTrans() { m_dbTrans = m_dbConn.BeginTransaction(); m_command.Transaction = m_dbTrans; } //事物回滚 public void Rollback() { m_dbTrans.Rollback(); } //事物生效 public void Commit() { m_dbTrans.Commit(); } //执行其他sql语句 public void ExcuteQuery(string sqlQuery) { m_command.CommandText = sqlQuery; m_command.ExecuteNonQuery(); } //关闭连接 public void Close() { if (m_dbTrans != null) { m_dbTrans.Dispose(); m_dbTrans = null; } if (m_command != null) { m_command.Dispose(); m_command = null; } m_dbConn.Close(); m_dbConn = null; } }}

中间那段查询函数写得有点麻烦,它的功能是把查询到的一行数据赋值给一个新对象,后面会详细说明

IConfig.cs

namespace Hi{ //这里只是想把所有的数据库配置类抽出一个接口,方便以后扩展 public interface IConfig { }}

ConfigFieldAttribute.cs

using UnityEngine;using System.Collections;using System;namespace Hi{ //自定义配置类特性,用于跟数据库中单条数据匹配 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] public class ConfigFieldAttribute : Attribute { public string filedName { get; private set; } public ConfigFieldAttribute(string name) { filedName = name; } }}

TestConfig.cs

using UnityEngine;using System.Collections;using Hi;public class TestConfig : IConfig{ //对应数据库中列名为ID的数据 [ConfigField("ID")] public string m_id { get; set; } [ConfigField("Name")] public string m_name { get; set; } [ConfigField("Len")] public int m_len { get; set; } public override string ToString() { return string.Format("Id : {0}, Name : {1}, Len : {2}", m_id, m_name, m_len); }}

Test.cs

using UnityEngine;using System.Collections;using Hi;using System.Text;public class Test : MonoBehaviour{ private Sqlite m_sqlite; private const string TABLE_NAME = "UnityTest"; // Use this for initialization void Start() { //数据库地址 string dbPath = application.dataPath + "/test.db"; m_sqlite = new Sqlite(dbPath); //创建新表 CreateTable(); //插入数据 TestConfig tc1 = new TestConfig { m_id = "001", m_name = "test1", m_len = 10 }; InsertTable(tc1); //在插入一条 TestConfig tc2 = new TestConfig { m_id = "002", m_name = "test2", m_len = 20 }; InsertTable(tc2); //查询数据 SelectTable(); //更新数据 tc2.m_name = "johnny"; tc2.m_len = 5; UpdateTable(tc2); SelectTable(); } void OnDestroy() { m_sqlite.Close(); } private void CreateTable() { StringBuilder sql = new StringBuilder(); sql.Append("create table "); sql.Append(TABLE_NAME); sql.Append("(ID VARCHAR(255) PRIMARY KEY,"); sql.Append("Name VARCHAR(255),"); sql.Append("Len INT);"); Excute(sql.ToString()); } private void InsertTable(TestConfig tc) { StringBuilder sql = new StringBuilder(); sql.Append("insert into "); sql.Append(TABLE_NAME); sql.Append(" values ('"); sql.Append(tc.m_id); sql.Append("','"); sql.Append(tc.m_name); sql.Append("',"); sql.Append(tc.m_len); sql.Append(");"); Excute(sql.ToString()); } private void SelectTable() { string sql = "select * from " + TABLE_NAME; foreach (var tc in m_sqlite.ExcuteSelectQuery<TestConfig>(sql)) { Debug.Log(tc); } } private void UpdateTable(TestConfig tc) { StringBuilder sql = new StringBuilder(); sql.Append("update "); sql.Append(TABLE_NAME); sql.Append(" set Name = '"); sql.Append(tc.m_name); sql.Append("', Len = "); sql.Append(tc.m_len); sql.Append(" where ID = '"); sql.Append(tc.m_id); sql.Append("';"); Excute(sql.ToString()); } private void Excute(string sql) { Debug.Log(sql.ToString()); m_sqlite.ExcuteQuery(sql); }}

创建表、插入数据、更新数据这些都是直接执行sql语句没什么可说的,但要注意的是在插入和更新多条数据时,使用事物会使效率提高很多。具体的方法就是在执行语句前调用BeginTrans()函数,执行后调用Commit()函数,如果有异常就回滚,参考ExcuteSelectQuery函数。

重点说一下查询操作,也就是ExcuteSelectQuery这个函数。 一般情况下,在数据库中获得一行数据时,这一行数据应该是能跟某个类对象的属性或字段一一对应的,也就是反序列化。但是反序列化的过程是繁琐、重复的,如果数据库中的每一个表都要去写对应的反序列化函数真是太麻烦了(谁让我们很懒呢╮(╯▽╰)╭)。为了简化这个过程,定义一个特性ConfigFieldAttribute,其filedName 属性就对应数据库中的列名(当然也可以方便的将属性名称直接对应为数据库中的列名),这样一来,只需给对应属性加上特性,即可完成反序列化,这里通过反射完成这一过程。

最后,Unity中运行结果: 这里写图片描述

此时,项目中会生成一个test.db文件,用数据库可视化工具打开 这里写图片描述

大功告成!!!最后放上自己的Unity工程。

第一篇文章,可能有些地方描述的不很清楚,如果有什么问题,欢迎大家指出!!!


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表