【OC数据持久化】:SQLite数据库

目录
一、SQLite数据库
 1、基本使用
 2、事务
二、FMDB
 1、基本使用
 2、事务
 3、线程安全


一、SQLite数据库


1、基本使用

SQLite数据库是基于C实现的、移动端开发常用的、一款轻量级数据库,它占用的资源非常低,可能只需要几百K的内存就够了,它的处理速度非常快,比MySQL、PostgreSQL这两款著名的数据库都还快。

我们需要通过C函数和SQL语句的配合来实现SQLite数据库的开发。

常用的C函数有3个:

  • 打开数据库sqlite3_open
  • 执行SQL语句sqlite3_exec
  • 关闭数据库sqlite3_close

常用的SQL语句有6条:

  • 创建表CREATE TABLE

例如:CREATE TABLE IF NOT EXISTS 't_book' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'price' FLOAT NOT NULL, 'abstract' TEXT)

  • 删除表DROP TABLE

例如:DROP TABLE IF EXISTS 't_book'

  • 增数据INSERT INTO

例如:INSERT INTO 't_book' VALUES (NULL, '子夜', 46.0, '天亮之前,有一个时候是非常暗的,星也没有,月亮也没有')

  • 删数据DELETE FROM

例如:删除全部DELETE FROM 't_book',条件删除DELETE FROM 't_book' WHERE name = '子夜'

  • 改数据UPDATE

例如:修改全部UPDATE 't_book' SET price = 11.11,条件修改UPDATE 't_book' SET price = 1111.1 WHERE name = '子夜'

  • 查数据SELECT * FROM

例如:查询全部SELECT * FROM 't_book',条件查询SELECT * FROM 't_book' WHERE name = '子夜'

举个例子。

导入sqlite3动态库
-----------BookModel.h-----------

#import <UIKit/UIKit.h>

@interface BookModel : NSObject

@property (strong, nonatomic) NSString *name;
@property (assign, nonatomic) CGFloat price;
@property (strong, nonatomic) NSString *abstract;

@end


-----------BookModel.m-----------

#import "BookModel.h"

@implementation BookModel

- (NSString *)description {
    return [NSString stringWithFormat:@"%@ %.2f %@", self.name, self.price, self.abstract];
}

@end
-----------SQLiteViewController.h-----------

#import <UIKit/UIKit.h>

@interface SQLiteViewController : UIViewController

@end


-----------SQLiteViewController.m-----------

#import "SQLiteViewController.h"
#import <sqlite3.h>
#import "BookModel.h"

@interface SQLiteViewController ()

@end

