從使用到源碼—GreenDao(基本使用篇)

引言

  • 在Android應(yīng)用開發(fā)過程中,幾乎每個(gè)應(yīng)用都需要使用到數(shù)據(jù)庫栅炒,而直接使用系統(tǒng)的Sqlite存儲數(shù)據(jù)的話又不是加密的,并且當(dāng)我們需要插入多種類型的Entity時(shí),就需要編寫多少種增刪改查的SQL語句沃疮,然后拼接或者反射轉(zhuǎn)換,繁瑣程度只有寫過的老哥才知道梅肤。因此司蔬,Ummm..為了不寫到吐血且保證一定的數(shù)據(jù)安全性,咱還是選擇一個(gè)能加密數(shù)據(jù)庫并且能簡潔使用的ORM吧姨蝴。
  • 為啥用GreenDao呢俊啼?宣傳描述可查看官網(wǎng)介紹 GreenDao , 簡單概述一下就是: 比Sqlite原生更快、可加密左医、使用更簡單授帕、極小的內(nèi)存消耗、庫體積小于100k等浮梢;EventBus也是出自他們的手筆跛十,可信賴程度高。
  • OK秕硝,接下來我們會從基本使用著手逐步分析它的內(nèi)部實(shí)現(xiàn)芥映,看看它內(nèi)部有啥貓膩。

準(zhǔn)備工作

  • 環(huán)境配置
    • projects#build.gradle文件中如下配置远豺。
buildscript {
    repositories {
        jcenter()
        mavenCentral() // add repository
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.1'
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'  
    }
}
  • app#build.gradle文件中如下配置 奈偏。
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin

