Mybatis

一、框架:

半成品,可以理解为房子的大框,之后砌墙会在其基础上。

二、简介

MyBatis 本是[apache]的一个开源项目[iBatis], 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的[持久层]框架。

Mybatis是一个持久层框架。代替JDBC技术完成与数据库交互功能的框架,操作过程中,调用底层封装好的API,代码会被解析成JDBC源码进行功能实现。

三、优点

1、基于SQL语法的,简单易学。

2、jdbc将sql语句写在java语句中,属于硬编码,而框架把SQL语句被封装在配置文件中,方便程序的维护,降低程序的耦合度。

3、程序调试方便。
4、jdbc需要将查询结果映射到实体类中,而框架可以省去这一步骤,查询即得到映射好的对象。

四、框架的搭建:

  1. 创建一个普通的JAVA工程。

  2. 引入jar包

Mybatis-3.2.7.jar:Mybatis的核心jar包

Ojdbc14.jar:驱动Jar

Commons-logging-1.1.1.jar
Log4j.jar:记录日志的jar

Log4j.properties 消息资源文件

3、在src中引入SqlMapConfig.xml文件(名字可随便取,但惯例都叫此,之后会有专门的地方根据名字读取文件),现在专门解释一下该文件,文件后缀名时xml,可扩展标记语言。

<?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">

是处理指令,能够解析MyBatis元素
③根元素

<configuration></configuration>

一个配置文件只能有一个根元素
④在<configuration></configuration>中配置数据源,所谓数据源就是数据的来源,可以理解为数据库

<environments default="oracle">
    <environment id="oracle">
        <!-- 事务管理器 -->
        <transactionManager type="jdbc"></transactionManager>
        <!-- 配置数据源 -->
        <dataSource type="POOLED">
            <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
            <property name="url" value="jdbc:oracle:thin:@localhost:1521:ORCL"/>
            <property name="username" value="scott"/>
            <property name="password" value="password"/>
        </dataSource>
    </environment>
</environments> 

a.default中的值决定具体使用哪一数据源,所谓数据源就是数据的来源,即数据库
b.在指定的数据源中,首先配置事务控制器
c.配置数据源的相关信息,POOLED表示使用连接池进行连接
d.property中的name都有特定的含义,不允许改变
4、建表语句

-- Create table
create table T_USER
(
  id       NUMBER not null,
  username VARCHAR2(20),
  hiredate DATE,
  password VARCHAR2(20)
)
tablespace USERS
  pctfree 10
  initrans 1
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
-- Create/Recreate primary, unique and foreign key constraints 
alter table T_USER
  add constraint PK_T_USER_ID primary key (ID)
  using index 
  tablespace USERS
  pctfree 10
  initrans 2
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );

创建实体类

public class User {
    private int id;
    private String username;
        private String password;
    private Date hiredate;
    
}

5、创建接口UserMapper

public interface UserMapper {
    User getUser(int id);

}

6、创建与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">
<!-- namespace命名空间,作用就是对sql进行分类化管理-->
<mapper namespace="com.dao.UserMapper">
    <select id="getUser" parameterType="java.lang.Integer" resultType="com.bean.User">
        select * from t_user where id = #{value}
    </select>
</mapper>

①mapper是根元素
②<mapper namespace="com.dao.UserMapper">的namespace值必须写接口的全限定名,
③根元素中可以编写sql语句
④parameterType:指定输入参数类型,mybatis 从输入对象中获取参数值拼接在 sql 中。
resultType:指定输出结果类型,mybatis 将 sql 查询结果的一行记录数据映射为 resultType 指定类型的对象。
上述参数值需要引入全限定名
7、有的童鞋可能猜到了,你可以认为这个配置文件类似于接口的实现类,只不过我们不用implements关键字,而是在主配置文件中读取它

<environments default="oracle"> 
</environments>
<mappers>
        <!-- <mapper resource="com/dao/UserMapper.xml"/> -->
        <package name="com.dao"/>
</mappers>

8最后,我们编写测试类,主要经过如下步骤
①加载核心配置文件

Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");

