MyBatis 入门笔记+理解(一)

这是基于b站狂神的JavaWeb课程的笔记 课程链接

1. 简介

1.1 什么是MyBatis?

  • MyBatis 是一款优秀的持久层框架
  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO为数据库中的记录

1.2 持久化

  • 将程序的数据在瞬时状态和持久状态转化的过程
  • JDBC、io文件持久化
  • 为什么要持久化:
    • 内存特点:断电即失
    • 内存太贵了...

1.3 持久层

(dao层,service层,controller层...)

  • 完成持久化工作的代码块
  • 层界限十分明显

1.4 为什么需要MyBatis?

  • 方便(传统JDBC代码笨重)
  • 流行
  • 优点:
    • sql和代码分离
    • orm映射(对象与数据库)
    • xml标签,支持动态sql

2. Hello, MyBatis

思路:搭建环境 \to 导入MyBatis \to 编写代码 \to 测试!

2.1 环境搭建

建一个新的数据库

create database `mybatis`;
use `mybatis`;
create table `user` (
    `id` int(20) not null primary key,
    `name` varchar(30) default null,
    `pwd` varchar(30) default null
) engine=innodb default charset=utf8;
insert into `user`  values (1, '张三','123456');
insert into `user`  values (2, '李四','112233');
insert into `user`  values (3, 'yldog','003773')

新建一个普通的Maven项目
导入依赖 (配置pom.xml)

     <dependencies>

        <!-- MyBatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>

        <!-- 添加mysql驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.8</version>
        </dependency>

        <!--  Junit  -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>

      </dependencies>

在刚才搭建的maven里新建Module -- 为maven的子工程

  • 在父工程的pom.xml里有
    <modules>
        <module>mybatis-01-hello</module>
    </modules>
  • 在子工程的pom.xml里有
    <parent>
        <artifactId>mybatis-learn</artifactId>
        <groupId>com.yldog</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
  • 好处:父工程中的依赖自动进入子工程,无需重复加入依赖

2.2 创建编写一个模块

编写mybatis核心配置文件

  • (一般)在resource目录下创建mybatis-config.xml文件
  • 编写配置 -- 这个操作等同于之前的读取properties.db
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="$com.mysql.jdbc.Driver"/>
                <!--  注意:原本的 & 用 &amp; 代替  -->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    </mappers>
</configuration>

编写MyBatis工具类

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

// sqlSessionFactory --> sqlSession
public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            // 使用MyBatis第一步:获取sqlSessionFactory对象
            // 以下三行是官方文档源码
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 由sqlSessionFactory获得sqlSession的实例
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }
}

2.3 编写代码

  • 实体类
public class User {
    private int id;
    private String name;
    private String pwd;
    
    public User() {
    }

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}
  • 接口类
    • 写一个接口类,拿到所有用户
public interface UserMapper {
   List<User> getUserList();
}
  • 实现接口由之前的编写一个UserMapperImpl类变为编写和绑定一个Mapper的xml配置文件
<!--namespace 绑定了一个mapper接口-->
<!--select 是一个查询语句-->
<!--id 指定了接口的方法-->
<!--resultType对应之前的结果集ResultSet-->
<mapper namespace="com.yldog.dao.UserMapper" >
    <select id="getUserList" resultType="com.yldog.pojo.User">
        select * from mybatis.user
    </select>
</mapper>

注意事项

  • (*)mapper 需在mybatis-config.xml中进行注册!
<!--  每一个Mapper.xml都需要在mybatis核心配置文件中注册!  -->
    <mappers>
        <mapper resource="com/yldog/dao/UserMapper.xml"/>
    </mappers>
  • Maven出现的资源过滤问题,需在pom.xml中加入如下配置 (将xml后缀的文件也一起导出):
<!--maven的资源过滤问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

2.4 测试 (用法)

    @Test
    public void getUserListTest() {
        // 获得sqlSession
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        // 执行sql
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 执行方法,拿到结果
        for (User user : userMapper.getUserList()) {
            System.out.println(user.toString());
        }

        // 关闭sqlSession
        sqlSession.close();
    }

