MyBatisPlus学习整理(一)

本文是通过慕课网相关课程学习MyBatisPlus整理的笔记。
MyBatisPlus入门 : - ) 老师讲的挺好的,还不会MyBatisPlus的小伙伴门可以听一下。
MyBatisPlus官网
MyBatisPlus源码地址

MyBatisPlus架构图(盗用官网的,侵,删。)

mybatis-plus.png

SpringBoot第一个简单应用

  1. 数据库建表
#创建用户表
CREATE TABLE user (
    id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
    name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
    age INT(11) DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
    manager_id BIGINT(20) DEFAULT NULL COMMENT '直属上级id',
    create_time DATETIME DEFAULT NULL COMMENT '创建时间',
    CONSTRAINT manager_fk FOREIGN KEY (manager_id)
        REFERENCES user (id)
)  ENGINE=INNODB CHARSET=UTF8;

#初始化数据:
INSERT INTO user (id, name, age, email, manager_id
    , create_time)
VALUES (1087982257332887553, '大boss', 40, 'boss@baomidou.com', NULL
        , '2019-01-11 14:20:20'),
    (1088248166370832385, '王天风', 25, 'wtf@baomidou.com', 1087982257332887553
        , '2019-02-05 11:12:22'),
    (1088250446457389058, '李艺伟', 28, 'lyw@baomidou.com', 1088248166370832385
        , '2019-02-14 08:31:16'),
    (1094590409767661570, '张雨琪', 31, 'zjq@baomidou.com', 1088248166370832385
        , '2019-01-14 09:15:15'),
    (1094592041087729666, '刘红雨', 32, 'lhm@baomidou.com', 1088248166370832385
        , '2019-01-14 09:48:16');
  1. 依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.2</version>
        </dependency>
  1. springboot配置文件
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/test?serverTimezone=CTT&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
logging:
  level:
    root: warn
    org.ywb.demo.dao: trace
  pattern:
    console: '%p%m%n'

  1. 创建相关包,如图:


    image.png
  2. 在pojo包中新建和数据库user表映射的类
@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private String managerId;
    private LocalDateTime createTime;
}
  1. 在dao包中创建mapper接口,并集成mybatisPlus的BaseMapper
public interface UserMapper extends BaseMapper<User> {

}
  1. 在springboot启动类添加@MapperScan扫描dao层接口
@MapperScan("org.ywb.demo.dao")
@SpringBootApplication
public class MybatisPlusDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusDemoApplication.class, args);
    }

}

8.编写测试类

@RunWith(SpringRunner.class)
@SpringBootTest
public class MybatisPlusDemoApplicationTests {

    @Resource
    private UserMapper userMapper;
    
    @Test
    public void select(){
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }

}

运行结果:


image.png

常用注解

MyBatisPlus提供了一些注解供我们在实体类和表信息出现不对应的时候使用。通过使用注解完成逻辑上匹配。

注解名称 说明
@TableName 实体类的类名和数据库表名不一致
@TableId 实体类的主键名称和表中主键名称不一致
@TableField 实体类中的成员名称和表中字段名称不一致
@Data
@TableName("t_user")
public class User {
    @TableId("user_id")
    private Long id;
    @TableField("real_name")
    private String name;
    private Integer age;
    private String email;
    private Long managerId;
    private LocalDateTime createTime;
}

排除实体类中非表字段

  1. 使用transient关键字修饰非表字段,但是被transient修饰后,无法进行序列化。
  2. 使用static关键字,因为我们使用的是lombok框架生成的get/set方法,所以对于静态变量,我们需要手动生成get/set方法。
  3. 使用@TableField(exist = false)注解

CURD

BaseMapper中封装了很多关于增删该查的方法,后期自动生成,我们直接调用接口中的相关方法即可完成相应的操作。
BaseMapper部分代码

public interface BaseMapper<T> extends Mapper<T> {

    int insert(T entity);
   
    int deleteById(Serializable id);

    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

    int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);

    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

    int updateById(@Param(Constants.ENTITY) T entity);

...
}

插入一条记录测试:

  @Test
    public void insert(){
        User user = new User();
        user.setAge(31);
        user.setManagerId(1088250446457389058L);
        user.setCreateTime(LocalDateTime.now());
        int insert = userMapper.insert(user);
        System.out.println("影像记录数:"+insert);
    }
image.png

