数据库基本使用和封装教程(基础版看我的就可以了)

赶在2019年春节放假前一天,发一最后一波文章,虽然有点凌乱,但绝对使用
大力我上一家公司是做即时聊天应用的,聊天功能不是走第三方的平台,如环信、网易云等,毕竟是应用的核心功能,所以还是掌握在自己手上比较靠谱(结果各种坑),当然还没到自定义协议那一高层次,我们使用的WebSocket,所以比如socket发消息的保证、socket重连、UI搭建等等都是需要自己从零开始做起,其中少不了重要的一环:聊天记录和用户数据的保存。
铺垫这么久了,是时候回归我们今天要讨论的话题:数据库

项目数据库管理是使用常用的第三方框架:FMDB,今天只讲一些常用的sql语法和功能,具体的代码说明已经在代码注释写了...请看代码

@interface ViewController ()
@property (nonatomic, copy, readwrite) NSString *fileName;
@property (nonatomic, strong, readwrite) FMDatabase *db;
@property (nonatomic, strong, readwrite) FMDatabaseQueue *queue;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 数据库路径
    NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *fileName = [doc stringByAppendingPathComponent:@"student.db"];
    self.fileName = fileName;
    NSLog(@"====%@",fileName);
}

#pragma mark - touch
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    [self insert];
//    [self update];
//    [self delete];
//    [self query];
    [self dataBaseQueueAction];
}

@end

1、数据库基本使用:增删改查

#pragma mark - FMDB 增删改查
- (void)baseUseDemo {
    /*
     三大类
     (1)FMDatabase
     一个FMDatabase对象就代表一个单独的SQLite数据库
     用来执行SQL语句
     (2)FMResultSet
     使用FMDatabase执行查询后的结果集
     (3)FMDatabaseQueue
     用于在多线程中执行多个查询或更新,它是线程安全的
     */
    
    
    // 获得数据库
    /*
     有三种情况
     (1)具体文件路径
       如果不存在会自动创建
     (2)空字符串@""
       会在临时目录创建一个空的数据库
       当FMDatabase连接关闭时,数据库文件也被删除
     (3)nil
       会创建一个内存中临时数据库,当FMDatabase连接关闭时,数据库会被销毁
     */
    FMDatabase *db = [FMDatabase databaseWithPath:self.fileName];
    
    // 打开数据库
    if ([db open]) {
        NSString *sqlString = @"CREATE TABLE IF NOT EXISTS t_student(id integer PRIMARY KEY AUTOINCREMENT,name text NOT NULL , age integer NOT NULL);";
        BOOL result = [db executeUpdate:sqlString];
        if (result) {
            NSLog(@"创表成功");
        }
        else{
            NSLog(@"创表失败");
        }
    }
    
    self.db = db;
}


#pragma mark - 数据库sql语句
// w3school sql 语句学习:http://www.w3school.com.cn/sql/sql_distinct.asp
/**
 插入数据
 */
- (void)insert {
    for (int i=0; i<10; i++) {
        NSString *name = [NSString stringWithFormat:@"liven-%d",arc4random_uniform(100)];
        [self.db executeUpdate:@"INSERT INTO t_student (name,age) VALUES (?,?)",name,@(arc4random_uniform(40))];
    }
}


/**
 删除
 */
- (void)delete {
    // 清空数据库表的数据(这只是删除所有行的数据,并不会对表的结构、属性和索性有影响)
    //(即使数据库没有这张表,删除也不会崩溃,只会报错)
    [self.db executeUpdate:@"DELETE FROM t_student"];
    
    // 删除某一行:DELETE FROM 表名称 WHERE 列名称 = 值
}


/**
 更新
 */
- (void)update {
    // 公式:UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
  [self.db executeUpdate:@"UPDATE t_student SET age=? WHERE name='大力'",@(18)];
}


/**
 查询
 */
- (void)query {
    FMResultSet *resultSet = [self.db executeQuery:@"SELECT * FROM t_student"];
    while ([resultSet next]) {
        int ID = [resultSet intForColumn:@"id"];
        NSString *name = [resultSet stringForColumn:@"name"];
        int age = [resultSet intForColumn:@"age"];
        NSLog(@"%d %@ %d",ID,name,age);
    }
}

2、批处理

#pragma mark - 批处理
- (void)lotOperation {
    NSString *creatSqlString = @"CREATE TABLE IF NOT EXISTS t_person (id integer PRINARY KEY AUTOINCREMENT,name text NOT NULL, age integer NOT NULL, age integer NOT NULL);"
    @"CREATE TABLE IF NOT EXISTS t_student (id integer PRINARY KEY AUTOINCREMENT,name text NOT NULL, age integer NOT NULL, age integer NOT NULL);";
    [self.queue inDatabase:^(FMDatabase * _Nonnull db) {
        [db executeStatements:creatSqlString];
    }];
}

