各位朋友大家好。欢迎大家关注我的博客,我是秦元培,我是博客地址是http://blog.csdn.net/qinyuanpei。在经历了一段时间的忙碌后,博主最终有时间来研究新的东西啦,今天博客向和大家一起交流的内容是在Unity3D游戏开发中使用SQLite进行数据库开发。坦白来讲,在我的技术体系中Web和数据库是相对薄弱的两个部分。因此正好这段时间项目须要和server、数据库进行交互,因此在接下来的文章中博主可能会更加倾向于解说这方面的内容,希望大家能够喜欢啊!
一、什么是SQLite?
SQLite是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包括在一个相对小的C库中,以嵌入式作为它的设计目标,它占用资源很的低,因此适合在嵌入式设备如Android、Ruby on Rails等中使用。它能够支持Windows/Linux/Unix等等主流的操作系统。同一时候能够跟和C、C++、Ruby、Python、C#、PHP、Java等编程语言相结合。SQLite是一个以文件形式存在的关系型数据库,虽然无法实现分布式和横向扩展,但是作为一个轻量级的嵌入式数据库。它不须要系统提供服务支持,通过SDK直接操作文件避免了对数据库维护的相关事务,从这个角度来讲它是一个出色的数据库。
二、为什么要选择SQLite
好了,在了解了SQLite后,我们来了解下SQLite有哪些让我们心动的特性,或者说我们为什么要选择SQLite,由于在这个世界上我们有太多的数据库能够选择。诸如Oracle、MySQL、SQLServer、DB2、NoSQL、MongoDB等等:
- ACID事务
- 零配置 – 无需安装和管理配置
- 储存在单一磁盘文件里的一个完整的数据库
- 数据库文件能够在不同字节顺序的机器间自由的共享
- 支持数据库大小至2TB
- 足够小, 大致13万行C代码, 4.43M
- 比一些流行的数据库在大部分普通数据库操作要快—SQLite读写效率如此之高,会使用其它数据库的理由是?
- 简单, 轻松的API
- 包括TCL绑定, 同一时候通过Wrapper支持其它语言的绑定
- 良好凝视的源代码, 而且有着90%以上的測试覆盖率
- 独立: 没有额外依赖
- 源代码全然的开源, 你能够用于不论什么用途, 包括出售它
- 支持多种开发语言,C, C++, PHP, Perl, Java, C#,Python, Ruby等
三、Unity3D中的SQLite
在Unity3D中使用SQLite。我们首先要明确这样一件事情。即我们这里的使用的SQLite并不是是通常意义上的SQLite.NET,而是经过移植后的Mono.Data.Sqlite。由于Unity3D基于Mono,因此使用移植后的Mono.Data.Sqlite能够降低我们的项目在不同平台上出现各种各样的问题。
在Unity3D中使用的SQLite以Mono.Data.Sqlite.dll即动态链接库的形式给出,因此我们须要将这个文件放置在项目文件夹下的Plugins文件夹中,此外我们须要System.Data.dll或者Mono.Data.dll这两个文件加入到Plugins文件夹中,由于我们须要的部分数据相关的API或者类都定义在这两个文件其中,这些文件能够从这里直接下载。
PS:博主注意到在网上有使用Mono.Data.SQLiteClient.dll这个库实如今Unity3D操作SQLite数据库的相关文章。博主大概看了下,感觉和使用Mono.Data.Sqlite.dll这个库大同小异,大家喜欢哪个就用哪个吧!哈哈!博主在开源社区找到一个版本号库。据说能够同一时候支持.NET和Mono,假设大家感兴趣欢迎大家去測试啊,哈哈!
在正式開始写代码前。我们首先来回想下通常情况下数据库读写的基本流程吧!
- 定义数据库连接字符串(ConnectionString)完毕数据库连接的构造,建立或者打开一个数据库。
- 定义相关的SQL命令(Command)通过这些命令实现对数据库的添加、删除、更新、读取四种基本功能。
- 在完毕各种数据库操作后及时关闭数据库连接。解除对数据库的连接和引用。
SQLite作为一款优秀的数据库,在为其编写数据库相关代码时相同遵循这种流程,考虑到对数据库的添加、删除、更新、读取四种操作具有相似性和统一性,因此在动手写Unity3D脚本前。首先让我们来编写一个SQLite的辅助类SQLiteHelper.cs。该类代码定义例如以下:
using UnityEngine;
using System.Collections;
using Mono.Data.Sqlite;
using System;
public class SQLiteHelper
{
/// <summary>
/// 数据库连接定义
/// </summary>
private SqliteConnection dbConnection;
/// <summary>
/// SQL命令定义
/// </summary>
private SqliteCommand dbCommand;
/// <summary>
/// 数据读取定义
/// </summary>
private SqliteDataReader dataReader;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="connectionString">数据库连接字符串</param>
public SQLiteHelper(string connectionString)
{
try{
//构造数据库连接
dbConnection=new SqliteConnection(connectionString);
//打开数据库
dbConnection.Open();
}catch(Exception e)
{
Debug.Log(e.Message);
}
}
/// <summary>
/// 执行SQL命令
/// </summary>
/// <returns>The query.</returns>
/// <param name="queryString">SQL命令字符串</param>
public SqliteDataReader ExecuteQuery(string queryString)
{
dbCommand = dbConnection.CreateCommand();
dbCommand.CommandText = queryString;
dataReader = dbCommand.ExecuteReader();
return dataReader;
}
/// <summary>
/// 关闭数据库连接
/// </summary>
public void CloseConnection()
{
//销毁Command
if(dbCommand != null){
dbCommand.Cancel();
}
dbCommand = null;
//销毁Reader
if(dataReader != null){
dataReader.Close();
}
dataReader = null;
//销毁Connection
if(dbConnection != null){
dbConnection.Close();
}
dbConnection = null;
}
/// <summary>
/// 读取整张数据表
/// </summary>
/// <returns>The full table.</returns>
/// <param name="tableName">数据表名称</param>
public SqliteDataReader ReadFullTable(string tableName)
{
string queryString = "SELECT * FROM " + tableName;
return ExecuteQuery (queryString);
}
/// <summary>
/// 向指定数据表中插入数据
/// </summary>
/// <returns>The values.</returns>
/// <param name="tableName">数据表名称</param>
/// <param name="values">插入的数值</param>
public SqliteDataReader InsertValues(string tableName,string[] values)
{
//获取数据表中字段数目
int fieldCount=ReadFullTable(tableName).FieldCount;
//当插入的数据长度不等于字段数目时引发异常
if(values.Length!=fieldCount){
throw new SqliteException("values.Length!=fieldCount");
}
string queryString = "INSERT INTO " + tableName + " VALUES (" + values[0];
for(int i=1; i<values.Length; i++)
{
queryString+=", " + values[i];
}
queryString += " )";
return ExecuteQuery(queryString);
}
/// <summary>
/// 更新指定数据表内的数据
/// </summary>
/// <returns>The values.</returns>
/// <param name="tableName">数据表名称</param>
/// <param name="colNames">字段名</param>
/// <param name="colValues">字段名相应的数据</param>
/// <param name="key">关键字</param>
/// <param name="value">关键字相应的值</param>
public SqliteDataReader UpdateValues(string tableName,string[] colNames,string[] colValues,string key,string operation,string value)
{
//当字段名称和字段数值不正确应时引发异常
if(colNames.Length!=colValues.Length) {
throw new SqliteException("colNames.Length!=colValues.Length");
}
string queryString = "UPDATE " + tableName + " SET " + colNames[0] + "=" + colValues[0];
for(int i=1; i<colValues.Length; i++)
{
queryString+=", " + colNames[i] + "=" + colValues[i];
}
queryString += " WHERE " + key + operation + value;
return ExecuteQuery(queryString);
}
/// <summary>
/// 删除指定数据表内的数据
/// </summary>
/// <returns>The values.</returns>
/// <param name="tableName">数据表名称</param>
/// <param name="colNames">字段名</param>
/// <param name="colValues">字段名相应的数据</param>
public SqliteDataReader DeleteValuesOR(string tableName,string[] colNames,string[] operations,string[] colValues)
{
//当字段名称和字段数值不正确应时引发异常
if(colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length) {
throw new SqliteException("colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length");
}
string queryString = "DELETE FROM " + tableName + " WHERE " + colNames[0] + operations[0] + colValues[0];
for(int i=1; i<colValues.Length; i++)
{
queryString+="OR " + colNames[i] + operations[0] + colValues[i];
}
return ExecuteQuery(queryString);
}
/// <summary>
/// 删除指定数据表内的数据
/// </summary>
/// <returns>The values.</returns>
/// <param name="tableName">数据表名称</param>
/// <param name="colNames">字段名</param>
/// <param name="colValues">字段名相应的数据</param>
public SqliteDataReader DeleteValuesAND(string tableName,string[] colNames,string[] operations,string[] colValues)
{
//当字段名称和字段数值不正确应时引发异常
if(colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length) {
throw new SqliteException("colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length");
}
string queryString = "DELETE FROM " + tableName + " WHERE " + colNames[0] + operations[0] + colValues[0];
for(int i=1; i<colValues.Length; i++)
{
queryString+=" AND " + colNames[i] + operations[i] + colValues[i];
}
return ExecuteQuery(queryString);
}
/// <summary>
/// 创建数据表
/// </summary> +
/// <returns>The table.</returns>
/// <param name="tableName">数据表名</param>
/// <param name="colNames">字段名</param>
/// <param name="colTypes">字段名类型</param>
public SqliteDataReader CreateTable(string tableName,string[] colNames,string[] colTypes)
{
string queryString = "CREATE TABLE " + tableName + "( " + colNames [0] + " " + colTypes [0];
for (int i=1; i<colNames.Length; i++)
{
queryString+=", " + colNames[i] + " " + colTypes[i];
}
queryString+= " ) ";
return ExecuteQuery(queryString);
}
/// <summary>
/// Reads the table.
/// </summary>
/// <returns>The table.</returns>
/// <param name="tableName">Table name.</param>
/// <param name="items">Items.</param>
/// <param name="colNames">Col names.</param>
/// <param name="operations">Operations.</param>
/// <param name="colValues">Col values.</param>
public SqliteDataReader ReadTable(string tableName,string[] items,string[] colNames,string[] operations, string[] colValues)
{
string queryString = "SELECT " + items [0];
for (int i=1; i<items.Length; i++)
{
queryString+=", " + items[i];
}
queryString += " FROM " + tableName + " WHERE " + colNames[0] + " " + operations[0] + " " + colValues[0];
for (int i=0; i<colNames.Length; i++)
{
queryString+=" AND " + colNames[i] + " " + operations[i] + " " + colValues[0] + " ";
}
return ExecuteQuery(queryString);
}
}
SQLiteHelper类主要实现了数据库、数据表的创建以及数据表中记录的添加、删除、更新、读取四种基本功能。该类最初由国外的Unity3D开发人员公布在Unity3D官方论坛,后来经宣雨松使用C#进行重写。我在此基础上进行了完好。再此对两位大神的无私付出表示感谢。
这里要说明的有三点:
- 一、在Unity3D编辑器下生成数据库文件(.db)默认位于和Assets文件夹同级的位置。即项目的project文件夹中。我们能够通过改动路径在改变数据库文件的存储位置,详细来讲:
Windows平台:data source=Application.dataPath/数据库名称.db
IOS平台:data source=Application.persistentDataPath/数据库名称.db
Android平台:URL=file:Application.persistentDataPath/数据库名称.db(我想说Android平台就是个奇葩。搞什么特殊化嘛)
- 二、确保Unity3D编辑器中的.NET版本号和MonoDevelop中的.NET版本号都为2.0版本号,在Unity3D中打包导出的程序可能不会保留数据库文件,因此须要手动将数据库文件复制到相应的位置,当然更加合理的方案是将数据库文件存放到StreamingAssets文件夹下,然后在第一次载入游戏的时候将数据库文件复制到相应平台上的存放位置。
- 三、在使用InsertValues方法时请參考SQLite中字段类型与C#中数据类型的相应关系,博主眼下測试了int类型和string类型都没有什么问题。很多其它类型的数据请大家自行測试然后告诉博主測试的结果。假设大家有兴趣扩展这个辅助类的话能够自行去扩展哦,嘿嘿!
好了。千呼万唤始出来的时候到了,以下我们以一个实例来完毕今天的项目解说,由于我们已经定义好了SQLite的辅助类,因此我们能够高速地编写出以下的脚本代码:
using UnityEngine;
using System.Collections;
using System.IO;
using Mono.Data.Sqlite;
public class SQLiteDemo : MonoBehaviour
{
/// <summary>
/// SQLite数据库辅助类
/// </summary>
private SQLiteHelper sql;
void Start ()
{
//创建名为sqlite4unity的数据库
sql = new SQLiteHelper("data source=sqlite4unity.db");
//创建名为table1的数据表
sql.CreateTable("table1",new string[]{"ID","Name","Age","Email"},new string[]{"INTEGER","TEXT","INTEGER","TEXT"});
//插入两条数据
sql.InsertValues("table1",new string[]{"‘1‘","‘张三‘","‘22‘","‘[email protected]‘"});
sql.InsertValues("table1",new string[]{"‘2‘","‘李四‘","‘25‘","‘[email protected]‘"});
//更新数据。将Name="张三"的记录中的Name改为"Zhang3"
sql.UpdateValues("table1", new string[]{"Name"}, new string[]{"‘Zhang3‘"}, "Name", "=", "‘张三‘");
//插入3条数据
sql.InsertValues("table1",new string[]{"3","‘王五‘","25","‘[email protected]‘"});
sql.InsertValues("table1",new string[]{"4","‘王五‘","26","‘[email protected]‘"});
sql.InsertValues("table1",new string[]{"5","‘王五‘","27","‘[email protected]‘"});
//删除Name="王五"且Age=26的记录,DeleteValuesOR方法相似
sql.DeleteValuesAND("table1", new string[]{"Name","Age"}, new string[]{"=","="}, new string[]{"‘王五‘","‘26‘"});
//读取整张表
SqliteDataReader reader = sql.ReadFullTable ("table1");
while(reader.Read())
{
//读取ID
Debug.Log(reader.GetInt32(reader.GetOrdinal("ID")));
//读取Name
Debug.Log(reader.GetString(reader.GetOrdinal("Name")));
//读取Age
Debug.Log(reader.GetInt32(reader.GetOrdinal("Age")));
//读取Email
Debug.Log(reader.GetString(reader.GetOrdinal("Email")));
}
//读取数据表中Age>=25的全部记录的ID和Name
reader = sql.ReadTable ("table1", new string[]{"ID","Name"}, new string[]{"Age"}, new string[]{">="}, new string[]{"‘25‘"});
while(reader.Read())
{
//读取ID
Debug.Log(reader.GetInt32(reader.GetOrdinal("ID")));
//读取Name
Debug.Log(reader.GetString(reader.GetOrdinal("Name")));
}
//自己定义SQL,删除数据表中全部Name="王五"的记录
sql.ExecuteQuery("DELETE FROM table1 WHERE NAME=‘王五‘");
//关闭数据库连接
sql.CloseConnection();
}
}
在上面的代码中我们是在Start方法中创建了数据库和数据表,然而在实际使用中我们须要推断数据库和数据表是否存在。因此假设你使用这段脚本提示错误信息,请确保数据库和数据表是否已经存在。
好了,以下的截图展示了程序执行的结果:
作为一个强大的数据库怎么能没有图形化的数据库管理工具呢?所以这里博主向大家推荐一个免安装的小工具SqliteStudio,使用这个工具能够帮助我们方便地管理Sqlite数据库里的数据,这样是不是比較方便呢?哈哈!
这个工具能够从这里下载哦。
好了,今天的内容就是这样了,为了写这篇文章花了三个晚上准备。希望大家喜欢啊!
假设大家认为这篇文章实用,请继续关注我的博客。我是秦元培。我的博客地址是http://blog.csdn.net/qinyuanpei。
2015年11月3日更新内容:在不同的平台上数据库的存储位置是不同的。在这里给出一个參考的路径。希望大家在处理移动端的时候注意这些问题啊!
//各平台下数据库存储的绝对路径(通用)
//PC:sql = new SQLiteHelper("data source=" + Application.dataPath + "/sqlite4unity.db");
//Mac:sql = new SQLiteHelper("data source=" + Application.dataPath + "/sqlite4unity.db");
//Android:sql = new SQLiteHelper("URI=file:" + Application.persistentDataPath + "/sqlite4unity.db");
//iOS:sql = new SQLiteHelper("data source=" + Application.persistentDataPath + "/sqlite4unity.db");
//PC平台下的相对路径
//sql = new SQLiteHelper("data source="sqlite4unity.db");
//编辑器:Assets/sqlite4unity.db
//编译后:和AppName.exe同级的文件夹下,这里比較奇葩
//当然能够用更任意的方式sql = new SQLiteHelper("data source="D://SQLite//sqlite4unity.db");
//确保路径存在就可以否则会错误发生
//假设是事先创建了一份数据库
//能够将这个数据库放置在StreamingAssets文件夹下然后再复制到
//Application.persistentDataPath + "/sqlite4unity.db"路径就可以