3.CRUD

1.编写接口
2.在接口的xml文件中绑定接口,并编写sql
3.测试

一个用户管理接口:

public interface UserMapper {
    // 获取所有用户
    List<User> getUserList();

    // 根据id查询用户
    User getUserById(int id);

    // 增加新用户
    int insert(User user);

    // 更新用户
    int update(User user);

    // 根据id删除用户
    int deleteById(int id);
}

在这个接口的xml配置文件中:

<mapper namespace="com.yldog.dao.UserMapper" >
    <select id="getUserList" resultType="com.yldog.pojo.User">
        select * from mybatis.user;
    </select>

    <select id="getUserById" parameterType="int" resultType="com.yldog.pojo.User">
        select * from mybatis.user where id = #{id};
    </select>

    <insert id="insert" parameterType="com.yldog.pojo.User" >
        insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd});
    </insert>
    
    <update id="update" parameterType="com.yldog.pojo.User" >
        update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
    </update>

    <delete id="deleteById" parameterType="int">
        delete from mybatis.user where id = #{id};
    </delete>
</mapper>

3.1. namespace

  • 绑定了这个接口
<mapper namespace="com.yldog.dao.UserMapper" >
  ...
</mapper>

3.2. select

<select id="getUserList" resultType="com.yldog.pojo.User">
    select * from mybatis.user;
</select>
  • id: 绑定了接口中要实现的方法
  • resultType:方法的返回值
  • paramType:方法的参数

测试

    @Test
    public void getUserListTest() {
        // 获得sqlSession
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        // 执行sql
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 执行方法,拿到结果
        for (User user : userMapper.getUserList()) {
            System.out.println(user.toString());
        }

        // 关闭sqlSession
        sqlSession.close();
    }

3.3. insert

注意:

  • 对象中的属性,可以直接取出来 i.e. (id, name, pwd)
  • 方法中的参数放在 #{} 中
<insert id="insert" parameterType="com.yldog.pojo.User" >
        insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd});
</insert>

测试
注意:要使用 sqlSession.commit() 提交事务,如同在JDBC中一样

    @Test
    public void insertTest() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        int res = userMapper.insert(new User(5,"zzh","000001"));
        // 提交事务,否则无法更改
        sqlSession.commit();
        sqlSession.close();
    }

3.4. update

<update id="update" parameterType="com.yldog.pojo.User" >
        update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
</update>

测试

    @Test
    public void updateTest() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        userMapper.update(new User(5, "zzh", "000002"));
        sqlSession.commit();
        sqlSession.close();
    }

3.5. delete

<delete id="deleteById" parameterType="int">
        delete from mybatis.user where id = #{id};
</delete>

测试

    @Test
    public void deleteByIdTest() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        userMapper.deleteById(5);
        sqlSession.commit();
        sqlSession.close();
    }

3.6. 万能的map

如果实体类或者数据库中的表,参数或者字段过多,应当考虑使用Map!

  • 接口
// 用map更新
int updateByMap(Map map);
  • xml
<update id="updateByMap" parameterType="map">
        update mybatis.user set name=#{name}, pwd=#{pwd} where id=#{id};
</update>
  • 测试
    @Test
    public void updateByMapTest() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        Map<String, Object> map = new HashMap<>();
        map.put("name", "kdb");
        map.put("pwd", "123123");
        map.put("id", 5);
        userMapper.updateByMap(map);
        sqlSession.commit();
        sqlSession.close();
    }

3.7. 模糊查询

  • 接口
    // 根据name模糊查询用户
    List<User> getUserByName(String value);
  • xml
<select id="getUserByName" parameterType="String" resultType="com.yldog.pojo.User">
        select * from mybatis.user where name like #{value}
</select>
  • 测试
    在输入参数时用%拼接 "%value%"
    @Test
    public void getUserByNameTest() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        for (User user : userMapper.getUserByName("%李%")) {
            System.out.println(user);
        }
        sqlSession.close();
    }

4.配置解析

4.1.核心配置文件

  • mybatis-config.xml

4.2.环境配置(environments)

<environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!--  注意:原本的 & 用 &amp; 代替  -->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="003773"/>
            </dataSource>
        </environment>
    </environments>
  • envrionments中可以配置多套环境,使用default="环境名"来选择使用哪一套
  • transactionManager (事务管理器)
    • 有两种选择"JDBC""MANAGED",一般选择JDBC
    • 使用 Spring + MyBatis,则没有必要配置事务管理器
  • dataSource(数据源)
    • type="[UNPOOLED|POOLED|JNDI]"
    • 这里使用"POOLED", 优点:

这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。

4.3. 属性(properties)

  • 方式1:在外部进行配置,并可以进行动态替换
    1. 编写一个db.properties配置文件
    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
    username=root
    password=003773
    
    1. 在mybatis-config中引入
    <!--  引入外部文件配置  -->
    <properties resource="db.properties"/>
    
    dataSource下使用 ${} 提取替换
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
    
  • 方式2:直接在dataSource下编写
<dataSource type="POOLED">
  <property name="driver" value="com.mysql.jdbc.Driver"/>
  <!--  注意:原本的 & 用 &amp; 代替  -->
  <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
  <property name="username" value="root"/>
  <property name="password" value="003773"/>
</dataSource>
  • 方式3:在properties下编写(可同时引入外部文件)
    <!--  引入外部文件配置  -->
    <properties resource="db.properties">
        <property name="username" value="xxxxxx"/>
        <property name="password" value="xxxxxx"/>
    </properties>

注意:若外部配置文件与 <property>name 一样,则程序会优先考虑外部配置文件中的值

4.4. 类型别名(typeAliases)

  • 方式1:
    指定一个类
<typeAliases>
  <typeAlias type="com.yldog.pojo.User" alias="User"/>
</typeAliases>
  • 方式2:
    扫描整个包,为包内所有实体类起别名
<typeAliases>
  <package name="com.yldog.pojo"/>
</typeAliases>

默认的别名为:

Bean 的首字母小写的非限定类名来作为它的别名

e.g. User \to user

若有注解,则别名为其注解值

@Alias("otherAliasForUser")
public class User {
    ...
}

4.5. 映射器(mappers)

我们需要告诉 MyBatis 到哪里去找定义 好的SQL 映射语句

  • 方法1(推荐): 使用相对于类路径的资源引用
<!--  每一个Mapper.xml都需要在mybatis核心配置文件中注册!  -->
    <mappers>
        <mapper resource="com/yldog/dao/UserMapper.xml"/>
    </mappers>
  • 方法2:使用映射器接口实现类的完全限定类名
<mappers>
  <mapper class="com.yldog.dao.UserMapper"/>
</mappers>
  • 方法3:扫描包,将包内的映射器接口实现全部注册为映射器
<mappers>
  <package name="com.yldog.dao"/>
</mappers>

注意:方式2,3需要:

  • 接口和它的Mapper配置文件必须同名!
  • 接口和它的Mapper配置文件必须在一个包下!

    e.g.

4.6. 生命周期和作用域

错误的使用会导致非常严重的并发问题。

  • SqlSessionFactoryBuilder
    • 一旦创建了 SqlSessionFactory,就不再需要它了
    • 局部方法变量
  • SqlSessionFactory
    • 可以想象成:数据库连接池
    • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
    • SqlSessionFactory 的最佳作用域是应用作用域
    • 单例模式或者静态单例模式
  • SqlSession
    • 连接到连接池的一个请求!
    • SqlSession 的实例不是线程安全的,因此是不能被共享的
    • 最佳的作用域是请求或方法作用域
    • 用完要关闭!
    • 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中

5. 解决属性名和字段名不一致的问题(ResultMap)

  • 问题:比如,User实体类中属性为 private String password; ,而数据库表格中字段名称为 pwd
    使用原本的mapper xml文件来执行查询语句,会出现这样的问题:

    在数据库查出pwd字段后无法被赋值到user.password上因为名称不同。
  • 解决:使用一个简单的resultMap
    编写一个结果映射集
    <!--  结果集映射  -->
    <!-- type 为实体类 -->
    <resultMap id="userResultMap" type="User">
        <!--   名称一样的自动(隐式)映射     -->
        <!--  <result column="id" property="id"/> -->
        <!--  <result column="name" property="name"/> -->

        <!--   column为表格字段
               property为类属性 -->
        <result column="pwd" property="password"/>
    </resultMap>

