mybatis plus like 模糊檢索時(shí)支持%_作為普通參數(shù)使用糜值。

mybatis plus like 模糊檢索時(shí)支持%_作為普通參數(shù)使用丰捷。

[TOC]

總結(jié):

實(shí)現(xiàn)方式有多種:

1坯墨、通過(guò)mybatis plus 攔截器(com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor)實(shí)現(xiàn),只能不能處理第二步SQL執(zhí)行瓢阴。
2畅蹂、通過(guò)mybatis 原生攔截器(org.apache.ibatis.plugin.Interceptor)實(shí)現(xiàn),不能解決QueryWrapper的問(wèn)題荣恐。
3、使用工具類(lèi)SqlUtils.convertToSQLSafeValue 處理所有的like 傳參累贤。缺點(diǎn)是侵入代碼叠穆。
4、spring 過(guò)濾器攔截特殊字符臼膏,通過(guò)自定義異常返回硼被。此處不寫(xiě)。

都可以實(shí)現(xiàn)渗磅,根據(jù)各自的攔截器實(shí)現(xiàn)原理嚷硫,在分頁(yè)處理上兩者會(huì)有明顯不同。

mybatis plus分頁(yè)處理始鱼,會(huì)執(zhí)行兩遍SQL查詢(xún):第一次執(zhí)行SQL獲取總數(shù)量仔掸, 第二次執(zhí)行根據(jù)總數(shù)量進(jìn)行分頁(yè)查詢(xún)。在只有第一步總數(shù)量有效的情況下會(huì)執(zhí)行第二次查詢(xún)医清。攔截器只作用在第二步上起暮,關(guān)鍵代碼如下:

com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor 代碼 :

// com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor
// 所有攔截器
for (InnerInterceptor query : interceptors) {
    // 執(zhí)行查詢(xún),確認(rèn)有無(wú)結(jié)果
    if (!query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)) {
        // 無(wú)結(jié)果不再執(zhí)行query(mybatis plus 攔截器)
        return Collections.emptyList();
    }
    // 只有成功獲取數(shù)量后会烙,才會(huì)調(diào)用mybatis plus 攔截器负懦。
    query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
}
CacheKey cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
// 執(zhí)行SQL
return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);

mybatis 同時(shí)可以作用在所有的SQL查詢(xún)上,也就是上面myabtis plus 的分頁(yè)兩步執(zhí)行柏腻,mybatis 攔截器都可以攔截纸厉。但是此種方式只能處理掉“?”占位符所使用的參數(shù),并不能處理 QueryWrapper.like()相關(guān)接口傳參五嫂,如果使用QueryWrapper傳參需要使用mybatis的sql 使用方式颗品。

先放兩種實(shí)現(xiàn)方式的共用代碼:

1、Mybatis 工具類(lèi)

package com.commons.mybatis.utils;

import com.commons.utils.SqlUtils;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@NoArgsConstructor
public class MyBatisUtil {

    /**
     * 檢查sql中贫导,是否含有l(wèi)ike查詢(xún)
     */
    public static final Pattern LIKE_PARAM_PATTERN = Pattern.compile("like\\s(concat)?[\\w'\"\\(\\)\\%,\\s\\n\\t]*\\?", Pattern.CASE_INSENSITIVE);

    public static void escapeParameterIfContainingLike(BoundSql boundSql) {
        if (boundSql == null) {
            return;
        }
        String prepareSql = boundSql.getSql();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

        // 找到 like 后面的參數(shù)
        List<Integer> position = findLikeParam(prepareSql);
        if (position.isEmpty()) {
            return;
        }

        List<ParameterMapping> likeParameterMappings = new ArrayList<>(parameterMappings.size());

        // 復(fù)制
        MetaObject metaObject = SystemMetaObject.forObject(boundSql.getParameterObject());
        for (ParameterMapping m : parameterMappings) {
            if (!metaObject.hasGetter(m.getProperty())) {
                continue;
            }
            boundSql.setAdditionalParameter(m.getProperty(), metaObject.getValue(m.getProperty()));
        }

        for (int i = 0; i < position.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(position.get(i));
            likeParameterMappings.add(parameterMapping);
        }
        // 覆蓋 轉(zhuǎn)義字符
        delegateMetaParameterForEscape(boundSql, likeParameterMappings);
    }

    /**
     * @param boundSql 原 boundSql
     * @param likeParameterMappings 需要轉(zhuǎn)義的參數(shù)
     * @return 支持轉(zhuǎn)義的 boundSql
     */
    public static void delegateMetaParameterForEscape(
            BoundSql boundSql,
            List<ParameterMapping> likeParameterMappings) {

        log.debug("like String Escape parsing ...");

        MetaObject metaObject = SystemMetaObject.forObject(boundSql.getParameterObject());

        for (ParameterMapping mapping : likeParameterMappings) {

            String property = mapping.getProperty();

            if (!metaObject.hasGetter(property)) {
                continue;
            }

            Object value = metaObject.getValue(property);
            if (value instanceof String) {
                boundSql.setAdditionalParameter(property, convertToSQLSafeValue((String) value));
            }
        }
    }