dependencies {
implementation 'org.greenrobot:greendao:3.2.2'  
    // 需要加密數(shù)據(jù)庫的話添加這個(gè)
    implementation 'net.zetetic:android-database-sqlcipher:3.5.6'
}
  • 接下來需要 建立模型入口(Model Entity,只有在創(chuàng)建了至少一個(gè)模型入口并且編譯通過的情況下才能開始使用greenDao躯护。

  • 生成Entity相關(guān)代碼

    • 生成模板代碼的方式有兩種
      • 方式一: 編寫JAVA代碼惊来,然后執(zhí)行生成,如下示例:
    public class MyDaoGenerator
    { 
        public static void main(String[] args)
        {
            //指定生成類所在的包名
            Schema schema = new Schema(1, "com.example.laixiaolong.greendaotraning.greenDao.db");
    
             addNote(schema);
           // addCustomerOrder(schema);
     
            // 指定生成的類存放的目錄
            new DaoGenerator().generateAll(schema, "./app/src/main/java");
        }
    
        private static void addNote(Schema schema) {
            Entity note = schema.addEntity("Note"); // 指定類名
            // id
            note.addIdProperty();
            // 屬性即表的Column字段
            note.addStringProperty("title").notNull();
            note.addStringProperty("content");
            note.addDateProperty("date");
        }
        
        private static void addCustomerOrder(Schema schema) {
            Entity customer = schema.addEntity("Customer");
            customer.addIdProperty();
            customer.addStringProperty("name").notNull();
    
            Entity order = schema.addEntity("Order");
            order.setTableName("ORDERS"); // "ORDER" is a reserved keyword
            order.addIdProperty();
            Property orderDate = order.addDateProperty("date").getProperty();
            Property customerId = order.addLongProperty("customerId").notNull().getProperty();
            order.addToOne(customer, customerId);  // 一對一關(guān)聯(lián)
    
            ToMany customerToOrders = customer.addToMany(order, customerId); // 一對多關(guān)聯(lián)
            customerToOrders.setName("orders");
            customerToOrders.orderAsc(orderDate);
        }  }
    
    • 方式二: 使用gradle插件配置:在app#build.gradle中如下配置棺滞,這種方式會便捷很多裁蚁,因?yàn)槲覀冎恍枰獙W⒂?code>Entity的編寫就好了,編譯器會自動生成對應(yīng)Dao文件继准。
    android {
    ...
    } 
    greendao {
        // 標(biāo)記當(dāng)前數(shù)據(jù)庫表版本枉证,*OpenHelpers會根據(jù)這個(gè)參數(shù)來進(jìn)行版本遷移
        // 如果entity或數(shù)據(jù)庫表版本改變一次,這個(gè)值就會增加1锰瘸,默認(rèn)值是1
        schemaVersion 1
        // 指定生成的DAO刽严、DaoMaster、DaoSession類所在的包名,默認(rèn)是Entity所在包下
        daoPackage "com.example.laixiaolong.greendaotraning.greenDao.db"
        // 生成類存放的路徑舞萄,默認(rèn)是“build/generated/source/greendao”
        targetGenDir "./app/src/main/java"
        // 是否自動生成單元測試
        generateTests false
        // 單元測試存放的路徑眨补,默認(rèn)是“src/androidTest/java”
        targetGenDirTests "src/androidTest/java"
    }
    
  • 下面一個(gè)簡單的ChatObject關(guān)聯(lián)User的示例:

@Entity
public class User
{
   @Id(autoincrement = true) 
    private Long id;
    @Unique 
    private Long userIncrId; 
    @NotNull
    private String userAccount;
    private String userName;
    private Long orgId;
    private String orgName;
    // ...
}

@Entity
public class ChatObject
{
    @Id(autoincrement = true) 
    private Long id;
    private String userIcrId;
   
    // 一對一關(guān)聯(lián)
    @ToOne(joinProperty = "userIncrId")
    private User user;
} 
  • 其中@Entity 屬性用法解釋如下:
    @Entity(
        // 存在多張圖時(shí),設(shè)置schema參數(shù)告訴greenDao這個(gè)Entity屬于哪張圖
       // 可以是任意名字
       schema = "myschema", 
       // 標(biāo)識這個(gè)entity是否“active”倒脓,active狀態(tài)的表才具備增刪改查的方法
       active = true, 
        // 指定表的名稱撑螺,默認(rèn)是entity的名稱
       nameInDb = "AWESOME_USERS",  
       // 申明某個(gè)列字段會會占用多個(gè)列
       indexes = {   @Index(value = "name DESC", unique = true)  }, 
       // 標(biāo)記是否生成數(shù)據(jù)表,默認(rèn)是true崎弃,如果false甘晤,那么多個(gè)entity會映射到一張表,或者在greenDao外創(chuàng)建表
       createInDb = false, 
       // 是否需要生成一個(gè)具有所有屬性的構(gòu)造函數(shù)
       // 無參構(gòu)造器是必須存在的
       generateConstructors = true, 
        // 是否需要在屬性的get和set方法缺失時(shí)自動為其生成
       generateGettersSetters = true
       )     
    

增刪改查(基本操作)

  • 初始化

    • 先在Application中如下初始化
    public class App extends Application
    { 
        private DaoSession daoSession;
        private static App app; 
        @Override
        public void onCreate()
        {
            super.onCreate();
            app = this; 
            //使用普通數(shù)據(jù)庫
            DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(this, "roz-hjjm-db");
            daoSession = new DaoMaster(devOpenHelper.getWritableDb()).newSession();
             //使用 加密數(shù)據(jù)庫
            //  Database encryptedReadableDb = devOpenHelper.getEncryptedReadableDb("123456");
            // daoSession =   new DaoMaster(encryptedReadableDb).newSession();
        } 
        public static App app()
        {  return app;  }
    
        public DaoSession getDaoSession()
        {   return daoSession;    }
    } 
    
  • 增刪改查

    • 使用GreenDao時(shí)幾乎可以完全擺脫手寫Sql的束縛饲做,不必?fù)?dān)心某個(gè)字段名稱寫錯线婚、關(guān)鍵字寫錯、標(biāo)點(diǎn)符號寫錯的問題盆均,從而更快穩(wěn)健的實(shí)現(xiàn)我們的想法塞弊。
    • 插入
      • 插入方式有以下兩種,包括普通插入和使用事務(wù)批量插入泪姨,兩者的區(qū)別通過下面代碼運(yùn)行結(jié)果可以很清晰的看出(++使用模擬器運(yùn)行++)游沿,同樣是插入100條數(shù)據(jù),如果使用事務(wù)批量插入肮砾,效率能高個(gè)3~4倍诀黍,而如果只需要插入一條,那么對于本例而言(不排除其他情況)仗处,兩者耗時(shí)是一樣的眯勾,可見:
        • 當(dāng)數(shù)據(jù)量大時(shí)還是集中使用批量插入更為妥當(dāng)
        • 如果只需要插入一條,那么二者可任選其一
    @Test
    public void testGreenDaoInsert() {
       DaoSession daoSession = App.getDaoSession();
       UserDao userDao = daoSession.getUserDao();
    
       long start = System.currentTimeMillis();
       // 1. 普通插入,每次只能插入單條
       //insert 100: System.out: -->>Insert total used:: 83ms
       //insert 1:System.out: -->>Insert total used:: 3ms
       for (int i = 0; i < 100; i++) {
           // insertOrReplace
           userDao.insertOrReplace(new User()
                   .setFirstName("firstName::" + i)
                   .setLastName("lastName::" + i)
                   .setGender(i % 2 == 0 ? "male" : "female")
                   .setPhoneNum(String.valueOf(SystemClock.elapsedRealtimeNanos()))
                   .setAge(i + 10));
       }
       
       // 2. 使用事務(wù)插入疆柔,可以進(jìn)行批量插入咒精,也可以單條插入
       //insert 100: System.out: -->>Insert total used:: 26ms
       //insert 1:System.out: -->>Insert total used:: 3ms
       /*User[] usersArray = new User[100];
       for (int i = 0; i < 100; i++) {
           // insertOrReplace
           usersArray[i] =  new User()
                   .setFirstName("firstName::" + i)
                   .setLastName("lastName::" + i)
                   .setGender(i % 2 == 0 ? "male" : "female")
                   .setPhoneNum(String.valueOf(SystemClock.elapsedRealtimeNanos()))
                   .setAge(i + 10) ;
       }
       userDao.insertOrReplaceInTx(usersArray);*/
    
    
       System.out.println(String.format("-->>Insert total used:: %sms" , (System.currentTimeMillis() - start)) );
    
    }
    
    • 修改
      • 修改過程也很簡單镶柱,并且可以單條修改和使用事務(wù)批量修改旷档,和插入方式是一致的,不過需要注意的是
        • 修改時(shí)需要設(shè)置要修改條目的id(也就是key)歇拆,否則會修改失敗
        • 修改過程是覆蓋式的鞋屈,意味著如果修改時(shí)Entity中某個(gè)字段值為null,那么就會覆蓋掉表中對應(yīng)的字段數(shù)據(jù),這點(diǎn)尤為重要故觅,因?yàn)榭赡苄∈忠欢冻П樱愕臄?shù)據(jù)就沒了
    // 修改
    @Test
    public void testGreenDaoUpdate() {
        DaoSession daoSession = App.getDaoSession();
        UserDao userDao = daoSession.getUserDao();
    
        User user = new User()
                .setId(51L)  
                .setFirstName("horseLai")
                .setAge(23)
                .setPhoneNum("1234");
        // before: User{id=51, phoneNum='5866201745180', firstName='firstName::7', gender='female', lastName='lastName::7', age=17}
        // after: User{id=51, phoneNum='1234', firstName='horseLai', gender='null', lastName='null', age=23}
        // 1. 普通修改 ,只能單條修改
        userDao.update(user);
        // 2. 批量修改输吏, 可批量修改
        // userDao.updateInTx(user);
    }
    
    • 刪除
      • 刪除過程也很簡單权旷,有以下四種方式進(jìn)行,具體可按需選擇贯溅,如果知道id那么13ok拄氯,如果需要指定刪除條件躲查,那么4,刪除所有數(shù)據(jù)則2译柏。
    // 刪除
    @Test
    public void testGreenDaoDelete() {
        DaoSession daoSession = App.getDaoSession();
        UserDao userDao = daoSession.getUserDao();
    
        // 1. 刪除單個(gè)
        // userDao.deleteByKey(15L);
        
        // 2. 刪除所有
        userDao.deleteAll();
        
        // 3. 批量刪除(前提是知道key镣煮,也就是id)
        userDao.deleteByKeyInTx(15L, 16L,17L);
    
        // 4. 批量(按查找條件)刪除
        userDao.queryBuilder()
                .where(UserDao.Properties.Age.gt(15))
                .buildDelete()
                .executeDeleteWithoutDetachingEntities();
    }
    
    
    • 查找
      • 查找也很容易,可以按需選擇鄙麦,在以下實(shí)例中已經(jīng)體現(xiàn)的很清楚典唇,相對來說如果條件簡單的話,使用sql語句進(jìn)行查詢(方式2)會簡潔很多胯府,其次是使用方式1,最后是使用方式3,不過我感覺沒人會喜歡使用方式3,除非有什么特殊需求介衔,畢竟看到那一大串代碼你就想敬而遠(yuǎn)之,還有可能會忘記釋放cursor,導(dǎo)致內(nèi)存泄漏骂因。
    // 查找
    @Test
    public void testGreenDaoQuery() {
    
        DaoSession daoSession = App.getDaoSession();
        UserDao userDao = daoSession.getUserDao();
        // 1. 查詢
        /*List<User> users = userDao.queryBuilder()
                .limit(10)
                .orderAsc(UserDao.Properties.Age)
                .where(UserDao.Properties.Age.gt(15))
                .build()
                .list();*/
    
        //  2. 通過sql語句查詢
        /*List<User> users = userDao.queryRaw("where age = ?", "15");
        for (User user : users) {
            System.out.println(user.toString());
        }*/
    
        // 3. 通過cursor查詢
        Cursor cursor = userDao.queryBuilder()
                .limit(10)
                .orderAsc(UserDao.Properties.Age)
                .where(UserDao.Properties.Age.gt(15))
                .buildCursor().query();
        try {
            if (cursor != null) {
                System.out.println("-->> cursor count::" + cursor.getCount());
                if (cursor.getCount() == 0) {
                    return;
                }
                String[] columnNames = cursor.getColumnNames();
                System.out.println("-->> columnNames::" + Arrays.toString(columnNames));
    
                StringBuilder sb = new StringBuilder();
                cursor.moveToFirst();
                while (cursor.moveToNext()) {
                    String firstName = cursor.getString(cursor.getColumnIndex(UserDao.Properties.FirstName.columnName));
                    String lastName = cursor.getString(cursor.getColumnIndex(UserDao.Properties.LastName.columnName));
                    String gender = cursor.getString(cursor.getColumnIndex(UserDao.Properties.Gender.columnName));
                    String phone = cursor.getString(cursor.getColumnIndex(UserDao.Properties.PhoneNum.columnName));
                    int age = cursor.getInt(cursor.getColumnIndex(UserDao.Properties.Age.columnName));
    
                    sb.append("\n{\nfirstName=").append(firstName).append(",\n")
                            .append("lastName=").append(lastName).append(", \n")
                            .append("gender=").append(gender).append(", \n")
                            .append("phone=").append(phone).append(", \n")
                            .append("age=").append(age).append(", \n")
                            .append("}");
                }
    
                System.out.println(sb.toString());
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
    
        // 統(tǒng)計(jì)相應(yīng)條件下的條目數(shù)量夜牡,如果需要查詢數(shù)量,而不需要提取數(shù)據(jù)侣签,
        // 那么請使用這種方式塘装,不然性能浪費(fèi)是明顯的。
       /* long totalCount = userDao.queryBuilder()
                .limit(10)
                .orderAsc(UserDao.Properties.Age)
                .where(UserDao.Properties.Age.gt(15))
                .buildCount()
                .count();
    
        System.out.println("-->>Total Count::" + totalCount);*/
    
    }
    

關(guān)系型數(shù)據(jù)庫

關(guān)系型數(shù)據(jù)庫幾乎覆蓋了我們大部分實(shí)際使用場景影所,比如說實(shí)時(shí)通訊場景蹦肴、商城等等。這里我們以一個(gè)博客系統(tǒng)數(shù)據(jù)庫模型為例猴娩,熟悉一下如何使用greenDao建立這些場景模型阴幌。

  • 復(fù)合場景

    • 假設(shè)我們存在一篇博客,那么肯定需要關(guān)聯(lián)若干條評論數(shù)量卷中,并且需要知道是誰寫的吧矛双,那么需要關(guān)聯(lián)一個(gè)用戶(發(fā)布者);因此對于評論而言蟆豫,我們也需要知道是是誰評論的吧议忽,那么我們也需要對每條評論關(guān)聯(lián)一個(gè)用戶; 如果我們還需要用戶可以評論用戶評論十减,那么我們還需要在用戶評論中關(guān)聯(lián)若干個(gè)用戶評論的回復(fù)評論栈幸;這樣的話,這個(gè)模型中存在兩種關(guān)聯(lián)關(guān)系
      • 一對一關(guān)聯(lián):比如這里的博客與發(fā)布者的關(guān)系帮辟,一篇博客對應(yīng)于一個(gè)發(fā)布者速址。
      • 一對多關(guān)聯(lián):比如這里的博客與評論的關(guān)系,一篇博客對應(yīng)與若干條評論由驹。
  • 根據(jù)以上這個(gè)相對復(fù)雜的應(yīng)用場景,可以如下建立數(shù)據(jù)庫模型:

    • 博客
    @Entity
    public class Blog { 
        @Id(autoincrement = true)
        @Unique
        private long id;
    
        @NotNull
        private String title;
        private String content;
        @NotNull
        private Date createTime;
    
        // 發(fā)布者
        // 關(guān)聯(lián)至User表的PRIMARY KEY芍锚,也就是id
        @NotNull
        private long userId;
        @ToOne(joinProperty = "userId")
        private User user;
    
        // 評論
        // JOIN Comment表中的commentId列至本表的commentId列
        @NotNull
        private long commentId;
        // 1. 簡單JOIN
        //  @ToMany(referencedJoinProperty = "commentId")
        // 2. 可以存在多個(gè)復(fù)雜的JOIN,或者你希望指定JOIN進(jìn)來的列的列名
        @ToMany(joinProperties = {@JoinProperty(name = "commentId", referencedName = "id")})
        @OrderBy("createTime ASC")
        List<Comment> comments;
    }  
    
    • 評論
      • 用于評論博客
    @Entity
    public class Comment {
        @Id(autoincrement = true )
        private long id;
    
        @NotNull
        private String title;
        private String content;
        @NotNull
        private Date createTime;
     
        // 評論發(fā)布者
        // 關(guān)聯(lián)至User表的PRIMARY KEY,也就是id
        private long userId;
        @ToOne(joinProperty = "userId")
        private User user;
     
        // 評論
        // JOIN 關(guān)聯(lián)CommentReply中的commentSubId列
        private long commentSubId;
        @ToMany( joinProperties = {@JoinProperty( name = "commentSubId" ,referencedName = "id")})
        @OrderBy("createTime ASC")
        List<CommentReply> subComments;
    }
    
    • 評論的回復(fù)評論
      • 某個(gè)用戶對博客發(fā)表了一條評論并炮,其他用戶對這條評論的評論
    @Entity
    public class CommentReply { 
        @Id(autoincrement = true )
        private long id;
        @NotNull
        @Unique
        private long commentSubId;
    
        @NotNull
        private String title;
        private String content;
        @NotNull
        private Date createTime;
     
        // 評論發(fā)布者
        // 關(guān)聯(lián)至User表的PRIMARY KEY蒿赢,也就是id
        private long userId;
        @ToOne(joinProperty = "userId")
        private User user;
    }
    
  • 那么如何操作這個(gè)關(guān)聯(lián)數(shù)據(jù)庫模型呢? 可以參考如下示例代碼渣触。

    • 注意羡棵,我的代碼只起到演示作用,主要用于闡述如何使用GreenDao建立數(shù)據(jù)關(guān)聯(lián)嗅钻,實(shí)際應(yīng)用請思考再三皂冰,否則出問題了可別怪我哈。
@Test
public void multiRelative() {

    UserDao userDao = App.getDaoSession().getUserDao();
    BlogDao blogDao = App.getDaoSession().getBlogDao();
    CommentDao commentDao = App.getDaoSession().getCommentDao();
    CommentReplyDao commentReplyDao = App.getDaoSession().getCommentReplyDao();
    
    // 1. 發(fā)布一篇博客
    // 發(fā)布者
    User publisher = new User();
    publisher.setAge(24)
            .setGender("男")
            .setFirstName("horseLai")
            .setLastName("horseLai")
            .setPhoneNum("22132323323");
    userDao.insertOrReplace(publisher);

    // 博客
    Blog blog = new Blog();
    blog.setTitle("GreenDao建立關(guān)系型數(shù)據(jù)庫模型");
    blog.setContent("xxxxxxxxx");
    blog.setCreateTime(Calendar.getInstance().getTime());

    blog.setUser(publisher);  // 關(guān)聯(lián)至發(fā)布者
    blogDao.insertOrReplace(blog);

    // 2. 添加一條評論
    // 評論者
    User commentUser = new User()
            .setPhoneNum("1234555")
            .setAge(24)
            .setFirstName("horseLai");
    userDao.insertOrReplace(commentUser);
    // 評論
    Comment comment = new Comment();
    comment.setTitle("評論");
    comment.setContent("評論內(nèi)容");
    comment.setUser(commentUser);  // 關(guān)聯(lián)評論者
    comment.setCreateTime(Calendar.getInstance().getTime());
    commentDao.insertOrReplace(comment);
    
 
    // 3. 新增一條評論的回復(fù)評論
    User commentReplyUser = new User()
            .setPhoneNum("12345568875")
            .setAge(23)
            .setFirstName("horseLai");
    userDao.insertOrReplace(commentReplyUser);
    // 回復(fù)的評論
    CommentReply commentReply = new CommentReply();
    commentReply.setTitle("回復(fù)評論");
    commentReply.setContent("評論回復(fù)內(nèi)容");
    commentReply.setUser(commentReplyUser);
    commentReply.setCreateTime(Calendar.getInstance().getTime());
    commentReplyDao.insertOrReplace(commentReply);
      
}

  • 查找數(shù)據(jù)
    • 增刪該查和之前講解的內(nèi)容是一致的养篓,這里只演示簡單查找秃流,如下示例代碼:
    @Test
    public void query() {
        BlogDao blogDao = App.getDaoSession().getBlogDao();
    
        List<Blog> blogs = blogDao.queryBuilder()
                .build()
                .list();
    
        blogs.forEach((blog -> {
            System.out.println();
            System.out.println(blog.toString()); 
            System.out.println();
        }));
    }
    
    
    • 數(shù)據(jù)結(jié)果如下:
      • 可見其實(shí)整個(gè)模型是個(gè)類似于樹形的關(guān)系結(jié)構(gòu)。
    Blog{
        id=0,
        title='GreenDao建立關(guān)系型數(shù)據(jù)庫模型',
        content='xxxxxxxxx',
        createTime=Tue Oct 02 14:03:27 GMT 2018, 
        userId=1, 
        user=User{id=1, phoneNum='22132323323', firstName='horseLai', gender='男', lastName='horseLai', age=24},
        commentId=0,
        comments=[
         Comment{
                 id=0, 
                 title='評論',
                 content='評論內(nèi)容', 
                 createTime=Tue Oct 02 14:03:27 GMT 2018, 
                 userId=2,
                 user=User{  id=2, phoneNum='1234555',   firstName='horseLai', gender='null', lastName='null',   age=24 }, 
                 subComments=[
                     CommentReply{
                     id=0, 
                     commentSubId=0, 
                     title='回復(fù)評論', 
                     content='評論回復(fù)內(nèi)容',
                     createTime=Tue Oct 02 14:03:27 GMT 2018, 
                     userId=3,
                     user=User{ id=3,  phoneNum='12345568875', firstName='horseLai',  gender='null',  lastName='null',   age=23  }
                     }
                 ]
                }
        ] 
    }
     
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末柳弄,一起剝皮案震驚了整個(gè)濱河市舶胀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碧注,老刑警劉巖嚣伐,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異萍丐,居然都是意外死亡轩端,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門逝变,熙熙樓的掌柜王于貴愁眉苦臉地迎上來基茵,“玉大人,你說我怎么就攤上這事壳影」安悖” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵宴咧,是天一觀的道長根灯。 經(jīng)常有香客問我,道長悠汽,這世上最難降的妖魔是什么箱吕? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮柿冲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘兆旬。我一直安慰自己假抄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宿饱,像睡著了一般熏瞄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谬以,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天强饮,我揣著相機(jī)與錄音,去河邊找鬼为黎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛铭乾,可吹牛的內(nèi)容都是我干的剪廉。 我是一名探鬼主播炕檩,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼笛质!你這毒婦竟也來了泉沾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤妇押,失蹤者是張志新(化名)和其女友劉穎爆哑,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舆吮,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡揭朝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了色冀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片潭袱。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锋恬,靈堂內(nèi)的尸體忽然破棺而出屯换,到底是詐尸還是另有隱情,我是刑警寧澤与学,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布彤悔,位于F島的核電站,受9級特大地震影響索守,放射性物質(zhì)發(fā)生泄漏晕窑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一卵佛、第九天 我趴在偏房一處隱蔽的房頂上張望杨赤。 院中可真熱鬧敞斋,春花似錦、人聲如沸疾牲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阳柔。三九已至焰枢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舌剂,已是汗流浹背济锄。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留架诞,地道東北人拟淮。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像谴忧,于是被迫代替她去往敵國和親很泊。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容