Unity3D之SQLite的使用
原创作者:秦元培
一、什么是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和SQLite3.dll这三个文件添加到Plugins目录中,因为我们需要的部分数据相关的API或者类都定义在这两个文件当中.
在正式开始写代码前,我们首先来回顾下通常情况下数据库读写的基本流程吧!
- 定义数据库连接字符串(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(){//销毁Commandif(dbCommand != null){dbCommand.Cancel();}dbCommand = null;//销毁Readerif(dataReader != null){dataReader.Close();}dataReader = null;//销毁Connectionif(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);}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
SQLiteHelper类主要实现了数据库、数据表的创建以及数据表中记录的增加、删除、更新、读取四种基本功能。该类最初由国外的Unity3D开发者发布在Unity3D官方论坛,后来经宣雨松使用C#进行重写,我在此基础上进行了完善,再此对两位大神的无私付出表示感谢。这里要说明的有三点:
一、在Unity3D编辑器下生成数据库文件(.db)默认位于和Assets目录同级的位置,即项目的工程文件夹中。我们可以通过修改路径在改变数据库文件的存储位置,具体来讲:
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'","'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()) {//读取IDDebug.Log(reader.GetInt32(reader.GetOrdinal("ID")));//读取NameDebug.Log(reader.GetString(reader.GetOrdinal("Name")));//读取AgeDebug.Log(reader.GetInt32(reader.GetOrdinal("Age")));//读取EmailDebug.Log(reader.GetString(reader.GetOrdinal("Email")));}//读取数据表中Age>=25的所有记录的ID和Namereader = sql.ReadTable ("table1", new string[]{"ID","Name"}, new string[]{"Age"}, new string[]{">="}, new string[]{"'25'"});while(reader.Read()) {//读取IDDebug.Log(reader.GetInt32(reader.GetOrdinal("ID")));//读取NameDebug.Log(reader.GetString(reader.GetOrdinal("Name")));}//自定义SQL,删除数据表中所有Name="王五"的记录sql.ExecuteQuery("DELETE FROM table1 WHERE NAME='王五'");//关闭数据库连接sql.CloseConnection();}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
在上面的代码中我们是在Start方法中创建了数据库和数据表,然而在实际使用中我们需要判断数据库和数据表是否存在,因此如果你使用这段脚本提示错误信息,请确保数据库和数据表是否已经存在。好了,下面的截图展示了程序运行的结果:
作为一个强大的数据库怎么能没有图形化的数据库管理工具呢?所以这里博主向大家推荐一个免安装的小工具SqliteStudio,使用这个工具可以帮助我们方便地管理Sqlite数据库里的数据,这样是不是比较方便呢?哈哈!这个工具可以从这里下载哦!
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"路径即可
注:有的同学出现了Unty 使用SQLITE 运行时出现“database is not open‘’
错误信息如下:InvalidOperationException: Database is not openMono.Data.Sqlite.SqliteCommand.InitializeForReader ()Mono.Data.Sqlite.SqliteCommand.ExecuteReader (CommandBehavior behavior)Mono.Data.Sqlite.SqliteCommand.ExecuteReader ()(wrapper remoting-invoke-with-check) Mono.Data.Sqlite.SqliteCommand:ExecuteReader ()SQLiteHelper.ExecuteQuery (System.String queryString) (at Assets/Script/SQLiteHelper.cs:51)SQLiteHelper.CreateTable (System.String tableName, System.String[] colNames, System.String[] colTypes) (at Assets/Script/SQLiteHelper.cs:205)SQLiteDemo.Start () (at Assets/Script/SQLiteDemo.cs:20)
原因可能是没有将SQLite3.dll文件放到指定的位置,
解决办法是将SQLITE3.DLL 放到C:\Windows\System32或者放到Unity项目中的plugins目录中,总之就是要3个dll文件都要放到plugins目录中。
CSDN社区的IT牛人,各种问题各种解决,谢谢大家!
Unity3D之SQLite的使用相关推荐
- Unity3D高级-Sqlite
SQLite 是一种嵌入式数据库,它的数据库就是一个文件.由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中,甚至在iOS和Android的App中都可以集成.SQLite是 ...
- Unity3D所使用的第三方工具
前言 最近在研究Unity3D的结构和原理.关于Unity3D怎么用之类的教程在网上一找一大堆,想看教程之类的就不用看这篇文章了.我想做的是从使用Unity3D所看到的表象(表),摸透其设计原则和组织 ...
- Unity3D常见面试题
Unity3D常见面试题 目录 C#问题 4 1.在类的构造函数前加上static会报什么错?为什么? 4 2.C# StringBuilder类型比string类型的优势是什么? 4 3.C# 函数 ...
- 腾讯开源手游热更新方案,Unity3D下的Lua编程
写在前面 \\ xLua是Unity3D下Lua编程解决方案,自2016年初推广以来,已经应用于十多款腾讯自研游戏,因其良好性能.易用性.扩展性而广受好评.现在,腾讯已经将xLua开源到GitHub. ...
- Unity3D面试ABC
Unity3D面试ABC 杨航最近在学Unity3D 最先执行的方法是: 1.(激活时的初始化代码)Awake,2.Start.3.Update[FixUpdate.LateUpdate].4.( ...
- u3d+向服务器上传文件,unity3d向服务器传数据库
unity3d向服务器传数据库 内容精选 换一换 "数据导入"章节适用于MRS 3.x及后续版本.Loader是实现MRS与外部数据源如关系型数据库.SFTP服务器.FTP服务器之 ...
- [Unity3D]面试题集锦
最先执行的方法是: 1.(激活时的初始化代码)Awake,2.Start.3.Update[FixUpdate.LateUpdate].4.(渲染模块)OnGUI.5.再向后,就是卸载模块(TearD ...
- 续上文,Unity3D面试ABC
最先执行的方法是: 1.(激活时的初始化代码)Awake,2.Start.3.Update[FixUpdate.LateUpdate].4.(渲染模块)OnGUI.5.再向后,就是卸载模块(TearD ...
- Unity3D研究院之在Unity中打开第三方数据库配合Android开发(三十二)
http://www.xuanyusong.com/archives/831 http://www.xuanyusong.com/archives/1454 如果大家对Unity中如何使用数据库还不是 ...
最新文章
- uboot引导kernel - 3 -uboot给内核传参详解
- 哈佛结构和冯诺依曼结构区别。
- Nginx(OpenResty)+Lua+Redis IP限流 10s内
- 浏览器HTTP缓存机制
- Python 调用C函数
- arch linux 安装 arm,给树莓派安装 Arch Linux ARM
- Lucene系列:(9)搜索结果排序
- php快速学习方法,php快速入门学习方法
- 匿名对象方案与实体对象方案对比
- HMS Core 携优势亮相华为发布会,与苹果谷歌三足鼎立
- JDBC 与ODBC的区别
- C++ 调用 SOAP Web Service
- 设计模式(十八):责任链模式
- 人工智能革命(上):通往超级智能之路
- 深信服 adesk linux 客户端,Sangfor-aDesk巡检工具(深信服桌面云智能交付巡检助手)V2.1 正式版...
- 2021年道路运输企业安全生产管理人员考试内容及道路运输企业安全生产管理人员题库
- 教你如何用java开发Alexa语音设备。全程详细介绍。
- IPFS为什么被学者称为数据的“黄金保险柜”?
- php远程控制代码,内部网络机器的远程控制软件UltraVNC及其源代码
- 虚拟主机3种方式nginx/apache+跨域知识点整理
热门文章
- Python爬虫进行接口测试
- CommandBuffer.Blit BuiltinRenderTextureType.CameraTarget为空的问题
- 一起来学Flutter: 使用Decoration设置背景
- 计算机二进制表示小数,小数的二进制表示
- Macbook pro 安装Homebrew 软件
- Python习题答案
- 为什么说 Flutter 可能不是下一件大事?
- Wordpress转移数据表后出现You do not have sufficient permissions to access this page的解决方法...
- 如何利用redis 实现分布式项目枷锁功能
- iphone 手机忘记 访问限制密码 处理方法(未越狱版)