• SQLite学习心得


       SQLite是一款很有名气的小型开源跨平台数据库,作为目前最流行的开源嵌入式关系型数据库,在系统结构设计中正在扮演着越来越重要的角色。

       本文主要沿着 http://www.cppblog.com/weiym/archive/2012/10/16/193357.html 这篇文章里面的学习指导思想,来一步步学习SQLite,为的是快速在实际开发中利用好SQLite。

    1. SQLite的优缺点以及适用场合

    1. 1 优点

        轻量级:       与传统的C/S模式的数据库软件不同,它是进程内的数据库引擎,不存在客户端和服务器。使用SQLite,只需要带上一个几百K大小的动态库就可以:
        绿色组件:     SQLite的核心引擎本身不依赖第三方软件,即插即用。
        单一文件:      数据库中所有的信息(比如表、视图、触发器等)都保存在单个文件内,可以拷贝到其他地方,照用不误。
        跨平台 :        不仅仅支持主流操作系统(Windows/Linux),还支持很多小型嵌入式系统(Android,WP,Vxworks)。
        查询效率极高 : SQLite的API不区分当前数据库是保存在内存中还是在磁盘文件中,为了提高效率,可以切换为内存方式。只需要在开始时将数据库载入内存,读写完成后,再把内存数据库dump会磁盘文件上就可以,读写内存比读写磁盘快很多倍。  
        

    1.2 缺点

        并发访问的锁机制: SQLite在并发读写方面的性能一直不太理想,在有大量insert,update访问时,不建议使用。

       SQL标准支持不全 :

    1.3适用场合

        适合查询速度要求较高,内存占用较少的场合,尤其是嵌入式操作系统,如各种手机操作系统,低并发web(99.9%网站是低并发),php环境里原生支持SQLite,asp.net/.net winform里可以很方便的使用System.Data.SQLite.

        SQLite本身是C写的,自带的API接口也是C接口,所以用C/C++来使用是最直接的了。对于Java语言来说,可以通过SQLite的JDBC接口方式使用SQLite,对于Python,pysqlite是操作SQLite的首选,它已经是Python的标准库之一。

        SQLite使用的授权协议时Public Domain协议,可以大胆的使用。

    2. 使用SQLite

         有几种使用方式,一种是直接把sqlite源码嵌入到系统中去,另一种是通过dll等开发库来调用相关函数接口。下面先介绍源码嵌入的方式。

         首先去SQLite官网上面下载最新版的源码,http://www.sqlite.org/download.html,我下载的是sqlite-amalgamation-3080704.zip版本。

         解压此压缩包,得到如下几个文件:     image ,往你的项目里面添加上述文件(除了shell.c文件),然后编译就行。

         这样想想,如此强大的SQLite,真正起作用的,只有一个c文件和两个h文件,不由得佩服开发者。在sqlite3.c中实现了全部的SQLite库中的内容,sqlite3.h定义了sqlite3.c的接口函数,因此很容易整合进工程。

           然后介绍dll调用开发方式。

           在Windows平台下调用SQLite3的接口,可以在上述网站下载 “Precompiled Binariers For Windows”下面的sqlite-dll-win32-x86-XXXX.zip版本,解压后会得到两个文件,image ,def文件用来定义一个dll的API导出文件。我们可以根据dll和.def文件来生成一个lib文件,供程序调用。  

    3. SQLite的接口函数

        在SQLite中,有两个核心对象,分别为database_connectionprepared_statement

        database_connection对象sqlite3 是 通过sqllite3_open()函数创建并返回的,在应用程序使用任何其他SQLite接口函数之前,必须先调用该函数用于初始化数据库。

        prepared_statement对象 sqlite3_stmt,可以简单理解为 编译后的SQL语句,所有SQL语句执行相关的函数 都需要该对象作为输入参数用于完成指定SQL操作。

    1.     sqlite3_open             打开一个数据库连接,创建并且返回 sqlite3  对象
    2.     sqlite3_prepare_v2     将制定SQL语句转换成sqlite3_stmt对象
    3.     sqlite3_step              单步执行sqlite3_stmt对象
    4.     sqlite3_column           返回sqlite3_stmt所在行的指定column的值
    5.     sqlite_finalize            销毁sqlite3_stmt对象。所有sqlite3_stmt对象在使用完后必须销毁,防止内存泄露
    6.     sqlite3_close             关闭数据库连接,销毁sqlite3 对象
    7.     sqlite3_reset             让执行过的sqlite3_step()的sqlite3_stmt重新执行,相当于将游标返回到开始位置重新读取数据,执行效率比新建一个stmt高
    8.     sqlite3_bind               用于INSERT SQL,当同一个INSERT SQL要插入一系列数据时使用,每次sqlite3_step后需要重新bind数据
    9.     sqlite3_exec               封装了一系列执行函数,执行制定的SQL语句,并且将查询结果返回给回调函数。

             

    编程过程中需要注意的地方:

    1. 调用sqlite3_exec时,对于第五个参数,errmsg来说,如果sql语句执行出错,那么从内部实现上来看,有如下操作:

    image

    内部会调用sqlite3Malloc为pzErrMsg分配内存,并且将对应的错误信息拷贝到pzErrMsg中去,sqlite3_exec函数返回时,通过printf(“%s”,errmsg)来提示具体的错误信息,这里,还需要手动做释放错误提示的内存空间,否则会有内存泄露。

    2. sqlite3_exe的回调函数,触发条件为每次成功获取新行后,就会触发回调函数,也就是说,当前表中有多少行符合查询结果,那就调用多少次回调函数。

    image

    这是sqlite_exec中具体执行回调函数的语句,它嵌套在while(1)大循环里面,可以看出,如果回调函数返回值不为0的话,即使查询结果是存在的,sqlite_exec会将本次查询结果设置为SQLITE_ABORT,从而打印出错误的函数提示,它将停止后续的查询,直接退出sqlite_exe的while(1)大循环。所以,sqlite_exe中的回调函数,返回值一定要为0,和查询的结果和次数都无关。

    对于更新、删除、插入等不需要回调函数的操作,sqlite3_exec的第三、第四个参数可以传入0或者NULL

       为了防止垃圾数据,在加载数据库的时候,需要先删除表操作。

        const char *sql_drop_table="drop table if exists t";

        sqlite3_exec(db,sql_drop_table,0,0,&errmsg);

         如果不进行上述操作,同样的插入,每次会在之前表的数据基础上进行。

    3. sqlite3_exe中的第五个参数,传入类型是指向指针的指针,所以,如果errmsg定义成 char *errmsg,那么传入参数要写成&errmsg才符合要求。

    4. sqlite3_get_table函数用于非回调函数查询结果,这里面,第三个参数比较难理解,先记录如下。

    image

    pnRow为查询的有效结果的行数指针,pnColumn为查询的每行有多少列,pzErrmsg为错误提示信息。

    pazResult这个需要好好解释一下,它有三个*,它是一个一维数组,以下述的表为例子:

    image 要把,这么些内容全部存在 一维指针数组中,既高效又边界,SQLite中是这样来处理的。

    假设char **dbResult; sqlite3_get_table中传入的第二个参数值为 &dbResult,那么,根据上表的构造,select * 查询出来的结果应该是这样的,

    nRow = 3,代表结果有3行, nColumn = 2,代表每行有2个字段,dbResult为一维指针数组,共有【(nRow+1)*nColumn】个元素,其内存布局如下:

    image

    这样的布局,感觉很巧妙。dbResult的0~nColumn为字段名称,从nColumn索引开始才是真正的数据。

    还是一样,我们给进去的只是指针的指针,传出来这么多内容,肯定是需要释放对应的内存。无论非回调的查询是成功还是失败,都必须调用sqlite3_free_table来释放内存,防止内存泄露。

    4. 在编写sql语句时,建议设置一个局部较大的数组sqlcmd,采用snprintf(sql_cmd,"CREATE TABLE datapro(package INTEGER,offset
       INTEGER,lklen INTEGER,base INTEHER,link INTEGER,err INTEGER);");来格式化sql命令到数组中去,然后再传入函数。

    5. 在执行sqlite_step时,当返回值不为SQLLITE_ROW(此时,代表操作执行成功)时,最好要调用sqlite3_reset来复位内部虚拟机。这是legcy版本的step函数,官方推荐使用v2版本的众多接口函数。

    sqlite3_exe类的函数可以完成大部分数据库需求,但是对于插入、查询二进制数据,就无能为力了。对于此类需求,sqlite引入了sqlite3_stmt数据类型,它里面存储被SQLite 解析后的SQL语句内容,因此,可以插入二进制数据,对于表的字段类型为blob类型。

    参考链接:

    SQLite3 C/C++ 开发接口简介

    SQlite3使用总结

    SQLite的官方主页:
    http://www.sqlite.org/

    SQLite中文站:
    http://www.sqlite.com.cn/

    System.Data.SQLite:
    http://sqlite.phxsoftware.com/

    sql学习笔记之 嵌入式数据库(sqlite,firebird)
    http://www.cnblogs.com/ljzforever/archive/2010/03/09/1681453.html

  • 相关阅读:
    Java中的逆变与协变
    JAVA中使用DOM解析XML文件
    ReentrantLock的使用
    tomcat源码 Container
    tomcat源码 Connector
    tomcat源码 StandardService
    BlockingQueue队列
    tomcat源码 StandardServer
    tomcat源码 分析 Catalina
    tomcat整体架构
  • 原文地址:https://www.cnblogs.com/cherishui/p/4185219.html
Copyright © 2020-2023  润新知