您的位置:首页 > 移动开发 > Cocos引擎

Cocos2d-x开发系列 lua sqlite集成 LSQLite3

2016-06-12 15:59 1736 查看
项目中经常会使用到sqlite来存储数据,例如聊天,以及一些游戏中的配置。

Cocos2d-x引擎中是有一个storage文件夹,仔细看LocalStorage.h文件,里面说明了,值专门针对JS Bindings使用。另外没有sqlite.c文件。引擎中也没有SQLite的sqlite.c实现文件。所以我们需要到官网去下载:

luaSqlite的源代码http://lua.sqlite.org/index.cgi/index

下载sqlite的源代码 http://sqlite.org/

引擎版本:3.6

一、集成到项目中

在上面的两个网址下载文件后等到如下文件:

lsqlite3.c (lua的绑定实行已经做好了)

shell.c

sqlite3.c

sqlite3.h

sqlite3ext.h

1、在Class里面新建文件夹Sqlite3,将上述文件放在里面。编译

2、lua-bindings中的auto目录下新建lsqlite3.h文件,内容如下

#ifndef __LSQLITE3_H__
#define __LSQLITE3_H__

#ifdef __cplusplus
extern "C" {
#endif
#include "tolua++.h"
#ifdef __cplusplus
}
#endif

extern "C" int luaopen_lsqlite3(lua_State* L);   //lsqlite3.c 中的C函数,这里注册C函数
#endif


3、注册SQLite相关函数

在L状态中调用lsqlite.h文件中的 luaopen_lsqlite3注册到lua虚拟机环境中。

跟其它的自定义类,或者引擎相关的注册函数调用出调用。这里我们在LuaSupportFactory::registerCustomLuaModule()中添加注册代码

void LuaSupportFactory::registerCustomLuaModule()
{
//TODO 注册自定义的Lua API
lua_State* L = LuaEngine::getInstance()->getLuaStack()->getLuaState();

register_all_ww_net(L);
register_all_ww_gui(L);
register_all_ww_data(L);
luaopen_lsqlite3(L); //注册LSQLite3相关内容。
}


4、测试验证

在lua中调用如下

local sqlite3 = require("sqlite3")
cclog("sqlite3  version"..sqlite3.version())


二、关于包体大小

集成的时候,发现,几个文件加起来达到6.78M。主要是sqlite3.c文件很大。不过其实编译后就很小了。



win32下面文件变为2.16M了,但是看看其他的cpp文件编译后大小比这大多了。

新建一个Cocos2d-x lua项目

分别打出不包含、包含lsqlite3的两个项目包



文件差不多大。所以sqlite.c文件很大,但是因为是C文件,在打包后会压缩到很小。不必担心集成包大小

三、使用API

1、引用LuaSqlite

local sqlite3 = require("sqlite3")

--打开数据库文件
local db = sqlite3.open('test.db')

--打开内存数据库
local db = sqlite3.open_memory()


四、碰到的问题

1、error loading module ‘sqlite3’

在一次更新代码的时候将sqlite注册到Lua的代码冲掉了,一直报这个错误,后来检查代码才发现是没有注册过去。

2、iOS平台下,出现shell.c报错

需要将这个文件去掉,这个文件是控制台数据库操作用的,不应该加搞项目中。

五、实例

-------------------------------------------------------------------------
-- Desc:    sqlite3test.lua
-- Author:  sqlite3测试
-- Copyright (c) wawagame Entertainment All right reserved.
-- local sqlite3 = require("app.dataorm.sqlite3test")
-- sqlite3:getDBVersion()
-- sqlite3:openDB()
-- sqlite3:insert('numbers', {10086, 10086,"diyal"})
-- sqlite3:test()
-- sqlite3:test2()
-- sqlite3:test3()
-- sqlite3:aggregate()
-- sqlite3:crudTest()
-- sqlite3:statement()
-- sqlite3:tracing()
-- sqlite3:batchsql()
-- sqlite3:updateHook()
-------------------------------------------------------------------------
local sqlite3 = require("sqlite3")

local sqlite3test = class('sqlite3test')

local _db, _vm  --数据库句柄, 数据库状态

--[[获取版本号]]
function sqlite3test:getDBVersion()
cclog("[SQLite] DB version : "..sqlite3.version())
end

function sqlite3test:openDB()
local dbFilePath = device.writablePath..'test.db'
local isExist = cc.FileUtils:getInstance():isFileExist(dbFilePath)