    /**
     * 匹配like 位置抛猫, 如
     * like concat('%',?,'%')
     * like CONCAT('%',?,'%')
     * LIKE CONCAT('%', ?,'%')
     * lIKE conCAT('%', ?,'%')
     * like ?
     * @param prepareSql
     * @return
     */
    public static List<Integer> findLikeParam(String prepareSql) {

        if (StringUtils.isEmpty(prepareSql)) {
            return Collections.emptyList();
        }
        Matcher matcher = LIKE_PARAM_PATTERN.matcher(prepareSql);

        if (!matcher.find()) {
            return Collections.emptyList();
        }

        matcher.reset();
        int pos = 0;
        List<Integer> indexes = new ArrayList<>();
        while (matcher.find(pos)) {
            int start = matcher.start();
            int index = StringUtils.countMatches(prepareSql.substring(0, start), "?");
            indexes.add(index);
            pos = matcher.end();
        }
        return indexes;
    }


    /**
     * MySQL需要轉(zhuǎn)義的字段:\ % _
     */
    public static final Pattern PATTERN_MYSQL_ESCAPE_CHARS = Pattern.compile("(['_%\\\\]{1})");

    /**
     * 在SQL進(jìn)行l(wèi)ike時(shí)使用 ,mysql like時(shí)孩灯,參數(shù)使用傳值 SqlUtils.convertToSQLSafeValue(String)闺金; 禁止與escape 同時(shí)使用。
     *
     * 轉(zhuǎn)義mysql的特殊字符 包括 '\', '%', '_', ''',
     * @param str
     * @return 返回可能為null eg:
     *  1'2_3%4\ 5 ?\ 轉(zhuǎn)義后  1\'2\_3\%4\\\\ 5 ?\\\\
     *  null >> null
     *  """ >> ""
     *  "%" >> "\%"
     *  "\" >> "\\\\\"
     *  "_" >> "\_"
     *  "_%" >> "\_\%"
     */
    public static String convertToSQLSafeValue(String str) {
        return SqlUtils.convertToSQLSafeValue(str);
    }
}

2峰档、SqlUtils 工具類(lèi)

package com.commons.utils.SqlUtils

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * SQL相關(guān)工具類(lèi)
 * @creator DZ
 * @create 2021-08-25 17:25:52
 */
public class SqlUtils {

    private SqlUtils() {
    }

    private static final Logger logger = LoggerFactory.getLogger(SqlUtils.class);

    /**
     * MySQL需要轉(zhuǎn)義的字段:\ % _
     */
    public static final Pattern PATTERN_MYSQL_ESCAPE_CHARS = Pattern.compile("(['_%\\\\]{1})");

    /**
     * 在SQL進(jìn)行l(wèi)ike時(shí)使用 败匹,mysql like時(shí)寨昙,參數(shù)使用傳值 SqlUtils.convertToSQLSafeValue(String); 禁止與escape 同時(shí)使用掀亩。
     *
     * 轉(zhuǎn)義mysql的特殊字符 包括 '\', '%', '_', ''',
     * @param str
     * @return 返回可能為null eg:
     *  1'2_3%4\ 5 ?\ 轉(zhuǎn)義后  1\'2\_3\%4\\\\ 5 ?\\\\
     *  null >> null
     *  """ >> ""
     *  "%" >> "\%"
     *  "\" >> "\\\\\"
     *  "_" >> "\_"
     *  "_%" >> "\_\%"
     */
    public static String convertToSQLSafeValue(String str) {
        if (str == null) {
            return null;
        }
        Matcher matcher = PATTERN_MYSQL_ESCAPE_CHARS.matcher(str);
        int charSplitStart = 0;
        if (!matcher.find()) {
            return str;
        }
        StringBuilder sb = new StringBuilder();
        matcher.reset();
        while (matcher.find()) {
            String ch = str.substring(matcher.start(), matcher.end());
            sb.append(str, charSplitStart, matcher.start())
                    .append('\\').append("\\".equals(ch) ? "\\\\\\" : ch);
            charSplitStart = matcher.end();
        }
        if (sb.length() == 0) return str;
        String result = sb.toString();
        logger.debug("對(duì)SQL參數(shù)進(jìn)行轉(zhuǎn)義:{} => {} ", str, result);
        return result;
    }

}

mybatis plus 攔截器實(shí)現(xiàn)方式

1舔哪、添加like 轉(zhuǎn)義攔截器

