6.1 Spring Boot集成mybatis

6.1 Spring Boot集成mybatis

在SpringBoot集成MyBatis時(shí),我們將去掉和Mybatis配置相關(guān)的xml文件配置方式,用注解和屬性配置來(lái)代替這些繁雜的配置。

本節(jié)我們使用SpringBoot集成Mybatis開(kāi)發(fā)一個(gè)簡(jiǎn)易的H5性能測(cè)試的Web后臺(tái)。我們采用Java,Groovy混合編程的方式。

新建gradle工程

組織工程目錄結(jié)構(gòu)如下

.
├── build.gradle
├── settings.gradle
└── src
    ├── main
    │   ├── groovy
    │   ├── java
    │   └── resources
    │       └── application.yml
    └── test
        ├── groovy
        ├── java
        └── resources

9 directories, 3 files

配置build.gradle

添加mybatis-spring-boot-starter依賴(lài)

compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.1.1')

該starter將會(huì)自動(dòng)配置好Spring Boot集成mybatis需要的mybatis,mybatis-spring等所有依賴(lài)。這個(gè)starter的依賴(lài)樹(shù)如下圖所示:

其中,mybatis-spring-boot-autoconfigure會(huì)完成我們之前使用xml配置mybatis使用的sqlmap-config.xml類(lèi)似如下配置:

<?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>
    <settings>
        <setting name="lazyLoadingEnabled" value="false"></setting>
    </settings>
</configuration>

和data-source.xml文件配置數(shù)據(jù)源dataSource和TransactionManager,

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

        <!-- 數(shù)據(jù)源定義 -->
       <bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName">
            <value>com.mysql.jdbc.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:mysql://127.0.0.1:3306/h5perf?useUnicode=true&autoReconnect=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull</value>
        </property>
        <property name="username">
            <value>root</value>
        </property>
        <property name="password">
            <value>root</value>
        </property>
    </bean>


    <!-- TransactionManager定義。 -->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="transactionTemplate"
          class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
    </bean>


    <!-- 通過(guò)aop定義事務(wù)增強(qiáng)切面 -->
    <aop:config>
        <aop:pointcut id="serviceMethod" expression="execution(* com.easy.springboot.h5perf.service.*.*(..))"/>
        <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/>
    </aop:config>
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="find*" read-only="false"/>
            <tx:method name="save*" rollback-for="Exception"/>
            <tx:method name="new*" rollback-for="Exception"/>
            <tx:method name="update*" rollback-for="Exception"/>
            <tx:method name="*" rollback-for="Exception"/>
        </tx:attributes>
    </tx:advice>

    <!-- MyBatis配置sqlSessionFactory, SQL Map -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 自動(dòng)掃描entity目錄 -->
        <property name="typeAliasesPackage" value="com.easy.springboot.h5perf.model"/>
        <!-- 顯式指定Mapper文件位置 -->
        <property name="mapperLocations"
                  value="classpath:/sqlmap/*Mapper.xml"/>
        <property name="configLocation" value="classpath:sqlmap-config.xml"></property>
    </bean>
    <!-- 掃描basePackage下所有以@MyBatisRepository標(biāo)識(shí)的 接口 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"
          p:basePackage="com.alibaba.swork.info.common.mapper"/>

</beans>

配置application.yml

配置數(shù)據(jù)源

spring:
    datasource:
        name: h5perf
        url: jdbc:mysql://localhost:3306/h5perf?autoReconnect=true&useSSL=false
        username: root
        password: root
        # 使用druid數(shù)據(jù)源
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver

配置mybatis

mybatis:
    type-aliases-package: com.easy.springboot.h5perf.model
    configuration:
        # 開(kāi)啟mybatis開(kāi)啟數(shù)據(jù)庫(kù)字段自動(dòng)映射駝峰命名規(guī)則java屬性
        map-underscore-to-camel-case: true
        default-fetch-size: 100
        default-statement-timeout: 30

數(shù)據(jù)庫(kù)表結(jié)構(gòu)

CREATE DATABASE IF NOT EXISTS `h5perf`
  DEFAULT CHARACTER SET utf8;
USE `h5perf`;

DROP TABLE IF EXISTS `test_case`;