条件构造器查询

除了BaseMapper中提供简单的增删改查方法之外,还提供了很多关于区间查询,多表连接查询,分组等等查询功能,实现的类图如下所示:

image.png

通过观察类图可知,我们需要这些功能时,只需要创建QueryWrapper对象即可。

  1. 模糊查询
/**
     * 查询名字中包含'雨'并且年龄小于40
     * where name like '%雨%' and age < 40
     */
    @Test
    public void selectByWrapper(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name","雨").lt("age",40);
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);
    }
image.png
  1. 嵌套查询
    /**
     * 创建日期为2019年2月14日并且直属上级姓名为王姓
     * date_format(create_time,'%Y-%m-%d') and manager_id in (select id from user where name like '王%')
     */
    @Test
    public void selectByWrapper2(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.apply("date_format(create_time,'%Y-%m-%d')={0}","2019-02-14")
                .inSql("manager_id","select id from user where name like '王%'");
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);
    }

image.png

注意
上面的日期查询使用的是占位符的形式进行查询,目的就是为了防止SQL注入的风险。
apply方法的源码

   /**
     * 拼接 sql
     * <p>!! 会有 sql 注入风险 !!</p>
     * <p>例1: apply("id = 1")</p>
     * <p>例2: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")</p>
     * <p>例3: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", LocalDate.now())</p>
     *
     * @param condition 执行条件
     * @return children
     */
    Children apply(boolean condition, String applySql, Object... value);

SQL 注入的例子:

queryWrapper.apply("date_format(create_time,'%Y-%m-%d')=2019-02-14 or true=true")
              .inSql("manager_id","select id from user where name like '王%'");
sql注入例子
  1. and & or
  /**
     * 名字为王姓,(年龄小于40或者邮箱不为空)
     */
    @Test
    public void selectByWrapper3(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name","王").and(wq-> wq.lt("age",40).or().isNotNull("email"));

        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);

    }
  1. between & and
   /**
     * 名字为王姓,(年龄小于40,并且年龄大于20,并且邮箱不为空)
     */
    @Test
    public void selectWrapper4(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name", "王").and(wq -> wq.between("age", 20, 40).and(wqq -> wqq.isNotNull("email")));
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);
    }
image.png
  1. nested
   /**
     * (年龄小于40或者邮箱不为空)并且名字为王姓
     * (age<40 or email is not null)and name like '王%'
     */
    @Test
    public void selectWrapper5(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();

        queryWrapper.nested(wq->wq.lt("age",40).or().isNotNull("email")).likeRight("name","王");

        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);
    }
image.png
  1. in
   /**
     * 年龄为30,31,35,34的员工
     */
    @Test
    public void selectWrapper6(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();

        queryWrapper.in("age", Arrays.asList(30,31,34,35));

        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);
    }
image.png
  1. last 有SQL注入的风险!!!
  /**
     * 无视优化规则直接拼接到 sql 的最后(有sql注入的风险,请谨慎使用)
     * <p>例: last("limit 1")</p>
     * <p>注意只能调用一次,多次调用以最后一次为准</p>
     *
     * @param condition 执行条件
     * @param lastSql   sql语句
     * @return children
     */
    Children last(boolean condition, String lastSql);
   /**
     * 只返回满足条件的一条语句即可
     * limit 1
     */
    @Test
    public void selectWrapper7(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();

        queryWrapper.in("age", Arrays.asList(30,31,34,35)).last("limit 1");

        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);
    }
image.png
  1. 查询指定部分列
    /**
     * 查找为王姓的员工的姓名和年龄
     */
    @Test
    public void selectWrapper8(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("name","age").likeRight("name","王");
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);
    }
image.png
  1. 使用过滤器查询指定列
   /**
     * 查询所有员工信息除了创建时间和员工ID列
     */
    @Test
    public void selectWrapper9(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select(User.class,info->!info.getColumn().equals("create_time")
                &&!info.getColumn().equals("manager_id"));
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);
    }
image.png

condition 的作用

在我们调用的查询语句中,通过查看源码(这里以apply方法为例)可以看出,每个查询方法的第一个参数都是boolean类型的参数,重载方法中默认给我们传入的都是true。

 default Children apply(String applySql, Object... value) {
        return apply(true, applySql, value);
    }
    Children apply(boolean condition, String applySql, Object... value);