_db = sqlite3.open(dbFilePath)
if isExist then
cclog('[SQLite] DB File is exist')
else
cclog('[SQLite] DB File is not exist, created it')
--初始化
4000
表结构
self:initDB()
end
end

function sqlite3test:initDB()
-- Demo表DDL语句
local t_demo_sql=
[=[
CREATE TABLE numbers(num1,num2,str);
INSERT INTO numbers VALUES(1,11,"ABC");
INSERT INTO numbers VALUES(2,22,"DEF");
INSERT INTO numbers VALUES(3,33,"UVW");
INSERT INTO numbers VALUES(4,44,"XYZ");
SELECT * FROM numbers;
]=]

local showrow = function(udata,cols,values,names)
assert(udata == 't_demo_create')

-- for i=1,cols do
--    cclog('%s |-> %s',names[i],values[i])
-- end
cclog('[SQLite] %s rows %s', udata,table.concat( values, "-"))

return 0
end
_db:exec(t_demo_sql, showrow, 't_demo_create')
end

function sqlite3test:insert( tableName, tableParas)

local t_demo_sql=
[=[
INSERT INTO tableName VALUES(tableParas);
SELECT * FROM numbers;
]=]

local showrow = function(udata,cols,values,names)
assert(udata == 't_demo_create')

-- for i=1,cols do
--    cclog('%s |-> %s',names[i],values[i])
-- end
cclog('[SQLite] %s rows %s', udata,table.concat( values, "-"))

return 0
end
local ret = _db:exec(t_demo_sql, showrow, 't_demo_create')
if ret ~= sqlite3.OK then
cclog('error')
end
end

--[[test]]
function sqlite3test:test()
local sqlite3 = require('sqlite3')

local width = 78
local function line(pref, suff)  --格式化函数
pref = pref or ''
suff = suff or ''
local len = width - 2 - string.len(pref) - string.len(suff)
cclog(pref .. string.rep('_', len) .. suff)
end

local db, vm
local assert_, assert = assert, function (test)
if (not test) then
error(db:errmsg(), 2)
end
end

-- os.remove('test.db')
db = sqlite3.open('test.db')

line(nil, 'db:exec')
db:exec('CREATE TABLE t(a, b)')

line(nil, 'prepare')
vm = db:prepare('insert into t values(?, :bork)')
assert(vm, db:errmsg())
assert(vm:bind_parameter_count() == 2)
assert(vm:bind_values(2, 4) == sqlite3.OK)
assert(vm:step() == sqlite3.DONE)
assert(vm:reset() == sqlite3.OK)
assert(vm:bind_names{ 'pork', bork = 'nono' } == sqlite3.OK)
assert(vm:step() == sqlite3.DONE)
assert(vm:reset() == sqlite3.OK)
assert(vm:bind_names{ bork = 'sisi' } == sqlite3.OK)
assert(vm:step() == sqlite3.DONE)
assert(vm:reset() == sqlite3.OK)
assert(vm:bind_names{ 1 } == sqlite3.OK)
assert(vm:step() == sqlite3.DONE)
assert(vm:finalize() == sqlite3.OK)

line("select * from t", 'db:exec')

-- assert(db:exec('select * from t', function (ud, ncols, values, names)
--     cclog(table.unpack(values))
--     return sqlite3.OK
-- end) == sqlite3.OK)

db:exec('select * from t', function (ud, ncols, values, names)
cclog(
table.concat(
{ unpack(values)}
)
)

return sqlite3.OK
end)

line("select * from t", 'db:prepare')

vm = db:prepare('select * from t')
assert(vm, db:errmsg())
cclog(vm:get_unames())
while (vm:step() == sqlite3.ROW) do
cclog(vm:get_uvalues())
end
assert(vm:finalize() == sqlite3.OK)

line('udf', 'scalar')

local function do_query(sql)
local r
local vm = db:prepare(sql)
assert(vm, db:errmsg())
cclog('====================================')
cclog(vm:get_unames())
cclog('------------------------------------')
r = vm:step()
while (r == sqlite3.ROW) do
cclog(vm:get_uvalues())
r = vm:step()
end
assert(r == sqlite3.DONE)
assert(vm:finalize() == sqlite3.OK)
cclog('====================================')
end

local function udf1_scalar(ctx, v)
local ud = ctx:user_data()
ud.r = (ud.r or '') .. tostring(v)
ctx:result_text(ud.r)
end

db:create_function('udf1', 1, udf1_scalar, { })
do_query('select udf1(a) from t')

line('udf', 'aggregate')