3、线程安全databaseQueue

#pragma mark - dataBaseQueue线程安全
- (void)dataBaseQueueAction {
    
    /*
      备注:
        1、FMDatabaseQueue初始化时已将数据库打开和关闭封装好了,所以操作时不需要单独调用FMDatabase的open和close方法
        2、FMDatabaseQueue是基于同步串行队列保证数据库访问的安全性,所以不能叠加使用,避免死锁
     */
    
    // 获取数据库队列
    FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:self.fileName];
    [queue inDatabase:^(FMDatabase * _Nonnull db) {
        BOOL resutl = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_person (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL);"];
        if (resutl) {
            NSLog(@"创表成功");
        }
        else{
            NSLog(@"创表失败");
        }
    }];
    self.queue = queue;
    
    // 插入数据
    [self.queue inDatabase:^(FMDatabase * _Nonnull db) {
        [db executeUpdate:@"INSERT INTO t_person (name,age) VALUES (?,?);",@"wendingding",@(22)];
    }];
    
    // 查询
    [self.queue inDatabase:^(FMDatabase * _Nonnull db) {
        FMResultSet *resultSet = [db executeQuery:@"SELECT * FROM t_person"];
        while ([resultSet next]) {
            int ID = [resultSet intForColumn:@"id"];
            NSString *name = [resultSet stringForColumn:@"name"];
            int age = [resultSet intForColumn:@"age"];
            NSLog(@"%d %@ %d",ID,name,age);
        }
    }];
    
    /*
     备注:
     https://stackoverflow.com/questions/15720272/when-to-close-sqlite-database-using-fmdb
     数据库不需频繁的切换open和close两种状态,否则会造成不小的性能损耗(如cpu使用率),FMDB的作者也提到
     只有在需要更改数据库模式的时候才需要切换状态,即使是退到后台线程也是不需要关闭的,维持数据打开的状态就可以
     */
    
}

4、事务

    // 事务:所有任务执行完成后才再将结果一次性提交到数据库
    // 特点:要么全部成功,要么全部失败(如果中途出现问题,则会回滚)
    // 注意:开启事务比不开事务的耗时更少
    [self.queue inDatabase:^(FMDatabase * _Nonnull db) {
        // 开启事务
        [db beginTransaction];
        BOOL first = [db executeUpdate:@"INSERT INTO t_person (name,age) VALUES (?,?);",@"lucky",@(22)];
        BOOL second = [db executeUpdate:@"INSERT INTO t_person (name,age) VALUES (?,?);",@"james",@(22)];
        // 如果其中一个失败,则结束,数据回滚
        if (!first || !second) {
            [db rollback];
            return ;
        }
        // 提交事务
        [db commit];
    }];
    
    // 另外一种实现方式
    [self.queue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
        BOOL first = [db executeUpdate:@"INSERT INTO t_person (name,age) VALUES (?,?);",@"wendi",@(22)];
        BOOL second = [db executeUpdate:@"INSERT INTO t_person (name,age) VALUES (?,?);",@"davi",@(22)];
        if (!first || !second) {
            *rollback = YES;
            return ;
        }
    }];

5、判断某张表是否存在

#pragma mark - 判断某张表是否存在
- (void)chectDatabaseTableExists {
    /*
     备注:
        sqlite中有一个内k建表sqlite_master,这个表中存储这个所有自建表的表名称等信息
        可以通过:select * from sqlite_master 查看这个内建表的所有记录
     
     
        引申:判断指定的表是否存在,可以用如下语句
        select count(*) from sqlite_master wheretype='table' and name='要查询的表名'
        如果查询结果大于0,表示该表存在于数据库中,否则不存在
     
        详情:
        https://blog.csdn.net/aflyeaglenku/article/details/50884837
     */
}

6、检测字段是否存在(并且添加字段)

#pragma mark - 检测字段是否存在(并且添加字段)
- (void)checkDatabaseColumn {
    /*
        sqlite中并没有直接删除列和重命名列名称的sql语句,但是可以使用下面的思路实现同样的效果
        1、新建一个新表(新表的字段除了要删除的字段外,其余的都要)
        2、将旧表的值copy到新表
        3、删除旧表
        4、重命名表表名
     
        详情:
        https://blog.csdn.net/aflyeaglenku/article/details/50884837
     */
    
    [self.queue inDatabase:^(FMDatabase * _Nonnull db) {
        if ([db columnExists:@"phone" inTableWithName:@"t_person"]) {
            // 如果t_person表中存在phone这个字段,不需要操作
        }else{
            // 如果t_person表中不存在phone这个字段,插入列
            [db executeUpdate:@"ALTER TABLE t_person ADD phone text"];
        }
    }];
}

