数据库之SQLite(平凡之路)

什么是SQLite?
数据库存储数据的步骤

●SQLite是一款轻型的嵌入式数据库,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了.它的处理速度比Mysql、PostgreSQL这两款著名的数据库都还快

什么是数据库?

●数据库(Database)是按照数据结构来组织、存储和管理数据的仓库

●数据库可以分为2大种类: 关系型数据库(主流), 对象型数据库

●常用关系型数据库:

     PC端:Oracle、MySQL、SQL Server、Access、DB2、Sybase

     嵌入式\移动客户端:SQLite

如何存储数据?

数据库的存储结构和excel很像,以表(table)为单位.

数据库存储数据的步骤 :

1. 新建一张表(table); 2. 添加多个字段(column,列,属性);  3. 添加多行记录(row,record,每行存放多个字段对应的值)


SQL语句

如何在程序运行过程中操作数据库中的数据,那得先学会使用SQL语句

●什么是SQL?

      ●SQL(structured query language):结构化查询语言

       ●SQL是一种对关系型数据库中的数据进行定义和操作的语言

       ●SQL语言简洁,语法简单,好学好用

●什么是SQL语句?

     ●使用SQL语言编写出来的句子\代码,就是SQL语句

      ●在程序运行过程中,要想操作(增删改查,CRUD)数据库中的数据,必须使用SQL语句

●SQL语句的特点 :

  1. 不区分大小写(比如数据库认为user和UsEr是一样的);      2. 每条语句都必须以分号;结尾

●SQL中的常用关键字有

●select、insert、update、delete、from、create、where、desc、order、by、group、

table、alter、view、index等等

●数据库中不可以使用关键字来命名表、字段

SQL语句的种类

●数据定义语句: (DDL:Data Definition Language)

      包括create和drop等操作;   例如:在数据库中创建新表或删除表(create table或drop table)

●数据操作语句(DML:Data Manipulation Language)

     包括insert、update、delete等操作(添加、修改(更新)、删除表中的数据)

●数据查询语句(DQL:Data Query Language)

     可以用于查询获得表中的数据,  关键字select是DQL(也是所有SQL)用得最多的操作

     其他DQL常用的关键字有where,order by,group by和having

创表

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

示例: 

create table if not exists t_student (id integer, name text, age inetger, score real) ;

  字段类型

一般数据采用的固定的静态数据类型,而SQLite采用的是动态数据类型,会根据存入值自动判断。SQLite的数据存储类型有: integer (有符号整形), real (浮点值,以8字节IEEE浮点数存放), text(文本字符串,使用数据库编码UTF-8,UTF-16BE或者UTF-16LE存放), blob(是一个数据块,完全按照输入存放; 二进制数据,比如: 文件) 布尔类型 Sqlite没有单独的布尔存储类型,它使用INTEGER作为存储类型,0为false,1为true

从上可以看出存储类比数据类型更一般化。比如INTEGER存储类,包括6中不同长度的不同整形数据类型,这在磁盘上造成了差异。但是只要INTEGER值被从磁盘读出进入到内存进行处理,它们被转换成最一般的数据类型(8-字节有符号整形)。

SQLite是无类型的. 这意味着你可以保存任何类型的数据到你所想要保存的任何表的任何列中, 无论这列声明的数据类型是什么(只有自动递增Integer Primary Key才有用). 对于SQLite来说对字段不指定类型是完全有效的.

建表时声明啥类型或者不声明类型都可以,也就意味着创表语句可以这么写:

create table if not exists  t_student(name, age);

CREATE TABLE ex2( a   VARCHAR(10),   b  NVARCHAR(15),  c   TEXT,  d   INTEGER,e   FLOAT,f    BOOLEAN,g     CLOB,h     BLOB,i    TIMESTAMP,j    NUMERIC(10,5),k    VARYING   CHARACTER (24),l    NATIONAL   VARYING   CHARACTER(16));

注意: 为了保持良好的编程规范、方便程序员之间的交流,编写建表语句的时候最好加上每个字段的具体类型(否则容易挨揍,哈哈)

额外了解:

char、varchar、text和nchar、nvarchar、ntext的区别