local function udf2_aggregate(ctx, ...)
local ud = ctx:get_aggregate_data()
if (not ud) then
ud = {}
ctx:set_aggregate_data(ud)
end
ud.r = (ud.r or 0) + 2
end

local function udf2_aggregate_finalize(ctx, v)
local ud = ctx:get_aggregate_data()
ctx:result_number(ud and ud.r or 0)
end

db:create_aggregate('udf2', 1, udf2_aggregate, udf2_aggregate_finalize, { })
do_query('select udf2(a) from t')

-- if (true) then
--     line(nil, '100 insert exec')
--     db:exec('delete from t')
--     local t = os.time()
--     for i = 1, 100 do
--         db:exec('insert into t values('..i..', '..(i * 2 * -1^i)..')')
--     end
--     print('elapsed: '..(os.time() - t))
--     do_query('select count(*) from t')

--     line(nil, '100000 insert exec T')
--     db:exec('delete from t')
--     local t = os.time()
--     db:exec('begin')
--     for i = 1, 100000 do
--         db:exec('insert into t values('..i..', '..(i * 2 * -1^i)..')')
--     end
--     db:exec('commit')
--     print('elapsed: '..(os.time() - t))
--     do_query('select count(*) from t')

--     line(nil, '100000 insert prepare/bind T')
--     db:exec('delete from t')
--     local t = os.time()
--     local vm = db:prepare('insert into t values(?, ?)')
--     db:exec('begin')
--     for i = 1, 100000 do
--         vm:bind_values(i, i * 2 * -1^i)
--         vm:step()
--         vm:reset()
--     end
--     vm:finalize()
--     db:exec('commit')
--     print('elapsed: '..(os.time() - t))
--     do_query('select count(*) from t')

-- end

line(nil, "db:close")

local filePath = cc.FileUtils:getInstance():fullPathForFilename('test.db')
cclog('path: '..filePath)

assert(db:close() == sqlite3.OK)
end

function sqlite3test:test2()
_db = sqlite3.open('test.db')
cclog('[SQLite] db:exec')
_db:exec('CREATE TABLE t(a, b)')

cclog('[SQLite] prepare')
_vm = _db:prepare('insert into t values(?, :bork)')
assert(_vm, _db:errmsg())
assert(_vm:bind_parameter_count() == 2)
assert(_vm:bind_values(2, 4) == sqlite3.OK)
assert(_vm:step() == sqlite3.DONE)
assert(_vm:reset() == sqlite3.OK)
assert(_vm:bind_names{ 'pork', bork = 'nono' } == sqlite3.OK)
assert(_vm:step() == sqlite3.DONE)
assert(_vm:reset() == sqlite3.OK)
assert(_vm:bind_names{ bork = 'sisi' } == sqlite3.OK)
assert(_vm:step() == sqlite3.DONE)
assert(_vm:reset() == sqlite3.OK)
assert(_vm:bind_names{ 1 } == sqlite3.OK)
assert(_vm:step() == sqlite3.DONE)
assert(_vm:finalize() == sqlite3.OK)

cclog("[SQLite] select * from t")

-- assert(_db:exec('select * from t',
--     function (ud, ncols, values, names)
--         cclog(table.unpack(values))
--         return sqlite3.OK
--     end) == sqlite3.OK
-- )

local ret = _db:exec('select * from t',
function (ud, ncols, values, names)
cclog('[SQLite] ' .. table.unpack(values))
return sqlite3.OK
end)

end

--[[test 3]]
function sqlite3test:test3()

end

--[[聚合函数]]
function sqlite3test:aggregate()
assert( _db:exec "CREATE TABLE test (col1, col2)" )
assert( _db:exec "INSERT INTO test VALUES (1, 2)" )
assert( _db:exec "INSERT INTO test VALUES (2, 4)" )
assert( _db:exec "INSERT INTO test VALUES (3, 6)" )
assert( _db:exec "INSERT INTO test VALUES (4, 8)" )
assert( _db:exec "INSERT INTO test VALUES (5, 10)" )

do
local square_error_sum = 0

local function step(ctx, a, b)
local error        = a - b
local square_error = error * error
square_error_sum   = square_error_sum + square_error
end

local function final(ctx)
ctx:result_number( square_error_sum / ctx:aggregate_count() )
end

assert( _db:create_aggregate("my_stats", 2, step, final) )
end

for my_stats in _db:urows("SELECT my_stats(col1, col2) FROM test")
do
cclog("my_stats:%d", my_stats)
end
end

--[[CRUD]]
function sqlite3test:crudTest()
local sqlite3 = require("sqlite3") --加载模块