CREATE TABLE `test_case` (
  `id`             INT(11) NOT NULL AUTO_INCREMENT,
  `container_type` VARCHAR(45)      DEFAULT NULL
  COMMENT '‘ORIGIN_WEBVIEW’,’UC_WEBVIEW’',
  `test_url`       VARCHAR(45)      DEFAULT NULL,
  `net_type`       VARCHAR(45)      DEFAULT NULL
  COMMENT '‘WIFI’,‘3G’,‘4G’',
  `gmt_created`    DATETIME         DEFAULT NULL,
  `gmt_modified`   DATETIME         DEFAULT NULL,
  PRIMARY KEY (`id`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8;


DROP TABLE IF EXISTS `test_resource`;

CREATE TABLE `test_resource` (
  `id`            INT(11) NOT NULL AUTO_INCREMENT,
  `test_case_id`  INT(11)          DEFAULT NULL,
  `start_time`    BIGINT(11)       DEFAULT NULL,
  `resource_url`  VARCHAR(500)     DEFAULT NULL,
  `resource_type` VARCHAR(45)      DEFAULT NULL
  COMMENT 'IMG,JS,CSS,OTHER',
  `resource_time` BIGINT(11)       DEFAULT NULL
  COMMENT 'onLoadResource資源時(shí)間戳',
  `resource_size` BIGINT(11)       DEFAULT NULL
  COMMENT '資源大小',
  `gmt_created`   DATETIME         DEFAULT NULL,
  PRIMARY KEY (`id`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8;


DROP TABLE IF EXISTS `test_time_data`;

CREATE TABLE `test_time_data` (
  `id`               INT(11) NOT NULL AUTO_INCREMENT,
  `test_case_id`     INT(11)          DEFAULT NULL,
  `on_recieve_title` VARCHAR(45)      DEFAULT NULL
  COMMENT '近似白屏?xí)r間',
  `on_page_finished` VARCHAR(45)      DEFAULT NULL
  COMMENT '頁(yè)面加載完成時(shí)間',
  `dom_content_load` VARCHAR(45)      DEFAULT NULL
  COMMENT 'dom內(nèi)容加載完成時(shí)間',
  `load`             VARCHAR(45)      DEFAULT NULL
  COMMENT '資源加載完成時(shí)間',
  `gmt_created`      DATETIME         DEFAULT NULL,
  PRIMARY KEY (`id`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

Model層代碼

我們?cè)赼pplication.yml中開(kāi)啟了mybatis開(kāi)啟數(shù)據(jù)庫(kù)字段自動(dòng)映射駝峰命名規(guī)則java屬性:

map-underscore-to-camel-case: true

所以,我們model類(lèi)的屬性命名規(guī)則按照駝峰命名規(guī)則。

TestCase.groovy代碼:

package com.easy.springboot.h5perf.model

/**
 * Created by jack on 2017/4/22.
 */
class TestCase {
    Integer id
    String containerType
    String testUrl
    String netType
    Date gmtCreated
    Date gmtModified

    @Override
    String toString() {
        return "TestCase{" +
                "id=" + id +
                ", containerType='" + containerType + '\'' +
                ", testUrl='" + testUrl + '\'' +
                ", netType='" + netType + '\'' +
                ", gmtCreated=" + gmtCreated +
                ", gmtModified=" + gmtModified +
                '}';
    }
}


Mapper接口層代碼

我們以往的通過(guò)xml配置顯式指定Mapper文件位置:

<!-- 顯式指定Mapper文件位置 -->
        <property name="mapperLocations"
                  value="classpath:/sqlmap/*Mapper.xml"/>

我們現(xiàn)在用注解方式,直接在接口上添加@Mapper注解即可。代碼示例TestCaseMapper.groovy

package com.easy.springboot.h5perf.mapper

import com.easy.springboot.h5perf.model.TestCase
import org.apache.ibatis.annotations.Insert
import org.apache.ibatis.annotations.Mapper
import org.apache.ibatis.annotations.Options
import org.apache.ibatis.annotations.Param
import org.apache.ibatis.annotations.Select

@Mapper
interface TestCaseMapper {

    @Insert([
            "insert into test_case",
            "set container_type = #{t.containerType},",
            "test_url = #{t.testUrl},",
            "net_type = #{t.netType},",
            "gmt_created = #{t.gmtCreated},",
            "gmt_modified = #{t.gmtModified}"
    ])
    @Options(useGeneratedKeys = true, keyProperty = "t.id")
    int insert(@Param("t") TestCase testCase)

    @Select("select * from test_case")
    List<TestCase> findAll()

    @Select("select * from test_case where net_type=#{netType}")
    List<TestCase> findByNetType(@Param(value="netType")String netType)

    @Select("select * from test_case where id=#{id} limit 1")
    TestCase findOne(@Param(value="id")Integer id)

    int insertSelective(TestCase record)
}

Controller層代碼

package com.easy.springboot.h5perf.controller

import com.easy.springboot.h5perf.model.TestCase
import com.easy.springboot.h5perf.result.Result
import com.easy.springboot.h5perf.service.TestCaseService
import com.github.pagehelper.PageInfo
import groovy.json.JsonOutput
import io.swagger.annotations.Api
import io.swagger.annotations.ApiImplicitParam
import io.swagger.annotations.ApiOperation
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseBody

/**
 * Created by jack on 2017/4/22.
 * http://springfox.github.io/springfox/docs/current/#springfox-samples
 */

//在Controller上使用@Api會(huì)生成這個(gè)Controller的整體描述
@Api(value = "測(cè)試任務(wù)管理", tags = ["測(cè)試任務(wù)管理API"], description = "描述信息")
@Controller
class TestCaseController {
    @Autowired
    TestCaseService testCaseService

    @PostMapping("/saveTestCase")
    @ResponseBody
    def saveTestCase(TestCase testCase) {
        testCase.gmtCreated = new Date()
        println("testCase===" + testCase)
        int insert = testCaseService.save(testCase)
        println("testCase===" + testCase)
        Result result = new Result()
        if (insert == 1) {
            result.success = true
            result.result = testCase
        } else {
            result.success = false
            result.result = testCase
        }
        def jsonOutput = new JsonOutput()
        println("saveTestCase result===" + jsonOutput.toJson(result))
        result
    }

    @GetMapping("/listTestCase")
    def listTestCase(Model model,
                     @RequestParam(value = "pageNo", required = false) Integer pageNo,
                     @RequestParam(value = "pageSize", required = false) Integer pageSize) {
        def pageInfo = testCaseService.findAll(pageNo, pageSize)
        model.addAttribute("testCaseList", pageInfo.list)
        model.addAttribute("pageNo", pageInfo.pageNum)
        model.addAttribute("totalPage", pageInfo.pages)
        model.addAttribute("prePage", pageInfo.prePage)
        model.addAttribute("nextPage", pageInfo.nextPage)
        model.addAttribute("hasPreviousPage", pageInfo.hasPreviousPage)
        model.addAttribute("hasNextPage", pageInfo.hasNextPage)

        "/test_case/list"
    }

//    在具體方法上使用@ApiOperation可以生成接口的描述
    @ApiOperation(value = "list all TestCase Json", notes = "listTestCaseJson", produces = "application/json")
    @GetMapping("/listTestCaseJson")
    @ResponseBody
    def listTestCaseJson(@RequestParam(value = "pageNo", required = false) Integer pageNo,
                         @RequestParam(value = "pageSize", required = false) Integer pageSize) {
        testCaseService.findAll(pageNo, pageSize)
    }

    @ApiOperation(value = "findOne TestCase Json", notes = "findOne TestCase", produces = "application/json")
//  在方法上使用@ApiImplicitParam可以增加對(duì)參數(shù)等的描述
    @ApiImplicitParam(name = "id",
            value = "測(cè)試任務(wù)ID",
            dataType = "Integer",//This can be the class name or a primitive
            required = true,
            paramType = "query")
//Valid values are {@code path}, {@code query}, {@code body}, {@code header} or {@code form}
    @GetMapping("/findOne")
    @ResponseBody
    def findOne(@RequestParam(value = "id", required = true) Integer id) {
        testCaseService.findOne(id)
    }

    @ApiOperation(value = "findByNetType TestCase Json", notes = "findByNetType TestCase", produces = "application/json")
//  在方法上使用@ApiImplicitParam可以增加對(duì)參數(shù)等的描述
    @ApiImplicitParam(name = "netType",
            value = "findByNetType",
            dataType = "String",//This can be the class name or a primitive
            required = true,
            paramType = "query")
    @GetMapping("/findByNetType")
    @ResponseBody
    def findByNetType(@RequestParam(value = "netType", required = false) String netType,
                      @RequestParam(value = "pageNo", required = false) Integer pageNo,
                      @RequestParam(value = "pageSize", required = false) Integer pageSize
    ) {
        testCaseService.queryByPage(netType, pageNo, pageSize)
    }


}

使用PageHelper實(shí)現(xiàn)后端分頁(yè)

(1)添加pagehelper-spring-boot-starter

//   pagehelper
    compile('com.github.pagehelper:pagehelper-spring-boot-starter:1.1.0')

(2)Service層代碼實(shí)現(xiàn)

接口類(lèi) TestCaseService.groovy

package com.easy.springboot.h5perf.service

import com.easy.springboot.h5perf.model.TestCase
import com.github.pagehelper.PageInfo

/**
 * Created by jack on 2017/4/23.
 */
interface TestCaseService {
    def save(TestCase testCase)

    PageInfo<TestCase> findAll(Integer pageNo, Integer pageSize)

    TestCase findOne(Integer id)

    PageInfo<TestCase> queryByPage(String netType, Integer pageNo, Integer pageSize)

}



實(shí)現(xiàn)類(lèi)TestCaseServiceImpl.groovy

package com.easy.springboot.h5perf.service

import com.easy.springboot.h5perf.mapper.TestCaseMapper
import com.easy.springboot.h5perf.model.TestCase
import com.github.pagehelper.PageHelper
import com.github.pagehelper.PageInfo
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

/**
 * Created by jack on 2017/4/23.
 */
@Service
class TestCaseServiceImpl implements TestCaseService {
    @Autowired
    TestCaseMapper testCaseMapper;

    @Override
    def save(TestCase testCase) {
        testCaseMapper.insert(testCase)
    }

    @Override
    PageInfo<TestCase> findAll(Integer pageNo, Integer pageSize) {
        pageNo = pageNo == null ? 1 : pageNo
        pageSize = pageSize == null ? 10 : pageSize
        PageHelper.startPage(pageNo, pageSize)
        List<TestCase> testCaseList = testCaseMapper.findAll()
        //用PageInfo對(duì)結(jié)果進(jìn)行包裝
        PageInfo<TestCase> testCasePageInfo = new PageInfo<>(testCaseList)
        testCasePageInfo

    }

    @Override
    TestCase findOne(Integer id) {
        testCaseMapper.findOne(id)
    }

    @Override
    PageInfo<TestCase> queryByPage(String netType, Integer pageNo, Integer pageSize) {
        pageNo = pageNo == null ? 1 : pageNo
        pageSize = pageSize == null ? 10 : pageSize
        PageHelper.startPage(pageNo, pageSize)

        List<TestCase> testCaseList = testCaseMapper.findByNetType(netType)
        //用PageInfo對(duì)結(jié)果進(jìn)行包裝
        PageInfo<TestCase> testCasePageInfo = new PageInfo<>(testCaseList)
        testCasePageInfo
    }
}

(3)Controller層與前端代碼集成

傳到前端的Model層數(shù)據(jù):

    @GetMapping("/listTestCase")
    def listTestCase(Model model,
                     @RequestParam(value = "pageNo", required = false) Integer pageNo,
                     @RequestParam(value = "pageSize", required = false) Integer pageSize) {
        def pageInfo = testCaseService.findAll(pageNo, pageSize)
        model.addAttribute("testCaseList", pageInfo.list)
        model.addAttribute("pageNo", pageInfo.pageNum)
        model.addAttribute("totalPage", pageInfo.pages)
        model.addAttribute("prePage", pageInfo.prePage)
        model.addAttribute("nextPage", pageInfo.nextPage)
        model.addAttribute("hasPreviousPage", pageInfo.hasPreviousPage)
        model.addAttribute("hasNextPage", pageInfo.hasNextPage)

        "/test_case/list"
    }

前端使用Bootstrap pagination樣式類(lèi):

    <table class="table table-hover">
        <thead>
        <th>Id</th>
        <th>testUrl</th>
        <th>containerType</th>
        <th>netType</th>
        <th>gmtCreated</th>
        </thead>
        <tbody>
        <#list testCaseList as tc >
        <tr>
            <td>${tc.id}</td>
            <td><a href="${tc.testUrl}">${tc.testUrl}</a></td>
            <td>${tc.containerType}</td>
            <td>${tc.netType}</td>
            <td>${tc.gmtCreated?string('yyyy/MM/dd HH:mm:ss')}</td>
        </tr>
        </#list>
        </tbody>
    </table>


    <div class="center">
        <ul class="pagination">
            <li><a href="/listTestCase?pageNo=${prePage}&pageSize=10">?</a></li>
        <#list 1..totalPage as index>
            <#if index=pageNo>
                <li class="active"><a href="/listTestCase?pageNo=${index}&pageSize=10">${index}</a></li>
            <#else>
                <li><a href="/listTestCase?pageNo=${index}&pageSize=10">${index}</a></li>
            </#if>
        </#list>
            <li><a href="/listTestCase?pageNo=${nextPage}&pageSize=10">?</a></li>
        </ul>
    </div>

(4)運(yùn)行測(cè)試

第1頁(yè)
第2頁(yè)

另外,我們還可以集成Druid對(duì)數(shù)據(jù)庫(kù)進(jìn)行監(jiān)控。相關(guān)的資料可以去網(wǎng)上搜索學(xué)習(xí)。本章節(jié)不再贅述。

本節(jié)完整的工程源代碼:https://github.com/EasySpringBoot/h5perf

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,310評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,951評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,796評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,566評(píng)論 6 407
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,055評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,303評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,799評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,683評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,899評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,135評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,520評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,757評(píng)論 1 282
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,528評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,844評(píng)論 2 372

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