7、表迁移

#pragma mark - 表迁移
- (void)updateToNewTable {
    /*
     这功能主要是表设计结构变更的时候需要使用
     思路:
        重新创建一个表
        将旧表数据导入到新的表中
        删除旧表
     */
}

8、多表联查(重点:此处先介绍左连、右连、内连的概念)

#pragma mark - 多表连查 join
- (void)mulTable {
    /*
     左连接:左边有的,右边没有的为null
     右连接:左边没有的,右边有的为null
     内连接:显示左边右边共有的
     
     详情介绍:
     https://blog.csdn.net/wang0112233/article/details/78418698
     https://blog.csdn.net/plg17/article/details/78758593
     */
    
    // 实例:比如有两个表A和B,联查条件A.a字段与B.b字段相同
    NSLog(@"select A.*,B.* from A left join B on(A.a = B.b)");
}

9、创建索引

#pragma mark - 创建索引(有索引后查找速度更快速)
- (void)createIndex {
    /*
        备注:
        1、在不读取整个表的情况下,索引使数据库应用程序可以更快地查找数据。
        2、更新一个包含索引的表需要比更新一个没有索引的表更多的时间,这是由于索引本身也需要更新。
           因此,理想的做法是仅仅在常常被搜索的列(以及表)上面创建索引
     
        公式:CREATE INDEX index_name ON table_name (column_name)
        详情介绍:
        http://www.w3school.com.cn/sql/sql_create_index.asp
     */
    
    // 实例:创建一个简单的索引,名为 "PersonIndex",在 Person 表的 LastName 列:
    NSLog(@"CREATE INDEX PersonIndex ON Person(LastName)");
}

10、数据库的删除(清空)

#pragma mark - 数据库的删除(清空)
- (void)clearTable {
    /*
        1、删除表(表的结构、属性以及索引也会被删除)
        DROP TABLE表名称
     
        2、删除数据库
        DROP DATABASE数据库名称
     
        3、清空表内的数据,但并不删除表本身
        TRUNCATE TABLE表名称
     
     */
}

11、统计表的数据

#pragma mark - 统计表的数据
- (void)tableCount {
    
    // 统计某表(如Person)的数据量
    NSLog(@"SELECT COUNT(*) FROM Person");
    
    // 统计某表某列的数据量(如:Person中name的数量,备注:NULL不计入)
    NSLog(@"SELECT COUNT(name) FROM Person");

}

12、分页读取数据

#pragma mark - 分页读取数据
- (void)pageData {
    // 实例:分页获取表Person中数据,每页20条
    // 从第0行开始获取 一次获取20条
    NSLog(@"SELECT * FROM Person LIMIT 0,20");
    // 从第21行开始获取 一次获取20条
    NSLog(@"SELECT * FROM Person LIMIT 20,20");
    
}

13、数据写入和读出处理(如写入的内容包括一些特殊的转义字符,这些直接写入会导致失败,所以需要特殊处理)

#pragma mark - 数据写入和读出处理(如写入的内容包括一些特殊的转义字符,这些直接写入会导致失败,所以需要特殊处理)
- (NSString *)inDBReplaceStr:(NSString *)inStr {
    NSString *tempStr = inStr;
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"/" withString:@"//"];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"'" withString:@"''"];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"[" withString:@"/["];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"]" withString:@"/]"];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"%" withString:@"/%"];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"&" withString:@"/&"];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"_" withString:@"/_"];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"(" withString:@"/("];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@")" withString:@"/)"];
    return tempStr;
}


- (NSString *)outDBReplaceStr:(NSString *)outStr {
    NSString *tempStr = outStr;
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"//" withString:@"/"];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"''" withString:@"'"];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"/[" withString:@"["];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"/]" withString:@"]"];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"/%" withString:@"%"];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"/&" withString:@"&"];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"/_" withString:@"_"];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"/(" withString:@"("];
    tempStr = [tempStr stringByReplacingOccurrencesOfString:@"/)" withString:@")"];
    return tempStr;
}

数据库的封装

1923392-0d0f93d0d01e252b.png
image.png
1923392-3381a74739abb4e5.png
WeChat76c9c04b8403928e83515db8e716cc3c.png

完整DEMO

猜你喜欢

转载自blog.csdn.net/weixin_34365635/article/details/87590717