这个condition的作用是为true时,执行其中的SQL条件,为false时,忽略设置的SQL条件。

实体作为条件构造方法的参数

在web开发中,controller层常常会传递给我们一个用户的对象,比如通过用户姓名和用户年龄查询用户列表。
我们可以将传递过来的对象直接以构造参数的形式传递给QueryWrapper,MyBatisPlus会自动根据实体对象中的属性自动构建相应查询的SQL语句。

 @Test
    public void selectWrapper10(){
        User user = new User();
        user.setName("刘红雨");
        user.setAge(32);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);
    }
image.png

如果想通过对象中某些属性进行模糊查询,我们可以在跟数据库表对应的实体类中相应的属性标注注解即可。
比如我们想通过姓名进行模糊查询用户列表。

@TableField(condition = SqlCondition.LIKE)
    private String name;
 @Test
    public void selectWrapper10(){
        User user = new User();
        user.setName("红");
        user.setAge(32);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);
    }
image.png

Lambda条件构造器

MybatisPlus提供了4种方式创建lambda条件构造器,前三种分别是这样的

        LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda();
        LambdaQueryWrapper<User> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<User> lambdaQueryWrapper2 = Wrappers.lambdaQuery();
  1. 查询名字中包含‘雨’并且年龄小于40的员工信息
    @Test
    public void lambdaSelect(){
        LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.like(User::getName,"雨").lt(User::getAge,40);

        List<User> userList = userMapper.selectList(lambdaQueryWrapper);
        userList.forEach(System.out::println);
    }
image.png

QueryWrapper类已经提供了很强大的功能,而lambda条件构造器做的和QueryWrapper的事也是相同的为什么要冗余的存在lambda条件构造器呢?
QueryWrapper是通过自己写表中相应的属性进行构造where条件的,容易发生拼写错误,在编译时不会报错,只有运行时才会报错,而lambda条件构造器是通过调用实体类中的方法,如果方法名称写错,直接进行报错,所以lambda的纠错功能比QueryWrapper要提前很多。
举个例子:
查找姓名中包含“雨”字的员工信息。
使用QueryWrapper

queryWrapper.like("name","雨");

使用lambda

lambdaQueryWrapper.like(User::getName,"雨");

如果在拼写name的时候不小心,写成了naem,程序并不会报错,但是如果把方法名写成了getNaem程序立即报错。

第四种lambda构造器
细心的人都会发现无论是之前的lambda构造器还是queryWrapper,每次编写完条件构造语句后都要将对象传递给mapper 的selectList方法,比较麻烦,MyBatisPlus提供了第四种函数式编程方式,不用每次都传。

  1. 查询名字中包含“雨”字的,并且年龄大于20的员工信息
    @Test
    public void lambdaSelect(){
        List<User> userList = new LambdaQueryChainWrapper<>(userMapper).like(User::getName, "雨").ge(User::getAge, 20).list();
        userList.forEach(System.out::println);
    }
image.png

自定义SQL

  1. 在resources资源文件夹下新建mapper文件夹,并将mapper文件夹的路径配置到配置文件中


    image.png
mybatis-plus:
  mapper-locations: mapper/*.xml
  1. 在mapper 文件夹中新建UserMapper.xml。
  2. 像mybatis那样在UseMapper接口中写接口,在UserMapper接口中写SQL即可。
    UserMapper
public interface UserMapper extends BaseMapper<User> {

    /**
     * 查询所有用户信息
     * @return list
     */
    List<User> selectAll();
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.ywb.demo.dao.UserMapper">

    <select id="selectAll" resultType="org.ywb.demo.pojo.User">
        select * from user
    </select>
</mapper>

分页查询

MyBatis分页提供的是逻辑分页,每次将所有数据查询出来,存储到内存中,然后根据页容量,逐页返回。如果表很大,无疑是一种灾难!
MyBatisPlus物理分页插件

  1. 新建config类,在config类中创建PaginationInterceptor对象
@Configuration
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }
}
  1. 测试:查询年龄大于20 的用户信息,并以每页容量为两条分页的形式返回。
    @Test
    public void selectPage(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.ge("age",20);

        //设置当前页和页容量
        Page<User> page = new Page<>(1, 2);

        IPage<User> userIPage = userMapper.selectPage(page, queryWrapper);

        System.out.println("总页数:"+userIPage.getPages());
        System.out.println("总记录数:"+userIPage.getTotal());
        userIPage.getRecords().forEach(System.out::println);
    }
