原地址: http://cn.cocos2d-x.org/tutorial/show?id=2172
在Cocos2d-x中使用Sqlite作为游戏持久化的解决方案是个不错的选择,使用json或者xml等文件存储的IO读写性能在手机设备上比较差,通常使用Xml和Json等文件存储数据时,因为实时的通过IO读取文件操作非常耗时,往往在游戏启动的时候把数据读取出来放到游戏的缓存中,使用时去缓存中查询,这样会占用大量的内存,如果不想使用缓存的机制来查询,使用sqlite3数据库是个不错的解决方案,我做过测试,在拥有100条记录的表中查询出一条数据,只需要1ms,通常游戏的帧率设置为60,每一帧的时间为1/60秒=16ms,1ms的查询时间是可以接受的,在游戏中每一帧要做大量的操作,CPU的逻辑运算,GPU的运算以及图形的渲染,如果1帧消耗的总时间>16ms,也就是线程Thread的sleep()的时间超过了16ms,则可能造成降帧,或者卡帧。所以数据读取对于游戏的性能影响还是很大的,一定要避免在游戏循环中进行文件的IO的操作,数据库的IO操作也不行。
分享一个Cocos2d-x访问Sqlite3数据库的单例的DBUitl类,对增删改查以及事务操作等常用的API进行了一下简单的封装,还有很多不太完善的地方,希望大家帮我改进。
分别对Cocos的2.x版本和3.x版本进行了封装。
使用方法很简单,直接在项目的dao层调用DBUtil::getInstance()->xxxMethod(params..)即可,这里就不列举使用的demo了,把DBUtil的实现源码贴一下。
2.x版
DBUtil.h
//
// DBUtil.h
// LeadinV1.1
//
// Created by ChengChao on 14-11-4.
//
//
#ifndef __LeadinV1_1__DBUtil__
#define __LeadinV1_1__DBUtil__
#include "main.h"
#include "sqlite3.h"
USING_NS_CC;
using namespace std;
class DBUtil {
private:
//数据库
sqlite3* m_pDataBase;
private:
#pragma mark <构造 && 析构>
DBUtil();
DBUtil(const DBUtil&);
DBUtil& operator = (const DBUtil&);
virtual ~DBUtil();
public:
#pragma mark <创建 && 销毁单例>
static DBUtil* getInstance();
static void destoryInstance();
#pragma mark <数据库操作>
/**
* 打开数据库(创建)
*
* @param aDataBaseName 数据库名
*/
void openDBWithName(string aDataBaseName);
/**
* 关闭数据库
*/
void closeDB();
/**
* 创建数据表
*
* @param aSql 建表sql语句
* @param aTableName 表名
*/
void createTable(string aSql, string aTableName);
/**
* 通过表名查询该表是否存在
*
* @param aTabelName 表秒
*
* @return true: 存在 false: 不存在
*/
bool isExistTableByName(string aTabelName);
/**
* 删除数据表
*
* @param aSql 删表sql语句
* @param aTableName 表名
*/
void deleteTable(string aSql, string aTableName);
/**
* 插入记录
*
* @param aSql 插入数据sql语句
*/
void insertData(string aSql);
/**
* 删除记录
*
* @param aSql 删除数据sql语句
*/
void deleteData(string aSql);
/**
* 修改记录
*
* @param aSql 修改数据sql语句
*/
void updateData(string aSql);
/**
* 查询记录
*
* @param aSql 修改数据sql语句
*/
vector<map<string, string> >searchData(string aSql);
/**
* 查询记录的条数
*
* @param sql 查询记录sql语句
*
* @return 记录条数
*/
int searchDataCount(string aSql);
/**
* 开始事务
*
* @return 操作结果(sqlite3提供的宏)
*/
int beginTransaction();
/**
* 提交事务(失败回滚)
*
* @param aResult 操作结果
*
* @return 操作结果(sqlite3提供的宏)
*/
int comitTransaction(int aResult);
};
#endif /* defined(__LeadinV1_1__DBUtil__) */
DBUtil.cpp
//
// DBUtil.cpp
// LeadinV1.1
//
// Created by ChengChao on 14-11-4.
//
//
#include "DBUtil.h"
#include "NTool.h"
static DBUtil* s_pInstance = NULL;
const string cDBName = "gamedb";
#pragma mark <构造 && 析构>
DBUtil::DBUtil():m_pDataBase(NULL) {
}
DBUtil::~DBUtil() {
}
#pragma mark <创建 && 销毁单例>
DBUtil* DBUtil::getInstance() {
if (!s_pInstance) {
s_pInstance = new DBUtil();
}
return s_pInstance;
}
void DBUtil::destoryInstance() {
delete s_pInstance;
s_pInstance = NULL;
}
#pragma mark <数据库操作>
/**
* 打开数据库(创建)
*
* @param aDataBaseName 数据库名
*/
void DBUtil::openDBWithName(string aDataBaseName) {
string writeablePath = CCFileUtils::sharedFileUtils()->getWritablePath();
CCLOG("path=%s", writeablePath.c_str());
string dbPath = writeablePath + aDataBaseName;
int result = sqlite3_open(dbPath.c_str(), &m_pDataBase);
char* errMsg = NULL;
if (result != SQLITE_OK) {
CCLog("打开数据库失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
}
/**
* 关闭数据库
*/
void DBUtil::closeDB() {
if (m_pDataBase) {
sqlite3_close(m_pDataBase);
m_pDataBase = NULL;
}
}
/**
* 创建数据表
*
* @param aSql 建表sql语句
* @param aTableName 表名
*
* @usage string sql = "create table user(id integer, username text, password text)";
*/
void DBUtil::createTable(string aSql, string aTableName) {
openDBWithName(cDBName);
if (!isExistTableByName(aTableName)) {
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, aSql.c_str(), NULL, NULL, &errMsg);
if (result != SQLITE_OK) {
CCLog("创建表失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
}
closeDB();
}
/**
* 是否存在某张数据表的查询回调
*
* @return 0
*/
int isExistTableCallback(void* para, int n_column, char ** column_value, char ** column_name) {
bool *isExisted_= (bool*)para;
*isExisted_= (**column_value) != '0';
return 0;
}
/**
* 通过表名查询该表是否存在
*
* @param aTabelName 表秒
*
* @return true: 存在 false: 不存在
*/
bool DBUtil::isExistTableByName(string aTabelName) {
if (m_pDataBase) {
//判断表是否存在
bool isExist;
char* errMsg = NULL;
string sql = "select count(type) from sqlite_master where type = 'table' and name = '" + aTabelName + "'";
int result = sqlite3_exec(m_pDataBase, sql.c_str(), isExistTableCallback, &isExist, &errMsg);
if (result != SQLITE_OK) {
CCLog("查询表是否存在失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
return isExist;
}
return false;
}
/**
* 删除数据表
*
* @param aSql 删表sql语句
* @param aTableName 表名
*
* @usage string sql = "drop table name";
*/
void DBUtil::deleteTable(string aSql, string aTableName) {
openDBWithName(cDBName);
beginTransaction();
int result = 0;
if (isExistTableByName(aTableName)) {
char* errMsg = NULL;
result = sqlite3_exec(m_pDataBase, aSql.c_str(), NULL, NULL, &errMsg);
if (result != SQLITE_OK) {
CCLog("创建表失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
}
comitTransaction(result);
closeDB();
}
/**
* 插入记录
*
* @param aSql 插入数据sql语句
*
* @usage string sql = "insert into User(name) values ('cc') ";
*/
void DBUtil::insertData(string aSql) {
openDBWithName(cDBName);
beginTransaction();
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, aSql.c_str(), NULL, NULL, &errMsg);
if (result != SQLITE_OK) {
CCLog("插入记录失败,错误码:%d,错误原因:%s\n", result, errMsg );
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
comitTransaction(result);
closeDB();
}
/**
* 删除记录
*
* @param aSql 插入数据sql语句
*
* @usage string sql = "delete from User where name = 'cc'";
*/
void DBUtil::deleteData(string aSql) {
openDBWithName(cDBName);
beginTransaction();
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, aSql.c_str(), NULL, NULL, &errMsg);
if (result != SQLITE_OK ) {
CCLog("删除记录失败,错误码:%d,错误原因:%s\n" , result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
comitTransaction(result);
closeDB();
}
/**
* 修改记录
*
* @param aSql 修改数据sql语句
*/
void DBUtil::updateData(string aSql) {
openDBWithName(cDBName);
beginTransaction();
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, aSql.c_str(), NULL, NULL, &errMsg );
if (result != SQLITE_OK) {
CCLog( "修改记录失败,错误码:%d,错误原因:%s\n", result, errMsg );
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
comitTransaction(result);
closeDB();
}
/**
* 查询回调
*
* @return 0
*/
int searchDataCallback(void* para, int n_column, char** column_value, char** column_name ) {
map<string, string> mapResults ;
for (int i = 0; i < n_column; i++) {
mapResults.insert(make_pair<string, string>((string)column_name[i], (string)column_value[i]));
}
vector<map<string,string> >* vect =(vector<map<string,string> >*)para;
vect->push_back(mapResults);
return 0;
}
/**
* 查询记录
*
* @param aSql 修改数据sql语句
*/
vector<map<string, string> > DBUtil::searchData(string aSql) {
long long int startTime = getNowTime();
CCLOG("startTime=%lld", getNowTime());
openDBWithName(cDBName);
//vector是查询的结果集,每一个结果都存在map中
//map的第一string是key(字段名),第二个string是value(查询出的对应数据)
vector<map<string, string> > vec;
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, aSql.c_str(), searchDataCallback, &vec, &errMsg);
if (result != SQLITE_OK) {
CCLog("查询失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
closeDB();
long long int endTime = getNowTime();
CCLOG("endTime=%lld", endTime);
CCLOG("needTime=%lld", endTime - startTime);
return vec;
}
/**
* 查询数据条数回调
*
* @return 0
*/
int searchDataCountCallback(void* para, int n_column, char** column_value, char** column_name) {
int* count = (int*)para;
*count = (int)atof(column_value[0]);
return 0;
}
/**
* 查询记录的条数
*
* @param sql 查询记录sql语句
*
* @return 记录条数
*/
int DBUtil::searchDataCount(string aSql) {
openDBWithName(cDBName);
int count = 0;
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, aSql.c_str(), searchDataCountCallback, &count, &errMsg);
if (result != SQLITE_OK) {
CCLog( "查询记录条数失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
closeDB();
return count;
}
/**
* 开始事务
*
* @return 操作结果(sqlite3提供的宏)
*/
int DBUtil::beginTransaction() {
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, "begin transaction", 0, 0, &errMsg);
if (result != SQLITE_OK ){
CCLog("开始事务记录失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
return result;
}
/**
* 提交事务(失败回滚)
*
* @param aResult 操作结果
*
* @return 操作结果(sqlite3提供的宏)
*/
int DBUtil::comitTransaction(int aResult) {
if (aResult == SQLITE_OK) {
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, "commit transaction", 0, 0, &errMsg);
if (result != SQLITE_OK) {
CCLog("提交事务记录失败,错误码:%d,错误原因:%s\n" , result, errMsg);
}
return result;
} else {
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, "rollback transaction", 0, 0, &errMsg);
if (result != SQLITE_OK ) {
CCLog("回滚事务记录失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
return result;
}
}
3.x版
// DBUtil.h
// GuessMovie
//
// Created by apple on 14/11/24.
//
//
#ifndef __GuessMovie__DBUtil__
#define __GuessMovie__DBUtil__
#include "sqlite3.h"
#include "main.h"
USING_NS_CC;
class DBUtil {
private:
//数据库
sqlite3* m_pDataBase;
private:
#pragma mark <构造 && 析构>
DBUtil();
virtual ~DBUtil();
public :
#pragma mark <创建 && 销毁单例>
static DBUtil* getInstance();
static void destoryInstance();
#pragma mark <数据库操作>
/**
* 打开数据库(创建)
*
* @param aDataBaseName 数据库名
*/
void openDBWithName(std::string aDataBaseName);
/**
* 关闭数据库
*/
void closeDB();
/**
* 创建数据表
*
* @param aSql 建表sql语句
* @param aTableName 表名
*/
void createTable(std::string aSql, std::string aTableName);
/**
* 通过表名查询该表是否存在
*
* @param aTabelName 表秒
*
* @return true: 存在 false: 不存在
*/
bool isExistTableByName(std::string aTabelName);
/**
* 删除数据表
*
* @param aSql 删表sql语句
* @param aTableName 表名
*/
void deleteTable(std::string aSql, std::string aTableName);
/**
* 插入记录
*
* @param aSql 插入数据sql语句
*/
void insertData(std::string aSql);
/**
* 删除记录
*
* @param aSql 删除数据sql语句
*/
void deleteData(std::string aSql);
/**
* 修改记录
*
* @param aSql 修改数据sql语句
*/
void updateData(std::string aSql);
/**
* 查询记录
*
* @param aSql 修改数据sql语句
*/
std::vector<std::map<std::string, std::string> >searchData(std::string aSql);
/**
* 查询记录的条数
*
* @param sql 查询记录sql语句
*
* @return 记录条数
*/
int searchDataCount(std::string aSql);
/**
* 开始事务
*
* @return 操作结果(sqlite3提供的宏)
*/
int beginTransaction();
/**
* 提交事务(失败回滚)
*
* @param aResult 操作结果
*
* @return 操作结果(sqlite3提供的宏)
*/
int comitTransaction(int aResult);
};
#endif /* defined(__GuessMovie__DBUtil__) */
DBUtil.cpp
// DBUtil.cpp
// GuessMovie
//
// Created by apple on 14/11/24.
//
//
#include "DBUtil.h"
static DBUtil* s_pInstance = NULL;
const std::string cDBName = "gamedb";
#pragma mark <构造 && 析构>
DBUtil::DBUtil():m_pDataBase(NULL) {
}
DBUtil::~DBUtil() {
}
#pragma mark <创建 && 销毁单例>
DBUtil* DBUtil::getInstance() {
if (!s_pInstance) {
s_pInstance = new DBUtil();
}
return s_pInstance;
}
void DBUtil::destoryInstance() {
delete s_pInstance;
s_pInstance = NULL;
}
#pragma mark <数据库操作>
/**
* 打开数据库(创建)
*
* @param aDataBaseName 数据库名
*/
void DBUtil::openDBWithName(std::string aDataBaseName) {
std::string writeablePath = FileUtils::getInstance()->getWritablePath();
CCLOG("path=%s", writeablePath.c_str());
std::string dbPath = writeablePath + aDataBaseName;
int result = sqlite3_open(dbPath.c_str(), &m_pDataBase);
char* errMsg = NULL;
if (result != SQLITE_OK) {
log("打开数据库失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
}
/**
* 关闭数据库
*/
void DBUtil::closeDB() {
if (m_pDataBase) {
sqlite3_close(m_pDataBase);
m_pDataBase = NULL;
}
}
/**
* 创建数据表
*
* @param aSql 建表sql语句
* @param aTableName 表名
*
* @usage string sql = "create table user(id integer, username text, password text)";
*/
void DBUtil::createTable(std::string aSql, std::string aTableName) {
openDBWithName(cDBName);
if (!isExistTableByName(aTableName)) {
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, aSql.c_str(), NULL, NULL, &errMsg);
if (result != SQLITE_OK) {
log("创建表失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
}
closeDB();
}
/**
* 是否存在某张数据表的查询回调
*
* @return 0
*/
int isExistTableCallback(void* para, int n_column, char ** column_value, char ** column_name) {
bool *isExisted_= (bool*)para;
*isExisted_= (**column_value) != '0';
return 0;
}
/**
* 通过表名查询该表是否存在
*
* @param aTabelName 表秒
*
* @return true: 存在 false: 不存在
*/
bool DBUtil::isExistTableByName(std::string aTabelName) {
if (m_pDataBase) {
//判断表是否存在
bool isExist;
char* errMsg = NULL;
std::string sql = "select count(type) from sqlite_master where type = 'table' and name = '" + aTabelName + "'";
int result = sqlite3_exec(m_pDataBase, sql.c_str(), isExistTableCallback, &isExist, &errMsg);
if (result != SQLITE_OK) {
log("查询表是否存在失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
return isExist;
}
return false;
}
/**
* 删除数据表
*
* @param aSql 删表sql语句
* @param aTableName 表名
*
* @usage string sql = "drop table name";
*/
void DBUtil::deleteTable(std::string aSql, std::string aTableName) {
openDBWithName(cDBName);
beginTransaction();
int result = 0;
if (isExistTableByName(aTableName)) {
char* errMsg = NULL;
result = sqlite3_exec(m_pDataBase, aSql.c_str(), NULL, NULL, &errMsg);
if (result != SQLITE_OK) {
log("创建表失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
}
comitTransaction(result);
closeDB();
}
/**
* 插入记录
*
* @param aSql 插入数据sql语句
*
* @usage string sql = "insert into User(name) values ('cc') ";
*/
void DBUtil::insertData(std::string aSql) {
openDBWithName(cDBName);
beginTransaction();
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, aSql.c_str(), NULL, NULL, &errMsg);
if (result != SQLITE_OK) {
log("插入记录失败,错误码:%d,错误原因:%s\n", result, errMsg );
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
comitTransaction(result);
closeDB();
}
/**
* 删除记录
*
* @param aSql 插入数据sql语句
*
* @usage string sql = "delete from User where name = 'cc'";
*/
void DBUtil::deleteData(std::string aSql) {
openDBWithName(cDBName);
beginTransaction();
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, aSql.c_str(), NULL, NULL, &errMsg);
if (result != SQLITE_OK ) {
log("删除记录失败,错误码:%d,错误原因:%s\n" , result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
comitTransaction(result);
closeDB();
}
/**
* 修改记录
*
* @param aSql 修改数据sql语句
*/
void DBUtil::updateData(std::string aSql) {
openDBWithName(cDBName);
beginTransaction();
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, aSql.c_str(), NULL, NULL, &errMsg );
if (result != SQLITE_OK) {
log( "修改记录失败,错误码:%d,错误原因:%s\n", result, errMsg );
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
comitTransaction(result);
closeDB();
}
/**
* 查询回调
*
* @return 0
*/
int searchDataCallback(void* para, int n_column, char** column_value, char** column_name ) {
std::map<std::string, std::string> mapResults ;
for (int i = 0; i < n_column; i++) {
mapResults.insert(std::make_pair<std::string, std::string>((std::string)column_name[i], (std::string)column_value[i]));
}
std::vector<std::map<std::string, std::string> >* vect = (std::vector<std::map<std::string, std::string> >*)para;
vect->push_back(mapResults);
return 0;
}
/**
* 查询记录
*
* @param aSql 查询数据sql语句
*/
std::vector<std::map<std::string, std::string> > DBUtil::searchData(std::string aSql) {
// long long int startTime = getNowTime();
// CCLOG("startTime=%lld", getNowTime());
openDBWithName(cDBName);
//vector是查询的结果集,每一个结果都存在map中
//map的第一string是key(字段名),第二个string是value(查询出的对应数据)
std::vector<std::map<std::string, std::string> > vec;
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, aSql.c_str(), searchDataCallback, &vec, &errMsg);
if (result != SQLITE_OK) {
log("查询失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
closeDB();
// long long int endTime = getNowTime();
// CCLOG("endTime=%lld", endTime);
// CCLOG("needTime=%lld", endTime - startTime);
return vec;
}
/**
* 查询数据条数回调
*
* @return 0
*/
int searchDataCountCallback(void* para, int n_column, char** column_value, char** column_name) {
int* count = (int*)para;
*count = (int)atof(column_value[0]);
return 0;
}
/**
* 查询记录的条数
*
* @param sql 查询记录sql语句
*
* @return 记录条数
*/
int DBUtil::searchDataCount(std::string aSql) {
openDBWithName(cDBName);
int count = 0;
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, aSql.c_str(), searchDataCountCallback, &count, &errMsg);
if (result != SQLITE_OK) {
log( "查询记录条数失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
if (errMsg) {
sqlite3_free(errMsg);
errMsg = NULL;
}
closeDB();
return count;
}
/**
* 开始事务
*
* @return 操作结果(sqlite3提供的宏)
*/
int DBUtil::beginTransaction() {
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, "begin transaction", 0, 0, &errMsg);
if (result != SQLITE_OK ){
log("开始事务记录失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
return result;
}
/**
* 提交事务(失败回滚)
*
* @param aResult 操作结果
*
* @return 操作结果(sqlite3提供的宏)
*/
int DBUtil::comitTransaction(int aResult) {
if (aResult == SQLITE_OK) {
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, "commit transaction", 0, 0, &errMsg);
if (result != SQLITE_OK) {
log("提交事务记录失败,错误码:%d,错误原因:%s\n" , result, errMsg);
}
return result;
} else {
char* errMsg = NULL;
int result = sqlite3_exec(m_pDataBase, "rollback transaction", 0, 0, &errMsg);
if (result != SQLITE_OK ) {
log("回滚事务记录失败,错误码:%d,错误原因:%s\n", result, errMsg);
}
return result;
}
}