1、CHAR。CHAR存储定长数据很方便,CHAR字段上的索引效率级高,比如定义char(10),那么不论你存储的数据是否达到了10个字节,都要占去10个字节的空间,不足的自动用空格填充。

2、VARCHAR。存储变长数据,但存储效率没有CHAR高。如果一个字段可能的值是不固定长度的,我们只知道它不可能超过10个字符,把它定义为VARCHAR(10)是最合算的。VARCHAR类型的实际长度是它的值的实际长度+1。为什么“+1”呢?这一个字节用于保存实际使用了多大的长度。从空间上考虑,用varchar合适;从效率上考虑,用char合适,关键是根据实际情况找到权衡点。3、TEXT。text存储可变长度的非Unicode数据,最大长度为2^31-1(2,147,483,647)个字符。4、NCHAR、NVARCHAR、NTEXT。这三种从名字上看比前面三种多了个“N”。它表示存储的是Unicode数据类型的字符。我们知道字符中,英文字符只需要一个字节存储就足够了,但汉字众多,需要两个字节存储,英文与汉字同时存在时容易造成混乱,Unicode字符集就是为了解决字符集这种不兼容的问题而产生的,它所有的字符都用两个字节表示,即英文字符也是用两个字节表示。nchar、nvarchar的长度是在1到4000之间。和char、varchar比较起来,nchar、nvarchar则最多存储4000个字符,不论是英文还是汉字;而char、varchar最多能存储8000个英文,4000个汉字。可以看出使用nchar、nvarchar数据类型时不用担心输入的字符是英文还是汉字,较为方便,但在存储英文时数量上有些损失。

所以一般来说,如果含有中文字符,用nchar/nvarchar,如果纯英文和数字,用char/varchar。

删表

格式 : drop table if exists表名;

示例:     drop  table   if  exists   t_student ;

插入数据(insert)

格式:   insert into表名(字段1,字段2, ...)values(字段1的值,字段2的值, ...) ;

示例 :

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

注意:  数据库中的字符串内容应该用单引号’括住

更新数据(update)

格式: update表名set字段1=字段1的值,字段2=字段2的值, ... ;

示例:

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

注意:  上面的示例会将t_student表中所有记录的name都改为jack,age都改为20

删除数据(delete)

格式: delete from 表名;

示例:    delete from   t_student ;

注意:     上面的示例会将t_student表中所有记录都删掉

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

条件语句格式:

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

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

      where 字段   != 某个值

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

      where 字段  > 某个值;

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

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

     条件语句练习

示例: 将t_student表中年龄大于10并且姓名不等于jack的记录,年龄都改为5

update t_student set age  = 5   where  age >10   and   name !=   'jack'; 

删除t_student表中年龄小于等于10或者年龄大于30的记录

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

猜猜下面语句的作用

update   t_student  set  score = age where  name=‘jack’ ; // (将t_student表中名字等于jack的记录,score字段的值 都改为age字段的值)


DQL语句

格式:  select字段1,字段2, ...from表名;     //查询表中的某些个字段

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

●示例:  

       select age ,name from  t_student;

        select * from  t_studnet;

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


起别名:

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

      select 字段1 别名, 字段2 别名, ......from  表名 别名;

       select字段1别名,字段2as别名,...from表名as别名;

       select别名.字段1,别名.字段2, ...from表名 别名;

示例:

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

          select name myname,age myage from t_student;

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

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

计算记录的数量

格式: select count (字段) from 表名;  // 某个字段记录的数据数量

          select  count (*) from 表名;  //

示例 :  select count(age) from  t_student ;

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

排序

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

        select  * from  t_student  order by  字段;

        select *from  t_student   order by  age ;

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

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

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

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

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

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

limit

使用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

简单约束

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

        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

主键约束

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

●良好的数据库编程规范应该要保证每条记录的唯一性,为此,增加了主键约束,也就是说,每张表都必须有一个主键,用来标识记录的唯一性

●什么是主键

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

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

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

主键的设计原则

●主键应当是对用户没有意义的●永远也不要更新主键

●主键不应包含动态变化的数据●主键应当由计算机自动生成

主键的声明

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