image.png
  1. 测试:不查询总记录数,分页查询
    IPage类的构造参数提供了参数的重载,第三个参数为false时,不会查询总记录数。
public Page(long current, long size, boolean isSearchCount) {
        this(current, size, 0, isSearchCount);
}
~~·
## 更新
1. 通过userMapper提供的方法更新用户信息
~~~java
    @Test
    public void updateTest1(){
        User user = new User();
        user.setId(1088250446457389058L);
        user.setEmail("update@email");
        int rows = userMapper.updateById(user);
        System.out.println(rows);
    }
image.png
  1. 使用UpdateWrapper更新数据(相当于使用联合主键)
    @Test
    public void updateTest2(){
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("name","李艺伟").eq("age",26);

        User user = new User();
        user.setEmail("update2@email");
        int rows = userMapper.update(user, updateWrapper);
        System.out.println(rows);
    }
image.png
  1. 当我们更新少量用户信息的时候,可以不用创建对象,直接通过调用set方法更新属性即可。
    @Test
    public void updateTest3(){
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("name","李艺伟").eq("age",26).set("email","update3@email.com");
        userMapper.update(null,updateWrapper);
    }
image.png
  1. 使用lambda更新数据
    @Test
    public void updateByLambda(){
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
        lambdaUpdateWrapper.eq(User::getName,"李艺伟").eq(User::getAge,26).set(User::getAge,27);
        userMapper.update(null,lambdaUpdateWrapper);
    }
image.png

删除

删除方式和update极其类似。

AR模式(Active Record)

直接通过实体类完成对数据的增删改查。

  1. 实体类继承Model类
@Data
@EqualsAndHashCode(callSuper = false)
public class User extends Model<User> {
    private Long id;
    @TableField(condition = SqlCondition.LIKE)
    private String name;
    private Integer age;
    private String email;
    private Long managerId;
    private LocalDateTime createTime;
}

Model类中封装了很多增删改查方法,不用使用UserMapper即可完成对数据的增删改查。

  1. 查询所有用户信息
    @Test
    public void test(){
        User user = new User();
        user.selectAll().forEach(System.out::println);
    }
image.png

主键策略

MyBatisPlus的主键策略封装在IdType枚举类中。

@Getter
public enum IdType {
    /**
     * 数据库ID自增
     */
    AUTO(0),
    /**
     * 该类型为未设置主键类型(将跟随全局)
     */
    NONE(1),
    /**
     * 用户输入ID
     * <p>该类型可以通过自己注册自动填充插件进行填充</p>
     */
    INPUT(2),

    /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
    /**
     * 全局唯一ID (idWorker)
     */
    ID_WORKER(3),
    /**
     * 全局唯一ID (UUID)
     */
    UUID(4),
    /**
     * 字符串全局唯一ID (idWorker 的字符串表示)
     */
    ID_WORKER_STR(5);

    private final int key;

    IdType(int key) {
        this.key = key;
    }
}

在实体类中对应数据库中的主键id属性上标注注解TableId(type='xxx')即可完成主键配置。

    @TableId(type = IdType.AUTO)
    private Long id;

这种配置方式的主键策略只能在该表中生效,但是其他表还需要进行配置,为了避免冗余,麻烦,MybatisPlus提供了全局配置,在配置文件中配置主键策略即可实现。

mybatis-plus:
  mapper-locations: mapper/*.xml
  global-config:
    db-config:
      id-type: auto

如果全局策略和局部策略全都设置,局部策略优先。

基本配置

MyBatisPlus官方文档

mybatis-plus:
  mapper-locations: mapper/*.xml
  global-config:
    db-config:
      # 主键策略
      id-type: auto
      # 表名前缀
      table-prefix: t
      # 表名是否使用下划线间隔,默认:是
      table-underline: true
  # 添加mybatis配置文件路径
  config-location: mybatis-config.xml
  # 配置实体类包地址
  type-aliases-package: org.ywb.demo.pojo
  # 驼峰转下划线
  configuration:
    map-underscore-to-camel-case: true
  • 附录
  1. mybatisPlus进阶功能请戳 MyBatisPlus学习整理(二)
  2. 源码地址:https://github.com/xiao-ren-wu/notebook/tree/master/mybatis-plus-demo
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335