@implementation SQLiteViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"%@", NSHomeDirectory());
        
    // 1、打开数据库(如果指定路径下的数据库存在则直接打开,不存在会创建一个再打开)
    sqlite3 *db = nil;
    NSString *sqliteDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"db.sqlite"];
    int openDBResult = sqlite3_open(sqliteDBPath.UTF8String, &db);
    if (openDBResult == SQLITE_OK) {
        NSLog(@"打开数据库成功");
    } else {
        NSLog(@"打开数据库失败:%d", openDBResult);
    }

    // 2、创建表
    NSString *creatTableWords = @"CREATE TABLE IF NOT EXISTS 't_book' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'price' FLOAT NOT NULL, 'abstract' TEXT)";
    int creatTableResult = sqlite3_exec(db, creatTableWords.UTF8String, nil, nil, nil);
    if (creatTableResult == SQLITE_OK) {
        NSLog(@"创建表成功");
    } else {
        NSLog(@"创建表失败:%d", creatTableResult);
    }

    // 3、增数据(主键传NULL就会自动增加,第一条数据会设置为1)
    BookModel *ziye = [[BookModel alloc] init];
    ziye.name = @"子夜";
    ziye.price = 46.0;
    ziye.abstract = @"天亮之前,有一个时候是非常暗的,星也没有,月亮也没有";
    NSString *insertDataWords = [NSString stringWithFormat:@"INSERT INTO 't_book' VALUES (NULL, '%@', %.2f, '%@')", ziye.name, ziye.price, ziye.abstract];
    int insertDataResult = sqlite3_exec(db, insertDataWords.UTF8String, nil, nil, nil);
    if (insertDataResult == SQLITE_OK) {
        NSLog(@"增数据成功");
    } else {
        NSLog(@"增数据失败:%d", insertDataResult);
    }

    // 4、删数据
    NSString *deleteDataWords = @"DELETE FROM 't_book' WHERE name = '子夜' and abstract != '天亮之前,有一个时候是非常暗的,星也没有,月亮也没有'";
    int deleteDataResult = sqlite3_exec(db, deleteDataWords.UTF8String, nil, nil, nil);
    if (deleteDataResult == SQLITE_OK) {
        NSLog(@"删数据成功");
    } else {
        NSLog(@"删数据失败:%d", deleteDataResult);
    }

    // 5、改数据
    NSString *updateDataWords = @"UPDATE 't_book' SET price = 11.11";
    int updateDataResult = sqlite3_exec(db, updateDataWords.UTF8String, nil, nil, nil);
    if (updateDataResult == SQLITE_OK) {
        NSLog(@"改数据成功");
    } else {
        NSLog(@"改数据失败:%d", updateDataResult);
    }

    // 6、查数据
    NSMutableArray *selectDataArray = [NSMutableArray array];
    sqlite3_stmt *stmt = nil; // 伴随指针
    NSString *selectDataWords = @"SELECT * FROM 't_book' WHERE id >= 3";
    int selectDataResult = sqlite3_prepare(db, selectDataWords.UTF8String, -1, &stmt, nil);
    if (selectDataResult == SQLITE_OK) {
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            BookModel *bookModel = [[BookModel alloc] init];
            bookModel.name = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)];
            bookModel.price = sqlite3_column_double(stmt, 2);
            bookModel.abstract = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 3)];

            [selectDataArray addObject:bookModel];
        }

        NSLog(@"查数据成功:%@", selectDataArray);
    } else {

        NSLog(@"查数据失败了:%d", selectDataResult);
    }
    sqlite3_finalize(stmt); // 释放伴随指针

    // 7、删除表
    NSString *dropTableWords = @"DROP TABLE IF EXISTS 't_book'";
    int dropTableResult = sqlite3_exec(db, dropTableWords.UTF8String, nil, nil, nil);
    if (dropTableResult == SQLITE_OK) {
        NSLog(@"删除表成功");
    } else {
        NSLog(@"删除表失败:%d", dropTableResult);
    }
    
    // 8、关闭数据库
    int closeDBResult = sqlite3_close(db);
    if (closeDBResult == SQLITE_OK) {
        NSLog(@"关闭数据库成功");
    } else {
        NSLog(@"关闭数据库失败:%d", closeDBResult);
    }
}

@end

2、事务

所谓事务就是指把多条SQL语句当作一个操作序列来执行,只有所有的SQL语句都执行成功这个操作序列才算执行成功,其中任意一条SQL语句执行这个操作序列就算执行失败,失败后我们可以把数据回滚到原始状态。比如张三给李四转钱,张三扣钱这个操作和李四加钱这个操作就应该当做一个操作序列来执行,只有张三扣钱成功 + 李四加钱成功同时发生这个操作才能算成功,如果不采用事务来做,那很可能出现张三扣钱成功了,李四加钱的时候刚好断网了,这样结果就是张三白白丢了钱。事务一般是以开启事务BEGIN TRANSACTION开始,提交事务COMMIT TRANSACTION或回滚事务ROLLBACK TRANSACTION结束。

举个例子。

-----------SQLiteViewController.h-----------

#import <UIKit/UIKit.h>

@interface SQLiteViewController : UIViewController

@end


-----------SQLiteViewController.m-----------

#import "SQLiteViewController.h"
#import <sqlite3.h>

@interface SQLiteViewController ()

@end

