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];

        }];

  • 相关阅读:
    17. Letter Combinations of a Phone Number
    16. 3Sum Closest
    15. 3Sum
    14. Longest Common Prefix
    13. Roman to Integer
    12. Integer to Roman
    11. Container With Most Water
    10. Regular Expression Matching
    9. Palindrome Number
    8. String to Integer (atoi)
  • 原文地址:https://www.cnblogs.com/lifengfneg/p/4773931.html
Copyright © 2011-2022 走看看