create   table   if   not   exists   t_student (id integer primary key  autoincrement, name text, age integer );   //integer类型的id作为t_student表的主键

外键约束

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

新建一个外键

create table if not exists t_student (id integer primary key autoincrement, name text, age integer, class_id integer,constraint  fk_student_class   foreign key   (class_id)  references   t_class (id));    //t_student表中有一个叫做fk_t_student_class_id_t_class_id的外键,  这个外键的作用是用t_student表中的class_id字段引用t_class表的id字段

表连接查询

什么是表连接查询?     --->   需要联合多张表才能查到想要的数据

表连接的类型:  

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

        左外连接: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’;



                                      SQLite3编码

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

创建、打开、关闭数据库

●创建或打开数据库

// path是数据库文件的存放路径

sqlite3*db = NULL;   // sqlite3 *db:一个打开的数据库实例

int result =sqlite3_open([path UTF8String], &db); //sqlite3_open()将根据文件路径打开数据库,如果不存在,则会创建一个新的数据库。如果result等于常量SQLITE_OK,则表示成功打开数据库;  UTF8String 数据库文件的路径必须以C字符串(而非NSString)传入

●关闭数据库:sqlite3_close(db);

执行不返回数据的SQL语句

执行创表语句

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

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语句,比如创表、更新、插入和删除操作。但是一般不用它执行查询语句,因为它不会返回查询到的数据

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

(1)开启事务:begin transaction;

(2)回滚事务:rollback;

(3)提交事务:commit;

带占位符插入数据

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

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

if (sqlite3_step(stmt) !=SQLITE_DONE) {

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

}sqlite3_finalize(stmt);

带占位符插入数据

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 *对象

查询数据

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

sqlite3_stmt*stmt;

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

while (sqlite3_step(stmt) ==SQLITE_ROW) {

int _id =sqlite3_column_int(stmt,0);

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

NSString *name = [NSStringstringWithUTF8String:_name];

int _age =sqlite3_column_int(stmt,2);

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

}

}

sqlite3_finalize(stmt);

●代码解析

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

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



下面以具体的代码演示 SQLite的使用

#import "HMViewController.h"
#import <sqlite3.h>
#import "HMShop.h"
@interface HMViewController () <UITableViewDataSource,UITableViewDelegate, UISearchBarDelegate>

@property (weak, nonatomic) IBOutlet UITextField *nameField;
@property (weak, nonatomic) IBOutlet UITextField *priceField;

/** 数据库对象实例 */

@property (nonatomic, assign) sqlite3 *db;

@property (weak, nonatomic) IBOutlet UITableView *tableView;

- (IBAction)insert;

@property (nonatomic, strong) NSMutableArray *shops;

@end

@implementation HMViewController

- (NSMutableArray *)shops{

     if (!_shops) {

          self.shops = [[NSMutableArray alloc] init];

       }

       return _shops;

}

- (void)viewDidLoad{

        [super viewDidLoad];

        // 增加搜索框

          UISearchBar *searchBar = [[UISearchBar alloc] init];

           searchBar.frame = CGRectMake(0, 0, 320, 44);

           searchBar.delegate = self;

              self.tableView.tableHeaderView = searchBar;

        // 初始化数据库

               [self setupDb];

      // 查询数据

          [self setupData];

         // 关闭数据库

        //    sqlite3_close();

}

#pragma mark - UISearchBarDelegate

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{

[self.shops removeAllObjects];

NSString *sql = [NSString stringWithFormat:@"SELECT name,price FROM t_shop WHERE name LIKE '%%%@%%' OR  price LIKE '%%%@%%' ;", searchText, searchText];

// stmt是用来取出查询结果的

sqlite3_stmt *stmt = NULL;

// 准备

int status = sqlite3_prepare_v2(self.db, sql.UTF8String, -1, &stmt, NULL);

if (status == SQLITE_OK) { // 准备成功 -- SQL语句正确

while (sqlite3_step(stmt) == SQLITE_ROW) { // 成功取出一条数据

const char *name = (const char *)sqlite3_column_text(stmt, 0);

const char *price = (const char *)sqlite3_column_text(stmt, 1);

HMShop *shop = [[HMShop alloc] init];

shop.name = [NSString stringWithUTF8String:name];

shop.price = [NSString stringWithUTF8String:price];

[self.shops addObject:shop];

}

}

[self.tableView reloadData];

}