@implementation SQLiteViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"%@", NSHomeDirectory());
        
    // 1、打开数据库
    sqlite3 *db = nil;
    NSString *sqliteDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"db.sqlite"];
    int openDBResult = sqlite3_open(sqliteDBPath.UTF8String, &db);
    if (openDBResult == SQLITE_OK) {
        NSLog(@"打开数据库成功");
    } else {
        NSLog(@"打开数据库失败:%d", openDBResult);
    }

    // 2、创建表
    NSString *creatTableWords = @"CREATE TABLE IF NOT EXISTS 't_money' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'money' FLOAT NOT NULL)";
    int creatTableResult = sqlite3_exec(db, creatTableWords.UTF8String, nil, nil, nil);
    if (creatTableResult == SQLITE_OK) {
        NSLog(@"创建表成功");
    } else {
        NSLog(@"创建表失败:%d", creatTableResult);
    }

    // 3、增数据(主键传NULL就会自动增加,第一条数据会设置为1)
    NSString *insertDataWords1 = @"INSERT INTO 't_money' VALUES (NULL, '张三', 100)";
    int insertDataResult1 = sqlite3_exec(db, insertDataWords1.UTF8String, nil, nil, nil);
    if (insertDataResult1 == SQLITE_OK) {
        NSLog(@"增数据成功1");
    } else {
        NSLog(@"增数据失败1:%d", insertDataResult1);
    }

    NSString *insertDataWords2 = @"INSERT INTO 't_money' VALUES (NULL, '李四', 100)";
    int insertDataResult2 = sqlite3_exec(db, insertDataWords2.UTF8String, nil, nil, nil);
    if (insertDataResult2 == SQLITE_OK) {
        NSLog(@"增数据成功2");
    } else {
        NSLog(@"增数据失败2:%d", insertDataResult2);
    }
    
    //-----------开启事务-----------//
    NSString *beginTransactionWords = @"BEGIN TRANSACTION";
    sqlite3_exec(db, beginTransactionWords.UTF8String, nil, nil, nil);
    //-----------开启事务-----------//

    // 4、改数据
    NSString *updateDataWords1 = @"UPDATE 't_money' SET money = 90 WHERE name = '张三'";
    int updateDataResult1 = sqlite3_exec(db, updateDataWords1.UTF8String, nil, nil, nil);

    NSString *updateDataWords2 = @"UPDATE 't_money' SET money = 110 WHERE name = '李四'";
    int updateDataResult2 = sqlite3_exec(db, updateDataWords2.UTF8String, nil, nil, nil);

    if (updateDataResult1 == SQLITE_OK && updateDataResult2 == SQLITE_OK) { // 两个都改成功,才算成功
        NSLog(@"改数据成功");

        //-----------提交事务-----------//
        NSString *commitTransactionWords = @"COMMIT TRANSACTION";
        sqlite3_exec(db, commitTransactionWords.UTF8String, nil, nil, nil);
        //-----------提交事务-----------//
    } else { // 任意一个改失败,就算失败
        NSLog(@"改数据失败:%d %d", updateDataResult1, updateDataResult2);

        //-----------回滚事务-----------//
        NSString *rollbackTransactionWords = @"ROLLBACK TRANSACTION";
        sqlite3_exec(db, rollbackTransactionWords.UTF8String, nil, nil, nil);
        //-----------回滚事务-----------//
    }
    
    // 5、关闭数据库
    int closeDBResult = sqlite3_close(db);
    if (closeDBResult == SQLITE_OK) {
        NSLog(@"关闭数据库成功");
    } else {
        NSLog(@"关闭数据库失败:%d", closeDBResult);
    }
}

@end


二、FMDB


1、基本使用

FMDB是对SQLite数据库C语言API的OC封装,所以使用起来更加面向对象,省去了冗长的C语言API的编写。更重要的是FMDB提供了线程安全的数据库操作方法,能够有效地保证数据安全。

常用的类有3个:

  • FMDatabase:可以把它理解为一个数据库,一个FMDatabase对象就是一个单独的SQLite数据库
  • FMResultSet:FMDatabase查询后的结果集合
  • FMDatabaseQueue:用来在多线程中操作FMDatabase,保证数据库访问的线程安全

常用的OC方法有2个:

  • 查询操作使用executeQuery
  • 其它操作使用executeUpdate

常用的SQL语句当然还是上面的6条:

  • 创建表CREATE TABLE

例如:CREATE TABLE IF NOT EXISTS 't_book' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'price' FLOAT NOT NULL, 'abstract' TEXT)

  • 删除表DROP TABLE

例如:DROP TABLE IF EXISTS 't_book'

  • 增数据INSERT INTO

例如:INSERT INTO 't_book' VALUES (NULL, '子夜', 46.0, '天亮之前,有一个时候是非常暗的,星也没有,月亮也没有')

  • 删数据DELETE FROM

例如:删除全部DELETE FROM 't_book',条件删除DELETE FROM 't_book' WHERE name = '子夜'

  • 改数据UPDATE

例如:修改全部UPDATE 't_book' SET price = 11.11,条件修改UPDATE 't_book' SET price = 1111.1 WHERE name = '子夜'

  • 查数据SELECT * FROM

例如:查询全部SELECT * FROM 't_book',条件查询SELECT * FROM 't_book' WHERE name = '子夜'

举个例子。

-----------BookModel.h-----------

#import <UIKit/UIKit.h>

@interface BookModel : NSObject

@property (strong, nonatomic) NSString *name;
@property (assign, nonatomic) CGFloat price;
@property (strong, nonatomic) NSString *abstract;

@end


-----------BookModel.m-----------

#import "BookModel.h"

@implementation BookModel

- (NSString *)description {
    return [NSString stringWithFormat:@"%@ %.2f %@", self.name, self.price, self.abstract];
}