②获取数据库连接池对象

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);

③获取与数据库的链接

SqlSession session = factory.openSession();

④通过连接拿到实体类对象

UserMapper userMapper = session.getMapper(UserMapper.class);

此时相关数据已经封装给了实体类

五、引入配置文件

在classpath下创建db.properties

jdbc.driver =oracle.jdbc.driver.OracleDriver

jdbc.url = jdbc:oracle:thin:@localhost:1521:neuedu

jdbc.username=scott

jdbc.password=tiger

此时在SqlMapConfig.xml文件中需要修改为

<!-- properties元素:
    resource属性,要引入的消息资源文件相对于src的路径 -->
    <properties resource="db.properties"></properties>
    <environments default="oracle">
        <environment id="oracle">
            <!-- 事务管理器 -->
            <transactionManager type="jdbc"></transactionManager>
            <!-- 配置数据源 -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

六、关于Mybatis的API

1、SqlSession
SqlSession 中封装了对数据库的操作,如:查询、插入、更新、删除等。
2、SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 用于创建 SqlSessionFacoty,SqlSessionFacoty 一旦创建完成就不需要SqlSessionFactoryBuilder 了,因为 SqlSession 是通过 SqlSessionFactory 生产,所以可以将SqlSessionFactoryBuilder 当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
3、SqlSessionFactory
SqlSessionFactory 是一个接口,接口中定义了 openSession 的不同重载方法,SqlSessionFactory 的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理 SqlSessionFactory。

