zoukankan      html  css  js  c++  java
  • iOS开发之数据存储之SQLite3(包括FMDB)

    1、概述

    SQLite3是一款开源的嵌入式关系型数据库,可移植性好、易使用、内存开销小。

    SQLite3是无类型的,意味着你可以保存任何类型的数据到任意表的任意字段中。比如下列的创表语句是合法的:

    create table t_person(name, age);

    为了保证可读性,建议还是把字段类型加上:

    create table t_person(name text, age integer);

    SQLite将数据划分为以下几种存储类型:

    integer : 整型值

    real : 浮点值

    text : 文本字符串

    blob : 二进制数据(比如文件)

    为了保持良好的编程规范、方便程序员之间的交流,编写建表语句的时候最好加上每个字段的具体类型

    iOS中使用SQLite3,首先要添加库文件libsqlite3.dylib和导入主头文件。

    2、增(创)表

    格式:

    (1)create table 表名 (字段名1 字段类型1, 字段名2 字段类型2, …) ;

    (2)create table if not exists 表名 (字段名1 字段类型1, 字段名2 字段类型2, …) ;

    例如:

    create table t_student (id integer, name text, age inetger, score real)

    3、删表

    格式:

    (1)drop table 表名 ;

    (2)drop table if exists 表名 ;

    例如:

    drop table t_student ;

    4、插入数据(insert

    格式:

    insert into 表名 (字段1, 字段2, …) values (字段1的值, 字段2的值, …) ;

    例如:

    insert into t_student (name, age) values (‘mj’, 10) ;

    【备注】数据库中的字符串内容应该用单引号括住

    5、更新数据(update

    格式:

    update 表名 set 字段1 = 字段1的值, 字段2 = 字段2的值, … ;

    例如:

    update t_student set name = ‘jack’, age = 20 ;

    【备注】上面的示例会将t_student表中所有记录的name都改为jack,age都改为20。

    6、删除数据(delete

    格式:

    delete from 表名 ;

    例如:

    delete from t_student ;

    【备注】上面的示例会将t_student表中所有记录都删掉

    7、条件语句

    如果只想更新或者删除某些固定的记录,那就必须在语句后加上一些条件。

    条件语句的常见格式:

    where 字段 = 某个值 ;   // 不能用两个 =

    where 字段 is 某个值 ;   // is 相当于 =

    where 字段 != 某个值 ;

    where 字段 is not 某个值 ;   // is not 相当于 !=

    where 字段 > 某个值 ;

    where 字段1 = 某个值 and 字段2 > 某个值 ;  // and相当于C语言中的 &&

    where 字段1 = 某个值 or 字段2 = 某个值 ;  //  or 相当于C语言中的 ||

    例如:

    delete from t_student where age <= 10 or age > 30 ;

    8、数据库查询(select

    格式:

    (1)select 字段1, 字段2, … from 表名 ;

    (2)select * from 表名;   //  查询所有的字段

    例如:

    select name, age from t_student ;

    select * from t_student ;

    select * from t_student where age > 10 ;  //  条件查询

    9、起别名

    格式(字段和表都可以起别名):

    (1)select 字段1 别名 , 字段2 别名 , … from 表名 别名 ;

    (2)select 字段1 别名, 字段2 as 别名, … from 表名 as 别名 ;

    (3)select 别名.字段1, 别名.字段2, … from 表名 别名 ;

    例如:

    select name myname, age myage from t_student ;

    给name起个叫做myname的别名,给age起个叫做myage的别名

    select s.name, s.age from t_student s ;

    给t_student表起个别名叫做s,利用s来引用表中的字段

    10、计算记录的数量

    格式:

    (1)select count (字段) from 表名 ;

    (2)select count ( * ) from 表名 ;

    例如:

    select count (age) from t_student ;

    select count ( * ) from t_student where score >= 60;

    11、排序

    查询出来的结果可以用order by进行排序:

    (1)select * from t_student order by 字段 ;

    例如:

    select * from t_student order by age ;

    默认是按照升序排序(由小到大),也可以变为降序(由大到小)

    (1)select * from t_student order by age desc ;  //降序

    (2)select * from t_student order by age asc ;   //升序(默认)

    也可以用多个字段进行排序:

    (1)select * from t_student order by age asc, height desc ;

    先按照年龄排序(升序),年龄相等就按照身高排序(降序)

    12limit

    使用limit可以精确地控制查询结果的数量,比如每次只查询10条数据。

    格式:

    select * from 表名 limit 数值1, 数值2 ;

    例如:

    select * from t_student limit 4, 8 ;

    可以理解为:跳过最前面4条语句,然后取8条记录

    limit常用来做分页查询,比如每页固定显示5条数据,那么应该这样取数据:

    第1页:limit 0, 5

    第2页:limit 5, 5

    第3页:limit 10, 5

    第n页:limit 5*(n-1), 5

    例如:

    select * from t_student limit 7 ;

    相当于select * from t_student limit 0, 7 ;

    表示取最前面的7条记录

    13、简单约束

    建表时可以给特定的字段设置一些约束条件,常见的约束有:

    not null :规定字段的值不能为null

    unique :规定字段的值必须唯一

    default :指定字段的默认值

    (建议:尽量给字段设定严格的约束,以保证数据的规范性)

    例如:

    create table t_student (id integer, name text not null unique, age integer not null default 1) ;

    name字段不能为null,并且唯一

    age字段不能为null,并且默认为1

    14、主键约束

    如果t_student表中就name和age两个字段,而且有些记录的name和age字段的值都一样时,那么就没法区分这些数据,造成数据库的记录不唯一,这样就不方便管理数据。

    良好的数据库编程规范应该要保证每条记录的唯一性,为此,增加了主键约束。

    也就是说,每张表都必须有一个主键,用来标识记录的唯一性。

    主键简介:

    主键(Primary Key,简称PK)用来唯一地标识某一条记录。

    例如t_student可以增加一个id字段作为主键,相当于人的身份证。

    主键可以是一个字段或多个字段。

    主键设计原则:

    主键应当是对用户没有意义的

    永远也不要更新主键

    主键不应包含动态变化的数据

    主键应当由计算机自动生成

    主键的声明:

    在创表的时候用primary key声明一个主键:

    create table t_student (id integer primary key, name text, age integer) ;

    integer类型的id作为t_student表的主键

    主键字段:

    只要声明为primary key,就说明是一个主键字段。

    主键字段默认就包含了not null 和 unique 两个约束。

    如果想要让主键自动增长(必须是integer类型),应该增加autoincrement:

    create table t_student (id integer primary key autoincrement, name text, age integer) ;

    15、外键约束

    利用外键约束可以用来建立表与表之间的联系。外键的一般情况是:一张表的某个字段,引用着另一张表的主键字段。

    新建一个外键:

    create table t_student (id integer primary key autoincrement, name text, age integer, class_id integer, constraint fk_t_student_class_id_t_class_id foreign key (class_id) (id)) ; references t_class

    t_student表中有一个叫做fk_t_student_class_id_t_class_id的外键

    这个外键的作用是用t_student表中的class_id字段引用t_class表的id字段

    16、表连接查询

    什么是表连接查询:

    需要联合多张表才能查到想要的数据

    表连接的类型(不要求掌握):

    (1)内连接:inner join 或者 join  (显示的是左右表都有完整字段值的记录)

    (2)左外连接:left outer join (保证左表数据的完整性)

    例如:

    查询0316iOS班的所有学生:

    select s.name,s.age from t_student s, t_class c where s.class_id = c.id and c.name = ‘0316iOS’;

    17、创建、打开、关闭数据库(创建数据库)

    创建或打开数据库

    // path为:~/Documents/person.db

    sqlite3 *db;

    int result = sqlite3_open([path UTF8String], &db); //注意传入的是db地址

    代码解析:

    (1)sqlite3_open()将根据文件路径打开数据库,如果不存在,则会创建一个新的数据库。如果result等于常量SQLITE_OK,则表示成功打开数据库。

    (2)sqlite3 *db:一个打开的数据库实例

    (3)数据库文件的路径必须以C字符串(而非NSString)传入,[path UTF8String]就是将NSString字符串转成C语言的Char型字符串。等价于:path.UTF8String

    关闭数据库:

    sqlite3_close(db);

    18、执行不返回数据的SQL语句(在数据库中创建表/删除表/更新表)

    执行创表语句:

    char *errorMsg;  // 用来存储错误信息

    char *sql = "create table if not exists t_person(id integer primary key autoincrement, name text, age integer);";

    int result = sqlite3_exec(db, sql, NULL, NULL, &errorMsg);

    代码解析:

    sqlite3_exec()可以执行任何SQL语句,比如创表、更新、插入和删除操作。但是一般不用它执行查询语句,因为它不会返回查询到的数据。如果result等于常量SQLITE_OK,表示创表成功。

    sqlite3_exec()还可以执行的语句:

    ①    开启事务:begin transaction;

    ②    回滚事务:rollback;

    ③    提交事务:commit;

    19、带占位符插入数据(可以防sql注入)

    char *sql = "insert into t_person(name, age) values(?, ?);";

    sqlite3_stmt *stmt;

    if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {

        sqlite3_bind_text(stmt, 1, "母鸡", -1, NULL);

        sqlite3_bind_int(stmt, 2, 27);

    }

    if (sqlite3_step(stmt) != SQLITE_DONE) {

        NSLog(@"插入数据错误");

    }

    sqlite3_finalize(stmt);

    代码解析:

    sqlite3_prepare_v2()返回值等于SQLITE_OK,说明SQL语句已经准备成功,没有语法问题。

    sqlite3_bind_text():大部分绑定函数都只有3个参数:

    (1)第1个参数是sqlite3_stmt *类型

    (2)第2个参数指占位符的位置,第一个占位符的位置是1,不是0

    (3)第3个参数指占位符要绑定的值

    (4)第4个参数指在第3个参数中所传递数据的长度,对于C字符串,可以传递-1代替字符串的长度

    (5)第5个参数是一个可选的函数回调,一般用于在语句执行后完成内存清理工作

    sqlite_step():执行SQL语句,返回SQLITE_DONE代表成功执行完毕

    sqlite_finalize():销毁sqlite3_stmt *对象

    20、查询数据(查询表数据)

    char *sql = "select id,name,age from t_person;";

    sqlite3_stmt *stmt;//存放结果集

    if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {//返回SQLITE_OK代表sql语句没有错误

        while (sqlite3_step(stmt) == SQLITE_ROW) {//返回SQLITE_ROW说明查询到了数据

            int _id = sqlite3_column_int(stmt, 0);//id

            char *_name = (char *)sqlite3_column_text(stmt, 1); //name

            NSString *name = [NSString stringWithUTF8String:_name];

            int _age = sqlite3_column_int(stmt, 2); //age

            NSLog(@"id=%i, name=%@, age=%i", _id, name, _age);

        }

    }

    sqlite3_finalize(stmt);//关闭结果集

    代码解析:

    (1)sqlite3_step()返回SQLITE_ROW代表遍历到一条新记录

    (2)sqlite3_column_*()用于获取每个字段对应的值,第2个参数是字段的索引,从0开始

    21FMDB

    1)概述

    FMDB是iOS平台的SQLite数据库框架。

    FMDB以OC的方式封装了SQLite的C语言API。

    FMDB的优点:

    使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码。

    对比苹果自带的Core Data框架,更加轻量级和灵活。

    提供了多线程安全的数据库操作方法,有效地防止数据混乱。

    FMDBgithub地址:

    https://github.com/ccgus/fmdb

    点击查看本地保存的FMDB框架

    2FMDB有三个主要的类

    FMDatabase

    一个FMDatabase对象就代表一个单独的SQLite数据库。

    用来执行SQL语句。

    FMResultSet

    使用FMDatabase执行查询后的结果集。

    FMDatabaseQueue

    用于在多线程中执行多个查询或更新,它是线程安全的。

    3)打开数据库

    通过指定SQLite数据库文件路径来创建FMDatabase对象

    FMDatabase *db = [FMDatabase databaseWithPath:path];

    if (![db open]) {

        NSLog(@"数据库打开失败!");

    }

    文件路径有三种情况:

    (a)具体文件路径。

    如果不存在会自动创建。

    (b)空字符串@""

    会在临时目录创建一个空的数据库。

    当FMDatabase连接关闭时,数据库文件也被删除。

    (c)nil

    会创建一个内存中临时数据库,当FMDatabase连接关闭时,数据库会被销毁。

    4)执行更新

    在FMDB中,除查询以外的所有操作,都称为“更新”:

    create、drop、insert、update、delete等。

    使用executeUpdate:方法执行更新:

    - (BOOL)executeUpdate:(NSString*)sql, ...

    - (BOOL)executeUpdateWithFormat:(NSString*)format, ...

    - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments

    例如:

    [db executeUpdate:@"UPDATE t_student SET age = ? WHERE name = ?;", @20, @"Jack"]

    例如,打开数据库并创建新表:

    @property (nonatomic, strong) FMDatabase *db;

    实现代码:

    - (void)viewDidLoad

    {

        [super viewDidLoad];

       

        // 0.获得沙盒中的数据库文件名

    NSString *filename

     = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:

    @"student.sqlite"];

       

        // 1.创建数据库实例对象

        self.db = [FMDatabase databaseWithPath:filename];

      

        // 2.打开数据库

        if ( [self.db open] ) {

            NSLog(@"数据库打开成功");

            // 创表

            BOOL result = [self.db executeUpdate:@"create table if not exists t_student (id integer primary key autoincrement, name text, age integer);"];

            if (result) {

                NSLog(@"创表成功");

            } else {

                NSLog(@"创表失败");

            }

        } else {

            NSLog(@"数据库打开失败");

        }

    }

    5)执行查询

    查询方法:

    - (FMResultSet *)executeQuery:(NSString*)sql, ...

    - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...

    - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:

    (NSArray *)arguments

    例如:

    // 查询数据

    FMResultSet *rs = [db executeQuery:@"SELECT * FROM t_student"];

    // 遍历结果集

    while ([rs next]) {

        NSString *name = [rs stringForColumn:@"name"];

        int age = [rs intForColumn:@"age"];

        double score = [rs doubleForColumn:@"score"];

    }

    【备注】关闭数据库[db close];

    6FMDatabaseQueue

    FMDatabase这个类是线程不安全的,如果在多个线程中同时使用一个FMDatabase实例,会造成数据混乱等问题。

    为了保证线程安全,FMDB提供方便快捷的FMDatabaseQueue类。

    FMDatabaseQueue的创建:

    FMDatabaseQueue *queue =

    [FMDatabaseQueue databaseQueueWithPath:path];

     [queue inDatabase:^(FMDatabase *db) {//从这个block中直接获得打开的数据库实例

        [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"];

        [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"];

        [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jim"];

       

        FMResultSet *rs = [db executeQuery:@"select * from t_student"];

        while ([rs next]) {

            // …

        }

    }];

    7FMDatabaseQueue使用事务

    [queue inTransaction:^(FMDatabase *db, BOOL *rollback) {

        [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"];

        [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"];

        [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jim"];

    if(发现情况不对)

    {

               *rollback = YES//回滚事务

    }

        FMResultSet *rs = [db executeQuery:@"select * from t_student"];

        while ([rs next]) {

            // …

        }

    }];

    当然也可以使用如下方法:

    [self.queue inDatabase:^(FMDatabase *db) {

            // 开启事务

            [db executeUpdate:@"begin transaction;"];//等价于[db beginTransaction];

            [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];

            [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];

           

            if (发现情况不对){

                // 回滚事务

               [db executeUpdate:@"rollback transaction;"];//等价于[db rollback];

           }

           

            [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];

           

            // 提交事务

           [db executeUpdate:@"commit transaction;"];//等价于[db commit];

        }];

  • 相关阅读:
    post请求返回404
    启动网关服务报错 Unable to find GatewayFilterFactory with name RequestRateLimiter
    数据库远程连接linux报错2003 can't connect to MySQL server on ip (0) 防火墙没有开放端口3306
    idea下maven项目打包部署到tomcat服务器
    修改Idea背景色
    20种源代码测试工具
    作为一名测试工程师,需要具备哪些能力
    Android自动化测试工具——monkey简介及入门
    appium关于定位元素
    appium testcase2
  • 原文地址:https://www.cnblogs.com/lifengfneg/p/4773931.html
Copyright © 2011-2022 走看看