Unity3D游戏开发之SQLite让数据库开发更简单
在忙碌了一段时间后,终于有时间研究新内容了。今天将和大家交流在Unity3D游戏开发中使用SQLite进行数据库开发的相关知识。坦白说,在我的技术体系里,Web和数据库是相对薄弱的部分。正好这段时间项目需要与服务器、数据库进行交互,所以在接下来的文章中,我会更倾向于讲解这方面的内容,希望大家喜欢!
一、什么是SQLite?
SQLite是一款轻型的关系型数据库管理系统,遵守ACID原则。它包含在一个相对较小的C库中,以嵌入式作为设计目标,占用资源极低,适合在嵌入式设备(如Android、Ruby on Rails等)中使用。它支持Windows、Linux、Unix等主流操作系统,并且能够与C、C++、Ruby、Python、C#、PHP、Java等多种编程语言结合使用。
SQLite以文件形式存在,虽然无法实现分布式和横向扩展,但作为轻量级的嵌入式数据库,它无需系统提供服务支持,可通过SDK直接操作文件,避免了数据库维护的相关事务,从这个角度来看,它是一款出色的数据库。
二、为什么要选择SQLite
在了解了SQLite之后,我们来看看它有哪些吸引人的特性,或者说我们选择SQLite的理由。毕竟在众多数据库中,我们有很多选择,如Oracle、MySQL、SQL Server、DB2、NoSQL、MongoDB等。
SQLite的特性
- ACID事务:保证数据的一致性和完整性。
- 零配置:无需安装和管理配置,使用方便。
- 单一磁盘文件存储:整个数据库存储在一个单一的磁盘文件中。
- 文件共享:数据库文件可以在不同字节顺序的机器间自由共享。
- 大容量支持:支持数据库大小至2TB。
- 体积小巧:代码量约13万行C代码,大小约4.43M。
- 读写效率高:在大部分普通数据库操作中,比一些流行的数据库更快。
- 简单的API:提供简单、易用的API。
- 多语言绑定:包含TCL绑定,同时通过Wrapper支持其他语言的绑定。
- 代码质量高:源代码注释良好,测试覆盖率达90%以上。
- 独立性强:没有额外依赖。
- 开源免费:源码完全开源,可用于任何用途,包括商业出售。
- 支持多种开发语言:如C、C++、PHP、Perl、Java、C#、Python、Ruby等。
三、Unity3D中的SQLite
在Unity3D中使用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和类定义在这两个文件中,这些文件可以从指定位置直接下载。
注意事项
网上有使用Mono.Data.SQLiteClient.dll库在Unity3D中操作SQLite数据库的相关文章,经查看,与使用Mono.Data.Sqlite.dll库大同小异,大家可以根据喜好选择。另外,我在开源社区找到一个据说可以同时支持.NET和Mono的版本库,感兴趣的朋友可以去测试。
数据库读写基本流程
在正式开始编写代码之前,我们先来回顾一下通常情况下数据库读写的基本流程:
- 定义数据库连接字符串(ConnectionString):构造数据库连接,建立或打开一个数据库。
- 定义相关的SQL命令(Command):通过这些命令实现对数据库的增加、删除、更新、读取四种基本功能。
- 关闭数据库连接:在完成各种数据库操作后,及时关闭数据库连接,解除对数据库的连接和引用。
SQLite辅助类
SQLite作为一款优秀的数据库,编写相关代码时同样遵循上述流程。考虑到数据库的增、删、改、查操作具有类似性和统一性,我们先编写一个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="operation">操作符</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>
/// 删除指定数据表内的数据(OR条件)
/// </summary>
/// <returns>The values.</returns>
/// <param name="tableName">数据表名称</param>
/// <param name="colNames">字段名</param>
/// <param name="operations">操作符</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>
/// 删除指定数据表内的数据(AND条件)
/// </summary>
/// <returns>The values.</returns>
/// <param name="tableName">数据表名称</param>
/// <param name="colNames">字段名</param>
/// <param name="operations">操作符</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>
/// 读取指定条件的数据表
/// </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 = 1; i < colNames.Length; i++)
{
queryString += " AND " + colNames[i] + " " + operations[i] + " " + colValues[i];
}
return ExecuteQuery(queryString);
}
}
使用注意事项
- 数据库文件位置:在Unity3D编辑器下,生成的数据库文件(.db)默认位于和Assets目录同级的位置(即项目的工程文件夹中)。可以通过修改路径改变数据库文件的存储位置,不同平台的路径如下:
- Windows平台:
data source=Application.dataPath/数据库名称.db - IOS平台:
data source=Application.persistentDataPath/数据库名称.db Android平台:
URL=file:Application.persistentDataPath/数据库名称.db数据库文件处理:确保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'", "'Zhang3@163.com'" });
sql.InsertValues("table1", new string[] { "'2'", "'李四'", "'25'", "'Li4@163.com'" });
// 更新数据,将Name="张三"的记录中的Name改为"Zhang3"
sql.UpdateValues("table1", new string[] { "Name" }, new string[] { "'Zhang3'" }, "Name", "=", "'张三'");
// 插入3条数据
sql.InsertValues("table1", new string[] { "3", "'王五'", "25", "'Wang5@163.com'" });
sql.InsertValues("table1", new string[] { "4", "'王五'", "26", "'Wang5@163.com'" });
sql.InsertValues("table1", new string[] { "5", "'王五'", "27", "'Wang5@163.com'" });
// 删除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方法中创建了数据库和数据表。但在实际使用中,需要判断数据库和数据表是否存在。如果使用这段脚本提示错误信息,请确保数据库和数据表已经存在。
五、图形化管理工具
作为一款强大的数据库,SQLite也有图形化的数据库管理工具。这里向大家推荐一个免安装的小工具Sqlite Studio,使用它可以方便地管理Sqlite数据库里的数据。该工具可以从指定位置下载。
六、总结
今天的内容就到这里,欢迎大家关注【泰课资讯】。希望通过本文的介绍,大家对在Unity3D中使用SQLite进行数据库开发有了更深入的了解。