// mybatis plus config 
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加like 轉(zhuǎn)義攔截器
interceptor.addInnerInterceptor(new LikeStringEscapeInterceptor()); 

2、mybatis plus 轉(zhuǎn)義攔截器 : LikeStringEscapeInterceptor.java

需要實(shí)現(xiàn)com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor 接口

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.commons.mybatis.utils.MyBatisUtil;
import lombok.NoArgsConstructor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;

/**
 * Like 轉(zhuǎn)義 插件:<br/>
 * 在mybatis plus 配置此插件使用槽棍;mybatis plus 插件使用機(jī)制捉蚤,優(yōu)先使用原始參數(shù)進(jìn)行條件查詢(xún)。<br/>
 * 1炼七、如果 count 記錄為0 時(shí)缆巧,name將不再執(zhí)行任何before query 調(diào)用; <br/>
 * 2豌拙、如果 count 結(jié)果非0 時(shí)陕悬,執(zhí)行插件業(yè)務(wù)邏輯。 <br/>
 *
 * @create dz
 * @date 2021-08-26 16:34:59
 * @see MybatisPlusInterceptor#intercept(org.apache.ibatis.plugin.Invocation)
 * for (InnerInterceptor query : interceptors) {
 * if (!query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)) {
 * return Collections.emptyList();
 * }
 * query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
 * }
 * <p>
 * 使用方法:
 * <ol>
 * <li> 添加插件 到mybatis plus </li>
 * </ol>
 * </p>
 */
@NoArgsConstructor
public class LikeStringEscapeInterceptor implements InnerInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(LikeStringEscapeInterceptor.class);

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        // 到此按傅,說(shuō)明mybatis plus 預(yù)執(zhí)行有結(jié)果捉超。
        logger.debug("LikeStringEscapeInterceptor beforeQuery");
        SqlCommandType sqlCommandType = ms.getSqlCommandType();
        StatementType statementType = ms.getStatementType();
        // 只處理 有參數(shù)的查詢(xún)語(yǔ)句
        if (sqlCommandType == SqlCommandType.SELECT && statementType == StatementType.PREPARED) {
            MyBatisUtil.escapeParameterIfContainingLike(boundSql);
        }
    }
}


3、結(jié)果

3.1. 數(shù)據(jù)庫(kù)中有【默認(rèn)】開(kāi)始的數(shù)據(jù)唯绍。設(shè)定參數(shù)【默認(rèn)%】分頁(yè)兩步驟SQL拼岳,只有第二步執(zhí)行了轉(zhuǎn)義。

stPage_mpCount : ==>  Preparing: SELECT COUNT(1) FROM (SELECT r
stPage_mpCount : ==> Parameters: 默認(rèn)%(String)
stPage_mpCount : <==      Total: 1
peInterceptor  : LikeStringEscapeInterceptor beforeQuery
peInterceptor  : like String Escape parsing ...
ils.SqlUtils   : 對(duì)SQL參數(shù)進(jìn)行轉(zhuǎn)義:默認(rèn)% => 默認(rèn)\% 
peInterceptor  : boundSql AdditionalParameter property: param.n
ListPage       : ==>  Preparing: SELECT role.id, role.name, rol
ListPage       : ==> Parameters: 默認(rèn)\%(String), 10(Long)

3.2. 數(shù)據(jù)庫(kù)無(wú)【九月天】開(kāi)頭的數(shù)據(jù)推捐,不再執(zhí)行插件邏輯裂问。設(shè)定參數(shù)【九月天】,只有第一步計(jì)數(shù)SQL 牛柒,無(wú)轉(zhuǎn)義后SQL

Param_mpCount    : ==>  Preparing: SELECT COUNT(1) FROM 
Param_mpCount    : ==> Parameters: 九月天%(String)
Param_mpCount    : <==      Total: 1 

mybatis 原生攔截器實(shí)現(xiàn)

1堪簿、LikeStringEscapeInterceptorForMybatis.java

實(shí)現(xiàn)com.commons.mybatis.interceptor.LikeStringEscapeInterceptorForMybatis代碼

package com.commons.mybatis.interceptor;

import com.commons.mybatis.utils.MyBatisUtil;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * Like 轉(zhuǎn)義
 * <p>
 * 使用方法:
 * <ol>
 * <li> 添加插件 </li>
 * </ol>
 * </p>
 */
@Component
@Intercepts(
        {
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class,
                        Object.class, RowBounds.class, ResultHandler.class}),
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class,
                        Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        }
)
public class LikeStringEscapeInterceptorForMybatis implements Interceptor {