/**

查询数据

*/

- (void)setupData{

const char *sql = "SELECT name,price FROM t_shop;";

// stmt是用来取出查询结果的

sqlite3_stmt *stmt = NULL;

// 准备

int status = sqlite3_prepare_v2(self.db, sql, -1, &stmt, NULL);

if (status == SQLITE_OK) { // 准备成功 -- SQL语句正确

while (sqlite3_step(stmt) == SQLITE_ROW) { // 成功取出一条数据

const char *name = (const char *)sqlite3_column_text(stmt, 0);

const char *price = (const char *)sqlite3_column_text(stmt, 1);

HMShop *shop = [[HMShop alloc] init];

shop.name = [NSString stringWithUTF8String:name];

shop.price = [NSString stringWithUTF8String:price];

[self.shops addObject:shop];

}

}

}

/**

初始化数据库

*/

- (void)setupDb

{

// 打开数据库(连接数据库)

NSString *filename = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"shops.sqlite"];

// 如果数据库文件不存在, 系统会自动创建文件自动初始化数据库

int status = sqlite3_open(filename.UTF8String, &_db);

if (status == SQLITE_OK) { // 打开成功

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

// 创表

const char *sql = "CREATE TABLE IF NOT EXISTS t_shop (id integer PRIMARY KEY, name text NOT NULL, price real);";

char *errmsg = NULL;

sqlite3_exec(self.db, sql, NULL, NULL, &errmsg);

if (errmsg) {

NSLog(@"创表失败--%s", errmsg);

}

} else { // 打开失败

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

}

}

#pragma mark 添加数据

- (IBAction)insert {

NSString *sql = [NSString stringWithFormat:@"INSERT INTO t_shop(name, price) VALUES ('%@', %f);", self.nameField.text, self.priceField.text.doubleValue];

sqlite3_exec(self.db, sql.UTF8String, NULL, NULL, NULL);

// 刷新表格

HMShop *shop = [[HMShop alloc] init];

shop.name = self.nameField.text;

shop.price = self.priceField.text;

[self.shops addObject:shop];

[self.tableView reloadData];

}

#pragma mark - 数据源方法

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{

return self.shops.count;

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

static NSString *ID = @"shop";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

if (!cell) {

cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];

cell.backgroundColor = [UIColor grayColor];

}

HMShop *shop = self.shops[indexPath.row];

cell.textLabel.text = shop.name;

cell.detailTextLabel.text = shop.price;

return cell;

}

@end




                                        SQLite函数总结

1.打开数据库

int sqlite3_open(

const char *filename,  // 数据库的文件路径

sqlite3 **ppDb          // 数据库实例

);

2.执行任何SQL语句

int sqlite3_exec(

sqlite3*,                                  // 一个打开的数据库实例

const char *sql,                          // 需要执行的SQL语句

int (*callback)(void*,int,char**,char**),  // SQL语句执行完毕后的回调

void *,                                    // 回调函数的第1个参数

char **errmsg                              // 错误信息

);

3.检查SQL语句的合法性(查询前的准备)

int sqlite3_prepare_v2(

sqlite3 *db,            // 数据库实例

const char *zSql,      // 需要检查的SQL语句

int nByte,              // SQL语句的最大字节长度

sqlite3_stmt **ppStmt,  // sqlite3_stmt实例,用来获得数据库数据

const char **pzTail

);

4.查询一行数据

int sqlite3_step(sqlite3_stmt*); // 如果查询到一行数据,就会返回SQLITE_ROW

5.利用stmt获得某一字段的值(字段的下标从0开始)

double sqlite3_column_double(sqlite3_stmt*, int iCol);  // 浮点数据

int sqlite3_column_int(sqlite3_stmt*, int iCol); // 整型数据

sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); // 长整型数据

const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); // 二进制文本数据

const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);  // 字符串数据


错误之处欢迎,

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

推荐阅读更多精彩内容