@end
-----------FMDBViewController.h-----------

#import <UIKit/UIKit.h>

@interface SQLiteViewController : UIViewController

@end


-----------FMDBViewController.m-----------

#import "FMDBViewController.h"
#import <FMDB.h>
#import "BookModel.h"

@interface FMDBViewController ()

@end

@implementation FMDBViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"%@", NSHomeDirectory());
    
    // 1、打开数据库(如果指定路径下的数据库存在则直接打开,不存在会创建一个再打开)
    NSString *sqliteDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"db.sqlite"];
    FMDatabase *db = [[FMDatabase alloc] initWithPath:sqliteDBPath];
    if ([db open]) {
        NSLog(@"打开数据库成功");
    } else {
        NSLog(@"打开数据库失败");
    }

    // 2、创建表
    NSString *creatTableWords = @"CREATE TABLE IF NOT EXISTS 't_book' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'price' FLOAT NOT NULL, 'abstract' TEXT)";
    if ([db executeUpdate:creatTableWords]) {
        NSLog(@"创建表成功");
    } else {
        NSLog(@"创建表失败");
    }

    // 3、增数据(主键传NULL就会自动增加,第一条数据会设置为1)
    BookModel *ziye = [[BookModel alloc] init];
    ziye.name = @"子夜";
    ziye.price = 46.0;
    ziye.abstract = @"11天亮之前,有一个时候是非常暗的,星也没有,月亮也没有";
    NSString *insertDataWords = [NSString stringWithFormat:@"INSERT INTO 't_book' VALUES (NULL, '%@', %.2f, '%@')", ziye.name, ziye.price, ziye.abstract];
    if ([db executeUpdate:insertDataWords]) {
        NSLog(@"增数据成功");
    } else {
        NSLog(@"增数据失败");
    }

    // 4、删数据
    NSString *deleteDataWords = @"DELETE FROM 't_book' WHERE name = '子夜' and abstract != '天亮之前,有一个时候是非常暗的,星也没有,月亮也没有'";
    if ([db executeUpdate:deleteDataWords]) {
        NSLog(@"删数据成功");
    } else {
        NSLog(@"删数据失败");
    }

    // 5、改数据
    NSString *updateDataWords = @"UPDATE 't_book' SET price = 11.11";
    if ([db executeUpdate:updateDataWords]) {
        NSLog(@"改数据成功");
    } else {
        NSLog(@"改数据失败");
    }

    // 6、查数据
    NSMutableArray *selectDataArray = [NSMutableArray array];
    NSString *selectDataWords = @"SELECT * FROM 't_book' WHERE id >= 3";
    FMResultSet *resultSet = [db executeQuery:selectDataWords];
    while ([resultSet next]) { // [resultSet next],代表在结果集合里面一行一行的查询,查到一条处理一条,直到没有
        BookModel *bookModel = [[BookModel alloc] init];
        bookModel.name = [resultSet stringForColumn:@"name"];
        bookModel.price = [resultSet doubleForColumn:@"price"];
        bookModel.abstract = [resultSet stringForColumn:@"abstract"];

        [selectDataArray addObject:bookModel];
    }
    [resultSet close]; // 关闭resultSet,不写的话会报warning

    for (BookModel *bookModel in selectDataArray) {
        NSLog(@"查数据成功:%@", bookModel);
    }
    
    // 7、删除表
    NSString *dropTableWords = @"DROP TABLE IF EXISTS 't_book'";
    if ([db executeUpdate:dropTableWords]) {
        NSLog(@"删除表成功");
    } else {
        NSLog(@"删除表失败");
    }
    
    // 8、关闭数据库
    if ([db close]) {
        NSLog(@"关闭数据库成功");
    } else {
        NSLog(@"关闭数据库失败");
    }
}

@end

2、事务

举个例子。

-----------FMDBViewController.h-----------

#import <UIKit/UIKit.h>

@interface SQLiteViewController : UIViewController

@end


-----------FMDBViewController.m-----------

#import "FMDBViewController.h"
#import <FMDB.h>
#import "BookModel.h"

@interface FMDBViewController ()

@end