    private static final Logger logger = LoggerFactory.getLogger(LikeStringEscapeInterceptorForMybatis.class);

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        RowBounds rowBounds = (RowBounds) args[2];
        ResultHandler resultHandler = (ResultHandler) args[3];
        Executor executor = (Executor) invocation.getTarget();
        CacheKey cacheKey;
        BoundSql boundSql;
        //由于邏輯關(guān)系,只會(huì)進(jìn)入一次
        if (args.length == 4) {
            //4 個(gè)參數(shù)時(shí)
            boundSql = ms.getBoundSql(parameter);
            cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
        } else {
            //6 個(gè)參數(shù)時(shí)
            cacheKey = (CacheKey) args[4];
            boundSql = (BoundSql) args[5];
        }

        SqlCommandType sqlCommandType = ms.getSqlCommandType();
        StatementType statementType = ms.getStatementType();
        // 只處理 有參數(shù)的查詢(xún)語(yǔ)句
        if (sqlCommandType == SqlCommandType.SELECT
                && statementType == StatementType.PREPARED) {
            MyBatisUtil.escapeParameterIfContainingLike(boundSql);
            return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
        }
        return invocation.proceed();
    }

}

2皮壁、結(jié)果:

2.1. 查詢(xún)條件:【九月天%】椭更,數(shù)據(jù)無(wú)【九月天】開(kāi)始的數(shù)據(jù)。mybatis plus 只查詢(xún)了一次蛾魄,執(zhí)行了轉(zhuǎn)義虑瀑。OK

MyBatisUtil  : like String Escape parsing ...
s.SqlUtils   : 對(duì)SQL參數(shù)進(jìn)行轉(zhuǎn)義:九月天% => 九月天\% 
Dept_mpCount : ==>  Preparing: SELECT COUNT(1) FROM (SELECT DIST
Dept_mpCount : ==> Parameters: 001002031(String), 九月天\%(String)
Dept_mpCount : <==      Total: 1

2.2. 查詢(xún)條件:【g%h】 ,數(shù)據(jù)中有含有“g%h”的文本數(shù)據(jù)滴须。mybatisplus執(zhí)行了分頁(yè)處理:兩步都執(zhí)行了轉(zhuǎn)義操作舌狗。OK

ils.MyBatisUtil  : like String Escape parsing ...
utils.SqlUtils   : 對(duì)SQL參數(shù)進(jìn)行轉(zhuǎn)義:g%h => g\% 
geInDept_mpCount : ==>  Preparing: SELECT COUNT(1) FROM (SELECT D
geInDept_mpCount : ==> Parameters: 001002031(String), g\%(String)
geInDept_mpCount : <==      Total: 1
ils.MyBatisUtil  : like String Escape parsing ...
utils.SqlUtils   : 對(duì)SQL參數(shù)進(jìn)行轉(zhuǎn)義:g%h => g\% 
ageInDept        : ==>  Preparing: select u.id, u.username, u.rea
ageInDept        : ==> Parameters: 001002031(String), g\%(String)
ageInDept        : <==      Total: 1

2.3 使用注意

qw.like(SysUserEntity::getUsername, "%"); 

// 日志如下傳入
// .SqlUtils : 對(duì)SQL參數(shù)進(jìn)行轉(zhuǎn)義:%%% => %%%
// lectList : ==> Preparing: SELECT id,username
// lectList : ==> Parameters: %%%(String)

queryWrapper.apply("`" + column_name + "` LIKE concat('%',{0})", value);

此方式OK

參考:
Mybatis 攔截器實(shí)現(xiàn) Like 通配符轉(zhuǎn)義

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市扔水,隨后出現(xiàn)的幾起案子痛侍,更是在濱河造成了極大的恐慌,老刑警劉巖魔市,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件主届,死亡現(xiàn)場(chǎng)離奇詭異赵哲,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)君丁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)枫夺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人绘闷,你說(shuō)我怎么就攤上這事橡庞。” “怎么了簸喂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵毙死,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我喻鳄,道長(zhǎng),這世上最難降的妖魔是什么确封? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任除呵,我火速辦了婚禮,結(jié)果婚禮上爪喘,老公的妹妹穿的比我還像新娘颜曾。我一直安慰自己,他們只是感情好秉剑,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布泛豪。 她就那樣靜靜地躺著,像睡著了一般侦鹏。 火紅的嫁衣襯著肌膚如雪诡曙。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天略水,我揣著相機(jī)與錄音价卤,去河邊找鬼。 笑死渊涝,一個(gè)胖子當(dāng)著我的面吹牛慎璧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播跨释,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼胸私,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了鳖谈?” 一聲冷哼從身側(cè)響起岁疼,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚯姆,沒(méi)想到半個(gè)月后五续,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體洒敏,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年疙驾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凶伙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡它碎,死狀恐怖函荣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情扳肛,我是刑警寧澤傻挂,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站挖息,受9級(jí)特大地震影響金拒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜套腹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一绪抛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧电禀,春花似錦幢码、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至政基,卻和暖如春贞铣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腋么。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工咕娄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人珊擂。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓圣勒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親摧扇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子圣贸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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