在Mapper文件中使用resultMap,指定这个结果映射集

    <!--  将resultType 变成 resultMap  -->
    <select id="getUserById" parameterType="int" resultMap="userResultMap">
        select * from mybatis.user where id = #{id};
    </select>
  • 结果:成功赋值

6. 日志

  • MyBatis中日志的使用可在mybatis-config.xml中的settings中配置
  • 设置名为 logImpl
  • 有以下种类 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
  • 默认为无日志

6.1 使用Log4j日志

6.1.1 什么是Log4j?

  • 通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器等
  • 可以控制每一条日志的输出格式
  • 定义每一条日志信息的级别
  • 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
    6.1.2. 导入依赖
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

6.1.3. 配置mybatis-config.xml

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

6.1.4. resources目录下加入Log4j的配置文件log4j.properties

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/yldog.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

6.1.5. 使用
执行以下sql:

    @Test
    public void getUserByIdTest() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        System.out.println("id:1 --> " + userMapper.getUserById(1).toString());
        sqlSession.close();
    }

结果:
log4j日志

也可以自主的输出信息,决定输出信息的级别:

  • 构建日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserMapper.class);
  • 输出不同级别的信息
  @Test
    public void log4jTest() {
        logger.info("info:进入了log4jTest");
        logger.debug("debug:进入了log4jTest");
        logger.error("error:进入了log4jTest");
    }
  • 结果
[com.yldog.dao.UserMapper]-info:进入了log4jTest
[com.yldog.dao.UserMapper]-debug:进入了log4jTest
[com.yldog.dao.UserMapper]-error:进入了log4jTest

6.2. 使用 STDOUT_LOGGING

配置mybatis-config.xml即可

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

7. 使用Limit分页

  • 编写接口方法使,用Map作为参数
List<User> getUserByLimit(Map<String, Integer> map);
  • 编写Mapper.xml
<select id="getUserByLimit" parameterType="map" resultMap="userResultMap">
        select * from mybatis.user limit #{startIndex}, #{pageSize};
</select>

这样就能从输入的Map中提取 startIndexpageSize

  • 测试
    @Test
    public void getUserByLimitTest() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String, Integer> map = new HashMap<>();
        // 从第一条记录开始查2条记录
        map.put("startIndex", 0);
        map.put("pageSize", 2);
        for (User user : mapper.getUserByLimit(map)) {
            System.out.println(user);
        }
        sqlSession.close();
    }

8. 使用注解开发

  • 不需要Mapper.xml配置
  • 需要在mybatis-config.xml中绑定接口
    <!--  注解需要绑定接口  -->
    <mappers>
        <mapper class="com.yldog.dao.UserMapper"/>
    </mappers>

实例1 - 根据id查找用户:

    // 根据id查询用户
    @Select("select * from user where id = #{id}")
    User getUserById(@Param("id") int id);

注意

  • 在方法的参数前,若是基本类型或String类,在需要加上 @Param("参数"),这个参数sql语句中 #{参数} 所提取的参数
  • 结果:
User{id=1, name='张三', pwd='null'}
  • 由于属性名和字段名不一致,pwdnull,且这里使用注解无法像之前那样,在Mapper.xml中配置进行属性和字段的映射,所以注解适用于比较简单的sql语句,有局限性

实例2 - 新增用户

    @Insert("insert into user(id,name,pwd) values (#{id}, #{name}, #{password}) ")
    int insertUser(User user);
  • 注意这里方法的参数为引用类型,故前面不用加 @Param()
  • 以此类推,
    • 更新 \to 注解为 @Update("sql")
    • 删除 \to 注解为 @Delete("sql")

笔记第二部分链接:MyBatis 入门笔记+理解(二)

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

推荐阅读更多精彩内容