local db = sqlite3.open_memory() --开辟内存数据库

db:exec[[ CREATE TABLE test (id INTEGER PRIMARY KEY, content) ]] --执行DDL语句

local stmt = db:prepare[[ INSERT INTO test VALUES (:key, :value) ]] --前导声明

stmt:bind_names({
key = 1,
value = "Hello World"
}) --参数绑定

-- step()
-- This is the top-level implementation of sqlite3_step().  Call
-- sqlite3Step() to do most of the work.  If a schema error occurs,
-- call sqlite3Reprepare() and try again.
stmt:step()  --执行
stmt:reset()  --重置
stmt:bind_names({  key = 2,  value = "Hello Lua"      } )
stmt:step()
stmt:reset()
stmt:bind_names({  key = 3,  value = "Hello Sqlite3"  })
stmt:step()
stmt:finalize()

for row in db:nrows("SELECT * FROM test") do
cclog("%d, %s", row.id, row.content)
end
end

--[[statement]]
function sqlite3test:statement()
local sqlite3 = require("sqlite3")

local db = sqlite3.open_memory()

db:exec[[
CREATE TABLE test (
id        INTEGER PRIMARY KEY,
content   VARCHAR
);
]]

local insert_stmt = assert( db:prepare("INSERT INTO test VALUES (NULL, ?)") )

local function insert(data)  --封装一个insert函数
insert_stmt:bind_values(data)
insert_stmt:step()
insert_stmt:reset()
end

local select_stmt = assert( db:prepare("SELECT * FROM test") )

local function select()  --封装一个查询函数
for row in select_stmt:nrows() do
cclog("%d, %s",row.id, row.content)
end
end

insert("Hello World")
cclog("First:")
select()

insert("Hello Lua")
cclog("Second:")
select()

insert("Hello Sqlite3")
cclog("Third:")
select()
end

--[[tracing]]
function sqlite3test:tracing()
local sqlite3 = require("sqlite3")

local db = sqlite3.open_memory()

db:trace( function(ud, sql)
cclog("[Sqlite Trace]: %s", sql)
end )

db:exec[=[
CREATE TABLE test ( id INTEGER PRIMARY KEY, content VARCHAR );

INSERT INTO test VALUES (NULL, 'Hello World');
INSERT INTO test VALUES (NULL, 'Hello Lua');
INSERT INTO test VALUES (NULL, 'Hello Sqlite3');
]=]

for row in db:rows("SELECT * FROM test") do
cclog(row.content)
end
end

--[[batch sql str]]
--批处理
function sqlite3test:batchsql()

local sqlite3 = require("sqlite3")

local db = sqlite3.open_memory()

local sql=[=[
CREATE TABLE numbers(num1,num2,str);
INSERT INTO numbers VALUES(1,11,"ABC");
INSERT INTO numbers VALUES(2,22,"DEF");
INSERT INTO numbers VALUES(3,33,"UVW");
INSERT INTO numbers VALUES(4,44,"XYZ");
SELECT * FROM numbers;
]=]
local showrow = function(udata,cols,values,names)
assert(udata=='test_udata')

for i=1,cols do
cclog('%s |-> %s',names[i],values[i])
end

return 0
end
db:exec(sql,showrow,'test_udata')
end

--[[update hook]]
--表事件监听   eg:有在一张表插入数据,则读取更新数据
function sqlite3test:updateHook()
local sqlite3 = require("sqlite3")

local db = sqlite3.open_memory()

local optbl = {
[sqlite3.UPDATE] = "UPDATE";
[sqlite3.INSERT] = "INSERT";
[sqlite3.DELETE] = "DELETE"
}

setmetatable(optbl,
{
__index=function(t,n)
return string.format("Unknown op %d",n)
end
})

local udtbl = {0, 0, 0}

db:update_hook( function(ud, op, dname, tname, rowid)
cclog("Sqlite Update Hook: %s,%s,%s,%d", optbl[op], dname, tname, rowid)
end, udtbl)

db:exec[[
CREATE TABLE test ( id INTEGER PRIMARY KEY, content VARCHAR );

INSERT INTO test VALUES (NULL, 'Hello World');
INSERT INTO test VALUES (NULL, 'Hello Lua');
INSERT INTO test VALUES (NULL, 'Hello Sqlite3');
UPDATE test SET content = 'Hello Again World' WHERE id = 1;
DELETE FROM test WHERE id = 2;
]]

for row in db:nrows("SELECT * FROM test") do
cclog('%d %s', row.id, row.content)
end
end

return sqlite3tes
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息