public class SqlSessionFactoryUtil {
    //首先创建静态成员变量sqlSessionFactory,静态变量被所有的对象所共享。
    public static SqlSessionFactory sqlSessionFactory = null;
    public static SqlSessionFactory getSqlSessionFactory() {
        //如果sqlSessionFactory没有被创建就读取全局配置文件,假如已经被创建过了,就使用已经存在的sqlsessionfactory。
        //这样就有了单例模式的效果
        if(sqlSessionFactory==null){
            String resource = "SqlMapConfig.xml";
            try {
                Reader reader = Resources.getResourceAsReader(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return sqlSessionFactory;
    }
}

七、类别名

1、以上提到,在xml文件中配置SQL语句时,需要使用全限定名,因此非常不便,需要配置类别名,使用typeAliases元素

<typeAliases>
        <!-- typeAlias元素
        type,全限定名
        alias,类别名
         -->
        <typeAlias alias="User" type="com.bean.User"/>
</typeAliases>

此时查询就可以使用User代替com.bean.User,注意,该元素必须在environments元素以前写,因为xml文件对元素顺序有要求
2、内置别名
java中的类型可以直接使用,无需配置别名,如int,long,String,List,Map

八、核心配置文件

1、defualt属性
现在,让我们来说一说environments元素的default属性,这是一个必须有的属性,由于系统可能配置多个数据源,因此需要指定一个默认数据源,default的值与environment的id相对应,那就走该id所在的数据源

<environments default="oracle">
        <environment id="oracle">
        </environment>
         <environment id="oracle1">
        </environment>
    </environments>

还有一种情况,若我default的值指定了某一个数据源,是不是就连不了别的数据源?答案是否定的,SqlSessionFactoryBuilder有一个重载的方法能够指定连接其他数据源,如下代码所示

<environments default="oracle">
        <environment id="oracle">

        </environment>
        <environment id="oracle1">
            <transactionManager type="jdbc"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="kkk"/>
            </dataSource>
        </environment>
    </environments>
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader,"oracle1");

由于我在oracle1中密码错误,因此登录失败
2、事务管理器transactionManager
type属性,指定事务管理方式,可选值有
jdbc:利用传统jdbc方式进行事务处理,需要手动提交、回滚
MANAGED:让容器进行事务处理,此处容器指Spring容器,若配成此选项,意味着Mybatis不进行事务管理,交给Spring

3、dataSource元素
type属性,指定配置数据源连接的方式,可选择的值如下
POOLED,使用数据库连接池,使用能够存放多个已经生成好的数据库连接的区域,好处在于想拿连接时只需要open一下就可以拿到连接,不涉及对连接的管理
UNPOOLED,不使用数据库连接池,即使用原始连接方式,需要对连接管理
JNDI,使用第三方数据库连接池,POOLED表示使用Mybatis自己的连接池,如果想使用第三方,就选这一项,常见提供数据库连接池的第三方有tomcat自带的连接池,Spring容器
4、mappers
将应用中所有SQL映射文件配置到核心配置文件中,目的是加载核心配置文件时能够加载SQL映射文件,xml文件与以往java文件不同,java文件编译成字节码后,其他方法就可以调用其中的方法,想用xml文件必须加载
5、其他元素
如settings,可以用来优化配置,最多可以连几个连接,或同时最多可以执行几个SQL语句,几乎不常用,而且真正开发时也不需要你去做

九、SQL映射文件

我们之前拿最简单的查询做了例子演示框架搭建,现在,让我们看一下复杂的查询以及增删改如何去做,一般情况下,我们都是一个SQL映射文件对应一张表和一个实体类,有关该实体类的增删改查都在同一个映射文件中
1、回顾一下最简单的查询
select元素相关属性
id,必须存在的属性,值从概念上讲是唯一标示,不能随便写,应对应接口中方法的名
parameterType,若该方法有参,则必须有该属性,值和接口中参数的类型相对应
resultType,结果集的类型,经过查询,数据库中的信息自动封装,指定类型,就告诉框架你把信息封装成一个什么类型的对象
2、多个参数
需求:根据用户名和编号进行查询
接口中增加方法

User selectUser(String username,int id);

发现parameterType无法指定类型,
①加注解

User selectUser(@Param("username")String username,@Param("id")int id);
<select id="selectUser" resultType="User">
        select * from t_user where username = #{username} and id = #{id}
    </select>

②参数整合在一起,使用bean对象,

List<User> selectUser(User user);
<select id="selectUser" parameterType="User" resultType="User">
        select * from t_user where username = #{username} and id = #{id}
    </select>

注意,查询结果有多条,使用list集合作为返回值类型,resultType指定集合中元素的类型
3、删除
需求:根据id进行删除

void deleteUser(int id);
<delete id="deleteUser" parameterType="int">
        delete from t_user where id = #{value}
    </delete>

去测试类中执行发现并没有生效,没有自动提交,因此需要在try中调用session的commit方法,在catch中调用session的rollback方法
4、修改

void updateUser(User user);
<update id="updateUser" parameterType="User">
        update t_user set username = #{username},password = #{password}
        where id = #{id}
    </update>

5、插入

<!-- keyProperty属性主键列对应的属性的名称
    order属性BEFORE表示子查询在主查询前
     -->
    <insert id="insertUser">
        insert into t_user values(#{id},#{username},sysdate,#{password})
        <selectKey keyProperty="id" resultType="int" order="BEFORE">
            select max(id)+1 from t_user
        </selectKey>
    </insert>
void insertUser(User user);
User user = new User();
            user.setUsername("k");
            user.setPassword("k");
            userMapper.insertUser(user);
            session.commit();

十、特殊结果、参数

1、特殊参数类型使用
根据用户名和密码查询,比如此时用户名和密码不同时写在同一个实体类中,此时要使用一个参数就要用到Map

User selectUserByNAP(Map<String,String> map);
<select id="selectUserByNAP" parameterType="map" resultType="User">
        select * from t_user where username = #{m_username} and password = #{m_password}
    </select>
Map<String,String> map = new HashMap<String,String>();
            map.put("m_username", "l");
            map.put("m_password", "l");
            User user = userMapper.selectUserByNAP(map);
            System.out.println(user.getPassword());

sql语句中参数等于Map的键
2、特殊结果集使用
如果实体类属性与数据库列名不一样,就使用resultMap作为结果集类型

User selectUserByNAP1(Map<String,String> map);
<select id="selectUserByNAP1" parameterType="map" resultMap="userres">
        select * from t_user where username = #{m_username} and password = #{m_password}
    </select>
    <resultMap type="User" id="userres">
        <result column="password" property="password"></result>
    </resultMap>
Map<String,String> map = new HashMap<String,String>();
            map.put("m_username", "l");
            map.put("m_password", "l");
            User user = userMapper.selectUserByNAP1(map);
            System.out.println(user.getPassword());

type:查询结果最终得到的类型
3、建employee和department两张表,其中employee表有eid、eno、ename、deptid等字段,department表有did、dno、dname等字段

-- Create table
create table T_DEPARTMENT
(
  did   NUMBER(4) not null,
  dno   VARCHAR2(20),
  dname VARCHAR2(20)
)
tablespace USERS
  pctfree 10
  initrans 1
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
-- Create/Recreate primary, unique and foreign key constraints 
alter table T_DEPARTMENT
  add constraint PK_DID_DEPARTMENT primary key (DID)
  using index 
  tablespace USERS
  pctfree 10
  initrans 2
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
-- Create table
create table T_EMPLOYEE
(
  eid    NUMBER(4) not null,
  eno    VARCHAR2(20),
  ename  VARCHAR2(20),
  deptid NUMBER(4)
)
tablespace USERS
  pctfree 10
  initrans 1
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
-- Create/Recreate primary, unique and foreign key constraints 
alter table T_EMPLOYEE
  add constraint PK_EID_EMPLOYEE primary key (EID)
  using index 
  tablespace USERS
  pctfree 10
  initrans 2
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
alter table T_EMPLOYEE
  add constraint FK_DEPTID_EMPLOYEE foreign key (DEPTID)
  references T_DEPARTMENT (DID);
public class Employee {
    private int eid;
    private String eno;
    private String ename;
    private Department dept;
}

public class Department {
    private int did;
    private String dno;
    private String dname;
    private List<Employee> employees;
}

十一、多表查询

1、一对一关联
如果要做查询员工信息同时将部门信息同时查出来
(1)嵌套子查询

public Employee getEmployeeById(int eid);
<select id="getEmployeeById" parameterType="int" resultMap="empres">
        select * from t_employee where eid = #{eid}
    </select>
    <!-- 嵌套子查询
    association元素
    property属性嵌套查询的结果赋值给Employee中属性的名字 
    column属性外键字段的名称
    javaType属性嵌套子查询返回结果集的类型
    select属性嵌套的子查询对相应查询
    -->
    <resultMap type="Employee" id="empres">
        <association property="dept" column="deptid" javaType="Department" select="getDeptById"></association>
    </resultMap>
    <select id="getDeptById" parameterType="int" resultType="Department">
        select * from t_department where did = #{deptid}
    </select>
association元素
property属性嵌套查询的结果赋值给Employee中属性的名字 
column属性外键字段的名称
javaType属性嵌套子查询返回结果集的类型
select属性嵌套的子查询对相应查询

以上查询的做法实际上非常不好,仅做了一个多表查询就需要多次操作数据库
(2)嵌套结果

public Employee getEmployeeByNo(String eno);
<select id="getEmployeeByNo" parameterType="string" resultMap="empRes">
        select * 
        from t_employee e,t_department d
        where e.deptid = d.did
        and e.eno = #{eno}
    </select>
    <resultMap type="employee" id="empRes">
        <association property="dept" column="deptid" javaType="Department" resultMap="deptRes"></association>
    </resultMap>
    <resultMap type="department" id="deptRes">
        <result property="did" column="did"></result>
        <result property="dno" column="dno"></result>
        <result property="dname" column="dname"></result>
    </resultMap>
EmployeeDao employeeDao = session.getMapper(EmployeeDao.class);
            Employee employee = employeeDao.getEmployeeByNo("1");
            System.out.println(employee.getDept().getDid());

2、一对多关联
查询部门信息,并将该部门所有员工查询出来

Department getDepartmentById(int did);
<select id="getDepartmentById" parameterType="int" resultMap="deptRes">
        select *
        from t_employee e,t_department d
        where e.deptid = d.did
        and d.did = #{did}
    </select>
    <resultMap type="department" id="deptRes">
    <result property="did" column="did"></result>
    <result property="dno" column="dno"></result>
    <result property="dname" column="dname"></result>
    <!-- 一对多的关联查询
    collection元素
    property属性针对多个员工信息封装出的集合容器类型的对象,赋值给department类中的对应的属性
    ofType属性集合中每个元素的类型 -->
        <collection property="employees" ofType="employee">
            <result property="eid" column="eid"></result>
            <result property="eno" column="eno"></result>
            <result property="ename" column="ename"></result>
        </collection>
    </resultMap>
DepartmentDao departmentDao = session.getMapper(DepartmentDao.class);
            Department department = departmentDao.getDepartmentById(1);
            List<Employee> list = department.getEmployees();
            System.out.println(list.get(0).getEid());

3、向员工表中插入一条数据,同时指定其部门信息

<insert id="insert" parameterType="employee">
        insert into t_employee values (#{eid},#{eno},#{ename},#{dept.did})
        <selectKey keyProperty="eid"  resultType="int" order="BEFORE">
            select max(eid)+1 from t_employee
        </selectKey>
    </insert>
void insert(Employee e);
Employee employee = new Employee();
            employee.setEno("2");
            employee.setEname("Lily");
            Department department = new Department();
            department.setDid(1);
            employee.setDept(department);
            EmployeeDao employeeDao = session.getMapper(EmployeeDao.class);
            employeeDao.insert(employee);
            session.commit();

4、分页查询
利用pageHelper插件
首先引入jar包
jsqlparser-0.9.jar
pagehelper-3.6.4.jar
在核心配置文件中加入

<!-- plugins元素加在typeAliases下 -->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <!-- property元素指明和哪个数据库匹配使用 -->
            <property name="dialect" value="oracle"></property>
        </plugin>
    </plugins>

在查询映射文件中加入

<select id="getAll" resultType="user">
        select * from t_user
    </select>
List<User> getAll();
//获取第一页数据,每页显示1条
            Page page = PageHelper.startPage(1, 1);
            userMapper.getAll();
            System.out.println("共"+page.getTotal()+"条记录");
            List<User> list = page.getResult();
            for(User u:list){
                System.out.println(u.getPassword());
            }

5、组合查询

<!-- 条件查询where元素,补齐where关键字 -->
    <select id="getByCondition" parameterType="user" resultType="user">
        select * from t_user
        <!-- where 1=1 -->
        <where>
            <if test="id != null and id != 0">
                and id = #{id}
            </if>
            <if test="username != null and username != ''">
                and username = #{username}
            </if>
        </where>
    </select>

6、批量删除

<!-- 参数是数组,无需加参数
        foreach元素遍历集合容器对象
        collection属性指定的是方法传入的类型
        item属性每次遍历到的元素
        open属性遍历得到的结果的开始字符
        separator属性元素之间的分隔符
     -->
    <delete id="deleteUsers">
        delete from t_user where id in
        <foreach collection="array" item="item" open="(" close=")" separator=",">
            #{item}
        </foreach>
    </delete>
void deleteUsers(int[] ids);
int[] i = new int[2];
            i[0] = 3;
            i[1] = 4;
            userMapper.deleteUsers(i);
            session.commit();

7、缓存

User user = userMapper.getUser(1);
            user.setPassword("k");
            //修改查询到的user信息
            System.out.println(user.getPassword());
            user.setPassword("j");
            //再次查询理论上应该得到数据库中的内容,但是却是上述修改后的内容,这是因为Mybatis缓存
            //缓存是因为提高性能
            user = userMapper.getUser(1);
            System.out.println(user.getPassword());

此时需要在sql映射文件中加入flushCache="true"

<select id="getUser" parameterType="java.lang.Integer" resultType="User" flushCache="true">
        select * from t_user where id = #{value}
    </select>

当查询数据库返回结果时,mybatis会生成key、value形式的对象存在于缓存,在接下来在内存中针对查询结果做变更,相当于改变了缓存中的数据,再次查询不访问数据库,而是直接采用缓存中的数据

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

推荐阅读更多精彩内容