@implementation FMDBViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"%@", NSHomeDirectory());
    
    // 1、打开数据库(如果指定路径下的数据库存在则直接打开,不存在会创建一个再打开)
    NSString *sqliteDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"db.sqlite"];
    FMDatabase *db = [[FMDatabase alloc] initWithPath:sqliteDBPath];
    if ([db open]) {
        NSLog(@"打开数据库成功");
    } else {
        NSLog(@"打开数据库失败");
    }

    // 2、创建表
    NSString *creatTableWords = @"CREATE TABLE IF NOT EXISTS 't_money' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'money' FLOAT NOT NULL)";
    if ([db executeUpdate:creatTableWords]) {
        NSLog(@"创建表成功");
    } else {
        NSLog(@"创建表失败");
    }

    // 3、增数据(主键传NULL就会自动增加,第一条数据会设置为1)
    NSString *insertDataWords1 = @"INSERT INTO 't_money' VALUES (NULL, '张三', 100)";
    if ([db executeUpdate:insertDataWords1]) {
        NSLog(@"增数据成功1");
    } else {
        NSLog(@"增数据失败1");
    }

    NSString *insertDataWords2 = @"INSERT INTO 't_money' VALUES (NULL, '李四', 100)";
    if ([db executeUpdate:insertDataWords2]) {
        NSLog(@"增数据成功2");
    } else {
        NSLog(@"增数据失败2");
    }

    // 4、改数据
    //-----------开启事务-----------//
    [db beginTransaction];
    //-----------开启事务-----------//

    NSString *updateDataWords1 = @"UPDATE 't_money' SET money = 90 WHERE name = '张三'";
    NSString *updateDataWords2 = @"UPDATE 't_money' SET money = 110 WHERE name = '李四'";

    if ([db executeUpdate:updateDataWords1] && [db executeUpdate:updateDataWords2]) { // 两个都改成功,才算成功
        NSLog(@"改数据成功");

        //-----------提交事务-----------//
        [db commit];
        //-----------提交事务-----------//
    } else { // 任意一个改失败,就算失败
        NSLog(@"改数据失败");

        //-----------回滚事务-----------//
        [db rollback];
        //-----------回滚事务-----------//
    }

    // 5、关闭数据库
    if ([db close]) {
        NSLog(@"关闭数据库成功");
    } else {
        NSLog(@"关闭数据库失败");
    }
}

@end

3、线程安全

线程安全的数据库操作方法非常简单,我们只需要在FMDatabase使用的基础上套一层FMDatabaseQueue、把所有的操作都放到dbQueue的block回调里就可以了。

我们把事务的例子搞成线程安全的。

-----------FMDBViewController.h-----------

#import <UIKit/UIKit.h>

@interface SQLiteViewController : UIViewController

@end


-----------FMDBViewController.m-----------

#import "FMDBViewController.h"
#import <FMDB.h>
#import "BookModel.h"

@interface FMDBViewController ()

@end

@implementation FMDBViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"%@", NSHomeDirectory());
    
    NSString *sqliteDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"db.sqlite"];
    FMDatabaseQueue *dbQueue = [FMDatabaseQueue databaseQueueWithPath:sqliteDBPath];
    [dbQueue inDatabase:^(FMDatabase *db) {
        // 1、创建表
        NSString *creatTableWords = @"CREATE TABLE IF NOT EXISTS 't_money' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'money' FLOAT NOT NULL)";
        if ([db executeUpdate:creatTableWords]) {
            NSLog(@"创建表成功");
        } else {
            NSLog(@"创建表失败");
        }
        
        // 2、增数据(主键传NULL就会自动增加,第一条数据会设置为1)
        NSString *insertDataWords1 = @"INSERT INTO 't_money' VALUES (NULL, '张三', 100)";
        if ([db executeUpdate:insertDataWords1]) {
            NSLog(@"增数据成功1");
        } else {
            NSLog(@"增数据失败1");
        }
        
        NSString *insertDataWords2 = @"INSERT INTO 't_money' VALUES (NULL, '李四', 100)";
        if ([db executeUpdate:insertDataWords2]) {
            NSLog(@"增数据成功2");
        } else {
            NSLog(@"增数据失败2");
        }
        
        // 3、改数据
        //-----------开启事务-----------//
        [db beginTransaction];
        //-----------开启事务-----------//
        
        NSString *updateDataWords1 = @"UPDATE 't_money' SET money = 90 WHERE name = '张三'";
        NSString *updateDataWords2 = @"UPDATE 't_money' SET money = 110 WHERE name = '李四'";
        
        if ([db executeUpdate:updateDataWords1] && [db executeUpdate:updateDataWords2]) { // 两个都改成功,才算成功
            NSLog(@"改数据成功");
            
            //-----------提交事务-----------//
            [db commit];
            //-----------提交事务-----------//
        } else { // 任意一个改失败,就算失败
            NSLog(@"改数据失败");
            
            //-----------回滚事务-----------//
            [db rollback];
            //-----------回滚事务-----------//
        }
    }];
}

@end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容