Spring-boot + Mybatis-plus 3.0-gamma 配置記錄

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatisplus-spring-boot</artifactId>
    <version>1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <java.version>1.8</java.version>
        <mybatis-plus-boot-starter.version>3.0-gamma</mybatis-plus-boot-starter.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>

        <!-- mybatis-plus begin -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>
        <!-- mybatis-plus end -->

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

這個(gè)工程里依賴 mysql, dbcp2, gson, lombok

工程的結(jié)構(gòu)

工程的結(jié)構(gòu)
  • config.mybatis mybatis的配置
  • config.mybatis.scanner 擴(kuò)展mybatis的mapper scanner
  • controller 接口控制器
  • db 訪問(wèn)數(shù)據(jù)庫(kù)相關(guān)的對(duì)象
  • db.entity 實(shí)體
  • db.mapper mybatis mapper
  • exception 例外處理器,用來(lái)處理controller里的例外
  • repository 倉(cāng)庫(kù)
  • resources.mapper mybatis mapper的xml文件
  • resources.META-INF 向spring注冊(cè)組件

application.yml 配置文件

#app
server:
    port: 8080

#spring
spring:
  devtools:
    restart:
      enabled: false

  # H2 DATABASE CONFIG
  datasource:
    type: org.apache.commons.dbcp2.BasicDataSource
    dbcp2:
      driver-class-name: com.mysql.jdbc.Driver
      username: xxxx
      password: xxxxx
      jmx-name: xxxxxx
      url: jdbc:mysql://xxxxxx:3306/xxxxxxx?zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&characterEncoding=UTF-8

mybatis:
  mapperScanner:
    basePackage: com.baomidou.springboot.db.mapper*


#mybatis
mybatis-plus:
  mapper-locations: classpath:/mapper/*Mapper.xml
  #實(shí)體掃描糙及,多個(gè)package用逗號(hào)或者分號(hào)分隔
  typeAliasesPackage: com.baomidou.springboot.db.entity
  typeEnumsPackage: com.baomidou.springboot.db.entity.enums
  global-config:
    #刷新mapper 調(diào)試神器
    db-config:
      #主鍵類型  0:"數(shù)據(jù)庫(kù)ID自增", 1:"用戶輸入ID",2:"全局唯一ID (數(shù)字類型唯一ID)", 3:"全局唯一ID UUID";
      id-type: id_worker
      #字段策略 0:"忽略判斷",1:"非 NULL 判斷"),2:"非空判斷"
      field-strategy: not_empty
      #駝峰下劃線轉(zhuǎn)換
      column-underline: true
      #數(shù)據(jù)庫(kù)大寫(xiě)下劃線轉(zhuǎn)換
      #capital-mode: true
      #邏輯刪除配置
      logic-delete-value: Y
      logic-not-delete-value: N
      db-type: mysql
    refresh: true
    sql-injector: com.baomidou.mybatisplus.extension.injector.LogicSqlInjector
      #自定義填充策略接口實(shí)現(xiàn)
      #meta-object-handler: com.baomidou.springboot.xxx
      #自定義SQL注入器
      #sql-injector: com.baomidou.springboot.xxx
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: false

mybatis-plus部分的配置中:

  • mapper-locations 存放mapper xml文件的位置
  • typeAliasesPackage 數(shù)據(jù)對(duì)象的目錄
  • typeEnumsPackage 數(shù)據(jù)對(duì)象中使用的enum定義
  • id-type id的生成策略
  • field-strategy 字段處理策略
  • column-underline 對(duì)代碼中的駝峰字段映射到數(shù)據(jù)庫(kù)字段的策略
  • logic-delete-value 邏輯刪除的標(biāo)記值
  • logic-not-delete-value 邏輯未刪除的標(biāo)記值
  • sql-injector SQL注入器详幽。 這里為了處理邏輯刪除操作,使用了LogicSqlInjector注入器
  • db-type 數(shù)據(jù)庫(kù)類型。支持主流的數(shù)據(jù)庫(kù)
  • map-underscore-to-camel-case 數(shù)據(jù)庫(kù)字段與數(shù)據(jù)對(duì)象字段的映射策略

類的說(shuō)明

MybatisPlusConfig

@Configuration
public class MybatisPlusConfig {
    ... 一些配置唇聘, 例如版姑,配置 datasource, sqlSessionFactory, transaction manager 等等
}

MybatisPlusAutoConfiguration

@Configuration
public class MybatisPlusAutoConfiguration {

    /**
     * 將自定義的converter注冊(cè)到ConversionService中
     *
     * @return
     */
    @Bean
    public ConversionService conversionService() {
        ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
        Set<Converter> converters = new HashSet<>();
        converters.add(new String2SqlInjectorConverter());
        bean.setConverters(converters);
        bean.afterPropertiesSet();
        return bean.getObject();
    }

    /**
     *  將String 轉(zhuǎn)成 ISqlInjector 實(shí)例
     */
    public class String2SqlInjectorConverter implements Converter<String, ISqlInjector> {
        @Override
        public ISqlInjector convert(String s) {
            if(StringUtils.isEmpty(s))
                return null;

            try {
                Class clazz = ClassUtils.getClass(s);
                ISqlInjector sqlInjector = (ISqlInjector)clazz.newInstance();
                return sqlInjector;
            } catch (ClassNotFoundException|IllegalAccessException|InstantiationException e) {
                ReflectionUtils.rethrowRuntimeException(e);
            }

            return null;
        }
    }
}

向spring注冊(cè)自定義的 converter迟郎,否則剥险,application.yml中mybatis-plus.sql-injector配置會(huì)出錯(cuò),提示沒(méi)有相應(yīng)的轉(zhuǎn)換器
這個(gè)文件需要在resources/META-INF/spring.factories中注冊(cè):

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.baomidou.springboot.config.mybatis.MybatisPlusAutoConfiguration

這個(gè)文件里的程序會(huì)先于spring組建bean之前執(zhí)行

SuperEntity

@Data
public abstract class SuperEntity<T extends Model> extends Model<T> {

    /**
     * 主鍵ID , 這里故意演示注解可以無(wú)
     */
    @TableId
    private String  uuid;


    @Override
    protected Serializable pkVal() {
        return this.uuid;
    }
}

數(shù)據(jù)庫(kù)對(duì)象的父類宪肖, 在這個(gè)類中可以定義一些公共的字段表制。 mybatis-plus只把id作為主鍵字段,非id的字段匈庭,需要用@TableId來(lái)標(biāo)注夫凸。pkVal() 是用來(lái)支持組合主鍵的,現(xiàn)在版本的mybatis-plus不支持組合主鍵

WxTokenObj

@Data
@TableName("tb_wx_token")
public class WxTokenObj extends SuperEntity<WxTokenObj>{
    private String token;

    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date createTime;
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date modifyTime;

    private String accountUuid;
    private String name;
    private String avatarPath;

    @TableLogic
    private String isDelete;
    private String unionId;
}

所有的數(shù)據(jù)對(duì)象都需要繼承SuperEntity阱持, 如果表名與數(shù)據(jù)對(duì)象名稱不對(duì)應(yīng)夭拌,需要用@TableName標(biāo)注數(shù)據(jù)表名。mybatis-plus可以配置自動(dòng)解析駝峰命名的字段為下劃線分割的數(shù)據(jù)庫(kù)字段名衷咽。如果數(shù)據(jù)庫(kù)字段名與數(shù)據(jù)字段名不一致鸽扁,需要用@TableField來(lái)標(biāo)注
@TableLogic標(biāo)注的字段,是用來(lái)做邏輯刪除的字段镶骗,在調(diào)用刪除方法的時(shí)候桶现,會(huì)將這個(gè)字段改成刪除狀態(tài),而不是真正刪除數(shù)據(jù)

SuperMapper

/**
 * 演示 mapper 父類鼎姊,注意這個(gè)類不要讓 mp 掃描到B夂汀!
 */
public interface SuperMapper<T> extends BaseMapper<T> {

    // 這里可以放一些公共的方法
}

所有mapper的父接口相寇,在這個(gè)接口中可以放一些公共方法慰于。這個(gè)接口需要放到mapper目錄外,不能讓mybatis scanner 掃描到唤衫。

WxTokenMapper

public interface WxTokenMapper extends SuperMapper<WxTokenObj> {

    @Select("select * from tb_wx_token where uuid=#{uuid}")
    WxTokenObj selectOneBySQL(String uuid);

    WxTokenObj selectOneByWrapper(@Param("ew") Wrapper wrapper);

    List<WxTokenObj> selectAllTokens();
}

一個(gè)具體的mapper接口婆赠。 接口可以使用@Select等標(biāo)注原生的sql,也可以使用mapper.xml 放置靈活的數(shù)據(jù)庫(kù)操作佳励。
自定義的mapper需要繼承SuperMapper休里,也就是需要從BaseMapper中繼承其定義的方法。BaseMapper中定義了很多類似JPA的自定義函數(shù)赃承,方便開(kāi)發(fā)使用:

public interface BaseMapper<T> {

    /**
     * <p>
     * 插入一條記錄
     * </p>
     *
     * @param entity 實(shí)體對(duì)象
     */
    Integer insert(T entity);

    /**
     * <p>
     * 根據(jù) ID 刪除
     * </p>
     *
     * @param id 主鍵ID
     */
    Integer deleteById(Serializable id);

    /**
     * <p>
     * 根據(jù) columnMap 條件妙黍,刪除記錄
     * </p>
     *
     * @param columnMap 表字段 map 對(duì)象
     */
    Integer deleteByMap(@Param("cm") Map<String, Object> columnMap);

    /**
     * <p>
     * 根據(jù) entity 條件,刪除記錄
     * </p>
     *
     * @param queryWrapper 實(shí)體對(duì)象封裝操作類(可以為 null)
     */
    Integer delete(@Param("ew") Wrapper<T> queryWrapper);

    /**
     * <p>
     * 刪除(根據(jù)ID 批量刪除)
     * </p>
     *
     * @param idList 主鍵ID列表(不能為 null 以及 empty)
     */
    Integer deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    /**
     * <p>
     * 根據(jù) ID 修改
     * </p>
     *
     * @param entity 實(shí)體對(duì)象
     */
    Integer updateById(@Param(Constants.META_OBJ_PREFIX) T entity);

    /**
     * <p>
     * 根據(jù) whereEntity 條件瞧剖,更新記錄
     * </p>
     *
     * @param entity        實(shí)體對(duì)象 (set 條件值,不能為 null)
     * @param updateWrapper 實(shí)體對(duì)象封裝操作類(可以為 null,里面的 entity 用于生成 where 語(yǔ)句)
     */
    Integer update(@Param(Constants.META_OBJ_PREFIX) T entity, @Param("ew") Wrapper<T> updateWrapper);

    /**
     * <p>
     * 根據(jù) ID 查詢
     * </p>
     *
     * @param id 主鍵ID
     */
    T selectById(Serializable id);

    /**
     * <p>
     * 查詢(根據(jù)ID 批量查詢)
     * </p>
     *
     * @param idList 主鍵ID列表(不能為 null 以及 empty)
     */
    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    /**
     * <p>
     * 查詢(根據(jù) columnMap 條件)
     * </p>
     *
     * @param columnMap 表字段 map 對(duì)象
     */
    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    /**
     * <p>
     * 根據(jù) entity 條件废境,查詢一條記錄
     * </p>
     *
     * @param queryWrapper 實(shí)體對(duì)象
     */
    T selectOne(@Param("ew") Wrapper<T> queryWrapper);

    /**
     * <p>
     * 根據(jù) Wrapper 條件,查詢總記錄數(shù)
     * </p>
     *
     * @param queryWrapper 實(shí)體對(duì)象
     */
    Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);

    /**
     * <p>
     * 根據(jù) entity 條件,查詢?nèi)坑涗?     * </p>
     *
     * @param queryWrapper 實(shí)體對(duì)象封裝操作類(可以為 null)
     */
    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

    /**
     * <p>
     * 根據(jù) Wrapper 條件噩凹,查詢?nèi)坑涗?     * </p>
     *
     * @param queryWrapper 實(shí)體對(duì)象封裝操作類(可以為 null)
     */
    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

    /**
     * <p>
     * 根據(jù) Wrapper 條件巴元,查詢?nèi)坑涗?     * 注意: 只返回第一個(gè)字段的值
     * </p>
     *
     * @param queryWrapper 實(shí)體對(duì)象封裝操作類(可以為 null)
     */
    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

    /**
     * <p>
     * 根據(jù) entity 條件,查詢?nèi)坑涗洠ú⒎?yè))
     * </p>
     *
     * @param page         分頁(yè)查詢條件(可以為 RowBounds.DEFAULT)
     * @param queryWrapper 實(shí)體對(duì)象封裝操作類(可以為 null)
     */
    IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);

    /**
     * <p>
     * 根據(jù) Wrapper 條件驮宴,查詢?nèi)坑涗洠ú⒎?yè))
     * </p>
     *
     * @param page         分頁(yè)查詢條件
     * @param queryWrapper 實(shí)體對(duì)象封裝操作類
     */
    IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
}

在定義mapper是逮刨,推薦使用idea ide + mybatisx 插件,支持從mapper 源代碼 到 mapper.xml 的雙向跳轉(zhuǎn)堵泽、代碼自動(dòng)生成等修己。

生成mapper xml

自動(dòng)生成的mapper xml

IWxTokenRepository

倉(cāng)庫(kù)接口。因?yàn)閭}(cāng)庫(kù)是用JDK proxy做增強(qiáng)迎罗,所有的倉(cāng)庫(kù)都需要單獨(dú)定義接口(如果換成用cglib做增強(qiáng)睬愤,可以不用單獨(dú)定義接口)

public interface IWxTokenRepository extends IService<WxTokenObj>{
    WxTokenObj getToken(String uuid);
    WxTokenObj getToken(Wrapper wrapper);
}

倉(cāng)庫(kù)接口需要繼承IService接口。IService接口中定義了很多方便使用的函數(shù)(主要是與mapper對(duì)應(yīng)的函數(shù)):

public interface IService<T> {

    /**
     * <p>
     * 插入一條記錄(選擇字段纹安,策略插入)
     * </p>
     *
     * @param entity 實(shí)體對(duì)象
     * @return boolean
     */
    boolean save(T entity);

    /**
     * <p>
     * 插入(批量)尤辱,該方法不適合 Oracle
     * </p>
     *
     * @param entityList 實(shí)體對(duì)象集合
     * @return boolean
     */
    boolean saveBatch(Collection<T> entityList);

    /**
     * <p>
     * 插入(批量)
     * </p>
     *
     * @param entityList 實(shí)體對(duì)象集合
     * @param batchSize  插入批次數(shù)量
     * @return boolean
     */
    boolean saveBatch(Collection<T> entityList, int batchSize);

    /**
     * <p>
     * 批量修改插入
     * </p>
     *
     * @param entityList 實(shí)體對(duì)象集合
     * @return boolean
     */
    boolean saveOrUpdateBatch(Collection<T> entityList);

    /**
     * <p>
     * 批量修改插入
     * </p>
     *
     * @param entityList 實(shí)體對(duì)象集合
     * @param batchSize
     * @return boolean
     */
    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

    /**
     * <p>
     * 根據(jù) ID 刪除
     * </p>
     *
     * @param id 主鍵ID
     * @return boolean
     */
    boolean removeById(Serializable id);

    /**
     * <p>
     * 根據(jù) columnMap 條件,刪除記錄
     * </p>
     *
     * @param columnMap 表字段 map 對(duì)象
     * @return boolean
     */
    boolean removeByMap(Map<String, Object> columnMap);

    /**
     * <p>
     * 根據(jù) entity 條件厢岂,刪除記錄
     * </p>
     *
     * @param wrapper 實(shí)體包裝類 {@link Wrapper}
     * @return boolean
     */
    boolean remove(Wrapper<T> wrapper);

    /**
     * <p>
     * 刪除(根據(jù)ID 批量刪除)
     * </p>
     *
     * @param idList 主鍵ID列表
     * @return boolean
     */
    boolean removeByIds(Collection<? extends Serializable> idList);

    /**
     * <p>
     * 根據(jù) ID 選擇修改
     * </p>
     *
     * @param entity 實(shí)體對(duì)象
     * @return boolean
     */
    boolean updateById(T entity);

    /**
     * <p>
     * 根據(jù) whereEntity 條件光督,更新記錄
     * </p>
     *
     * @param entity  實(shí)體對(duì)象
     * @param wrapper 實(shí)體包裝類 {@link Wrapper}
     * @return boolean
     */
    boolean update(T entity, Wrapper<T> wrapper);

    /**
     * <p>
     * 根據(jù)ID 批量更新
     * </p>
     *
     * @param entityList 實(shí)體對(duì)象集合
     * @return boolean
     */
    boolean updateBatchById(Collection<T> entityList);

    /**
     * <p>
     * 根據(jù)ID 批量更新
     * </p>
     *
     * @param entityList 實(shí)體對(duì)象集合
     * @param batchSize  更新批次數(shù)量
     * @return boolean
     */
    boolean updateBatchById(Collection<T> entityList, int batchSize);

    /**
     * <p>
     * TableId 注解存在更新記錄,否插入一條記錄
     * </p>
     *
     * @param entity 實(shí)體對(duì)象
     * @return boolean
     */
    boolean saveOrUpdate(T entity);

    /**
     * <p>
     * 根據(jù) ID 查詢
     * </p>
     *
     * @param id 主鍵ID
     * @return T
     */
    T getById(Serializable id);

    /**
     * <p>
     * 查詢(根據(jù)ID 批量查詢)
     * </p>
     *
     * @param idList 主鍵ID列表
     * @return Collection<T>
     */
    Collection<T> listByIds(Collection<? extends Serializable> idList);

    /**
     * <p>
     * 查詢(根據(jù) columnMap 條件)
     * </p>
     *
     * @param columnMap 表字段 map 對(duì)象
     * @return Collection<T>
     */
    Collection<T> listByMap(Map<String, Object> columnMap);

    /**
     * <p>
     * 根據(jù) Wrapper塔粒,查詢一條記錄
     * </p>
     *
     * @param wrapper 實(shí)體對(duì)象
     * @return T
     */
    T getOne(Wrapper<T> wrapper);

    /**
     * <p>
     * 根據(jù) Wrapper结借,查詢一條記錄
     * </p>
     *
     * @param wrapper {@link Wrapper}
     * @return
     */
    Map<String, Object> getMap(Wrapper<T> wrapper);

    /**
     * <p>
     * 根據(jù) Wrapper,查詢一條記錄
     * </p>
     *
     * @param wrapper {@link Wrapper}
     * @return Object
     */
    Object getObj(Wrapper<T> wrapper);

    /**
     * <p>
     * 根據(jù) Wrapper 條件卒茬,查詢總記錄數(shù)
     * </p>
     *
     * @param wrapper 實(shí)體對(duì)象
     * @return int
     */
    int count(Wrapper<T> wrapper);

    /**
     * <p>
     * 查詢列表
     * </p>
     *
     * @param wrapper 實(shí)體包裝類 {@link Wrapper}
     * @return
     */
    List<T> list(Wrapper<T> wrapper);

    /**
     * <p>
     * 翻頁(yè)查詢
     * </p>
     *
     * @param page    翻頁(yè)對(duì)象
     * @param wrapper 實(shí)體包裝類 {@link Wrapper}
     * @return
     */
    IPage<T> page(IPage<T> page, Wrapper<T> wrapper);

    /**
     * <p>
     * 查詢列表
     * </p>
     *
     * @param wrapper {@link Wrapper}
     * @return
     */
    List<Map<String, Object>> listMaps(Wrapper<T> wrapper);

    /**
     * <p>
     * 根據(jù) Wrapper 條件船老,查詢?nèi)坑涗?     * </p>
     *
     * @param wrapper 實(shí)體對(duì)象封裝操作類(可以為 null)
     * @return List<Object>
     */
    List<Object> listObjs(Wrapper<T> wrapper);

    /**
     * <p>
     * 翻頁(yè)查詢
     * </p>
     *
     * @param page    翻頁(yè)對(duì)象
     * @param wrapper {@link Wrapper}
     * @return
     */
    IPage<Map<String, Object>> pageMaps(IPage page, Wrapper<T> wrapper);

}

對(duì)應(yīng)的,倉(cāng)庫(kù)實(shí)現(xiàn)類也就要繼承ServiceImpl圃酵,來(lái)對(duì)IService中的函數(shù)提供實(shí)現(xiàn)柳畔。

WxTokenRepository

倉(cāng)庫(kù)實(shí)現(xiàn)類,對(duì)自定義的倉(cāng)庫(kù)接口中的函數(shù)提供實(shí)現(xiàn):

@Component
public class WxTokenRepository extends ServiceImpl<WxTokenMapper, WxTokenObj> implements IWxTokenRepository{
    public WxTokenObj getToken(String uuid) {
        return baseMapper.selectOneBySQL(uuid);
    }

    public WxTokenObj getToken(Wrapper wrapper) {
        return baseMapper.selectOneByWrapper(wrapper);
    }
}

在繼承了ServiceImpl后辜昵,可通過(guò)baseMapper方法 接口對(duì)應(yīng)的mapper荸镊。如果還需要訪問(wèn)其他的mapper咽斧,可以用個(gè)@Autowired 注入其他mapper堪置。

Controller

用戶接口。這里演示如何用倉(cāng)庫(kù)訪問(wèn)數(shù)據(jù)讀取數(shù)據(jù)张惹。

查詢1

@GetMapping(value = "/id/{id}")
    public String getById(@PathVariable String id){
        WxTokenObj wxTokenObj = wxTokenRepository.getToken(id);

        return gson.toJson(wxTokenObj);
    }

這個(gè)接口中舀锨,訪問(wèn)的是mapper中使用@Select標(biāo)注的數(shù)據(jù)庫(kù)訪問(wèn)方法⊥鸲海可以在mapper中使用這種方式簡(jiǎn)單的定義數(shù)據(jù)庫(kù)訪問(wèn)操作坎匿,這樣的mapper函數(shù)不需要生成mapper.xml中的配置就可以實(shí)現(xiàn)數(shù)據(jù)操作。

查詢2

@GetMapping("/token/{token}")
    public String getByToken(@PathVariable String token){
        Wrapper<WxTokenObj> wrapper = new QueryWrapper<WxTokenObj>().lambda().eq(WxTokenObj::getToken, token);

        WxTokenObj wxTokenObj = wxTokenRepository.getToken(wrapper);

        return gson.toJson(wxTokenObj);
    }

這個(gè)接口中,訪問(wèn)的是自定義的數(shù)據(jù)庫(kù)訪問(wèn)方法替蔬。這樣的mapper函數(shù)需要生成mapper.xml配置才能正確的操作數(shù)據(jù)告私。
為了提供查詢條件,mybatis-plus提供了QueryWrapper來(lái)生成where片段承桥。

查詢3

@GetMapping("/token2/{token}")
    public String getByToken2(@PathVariable String token){
        Wrapper<WxTokenObj> wrapper = new QueryWrapper<WxTokenObj>().lambda().eq(WxTokenObj::getToken, token);

        WxTokenObj wxTokenObj = wxTokenRepository.selectOne(wrapper);

        return gson.toJson(wxTokenObj);
    }

這個(gè)接口中驻粟,使用的是BaseMapper中提供的數(shù)據(jù)庫(kù)訪問(wèn)函數(shù)。這樣的函數(shù)可以直接訪問(wèn)凶异,不需要自己定義蜀撑,也不需要在mapper.xml中配置。這種體驗(yàn)與JPA類似剩彬。

刪除

    @DeleteMapping("/id/{id}")
    public String deleteById(@PathVariable String id) {
        boolean success = wxTokenRepository.deleteById(id);

        return gson.toJson(success);
    }

這個(gè)接口使用BaseMapper中提供的刪除函數(shù)來(lái)根據(jù)ID刪除數(shù)據(jù)酷麦。配置了邏輯刪除后,這個(gè)刪除操作只是修改了刪除標(biāo)記喉恋,并沒(méi)有真正的刪除數(shù)據(jù)沃饶。


image

mapper xml說(shuō)明

<?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="com.baomidou.springboot.db.mapper.WxTokenMapper">

    <!-- 通用查詢結(jié)果列 -->
    <!--<sql id="Base_Column_List">-->
        <!--token       ,-->
        <!--create_time ,-->
        <!--modify_time ,-->
        <!--account_id  ,-->
        <!--name        ,-->
        <!--avatar_path ,-->
        <!--is_delete   ,-->
        <!--uuid        ,-->
        <!--account_uuid,-->
        <!--union_id-->
    <!--</sql>-->

    <select id="selectOneByWrapper" resultType="com.baomidou.springboot.db.entity.WxTokenObj">
        SELECT * FROM tb_wx_token
        <!-- 判斷 wrapper 是否為空 emptyOfWhere -->
        <where>
            ${ew.sqlSegment}
        </where>
    </select>
    <select id="selectAllTokens" resultType="com.baomidou.springboot.db.entity.WxTokenObj"></select>
    <select id="selectTokensByDeleteStatus" resultType="com.baomidou.springboot.db.entity.WxTokenObj"></select>
</mapper>

這里面的select操作的where條件,使用的是傳入的Wrapper參數(shù)中的sqlSegment來(lái)實(shí)現(xiàn)的瀑晒。Wrapper可以通過(guò)流式函數(shù)風(fēng)格绍坝,以類jpa的方式定義where 條件。
mybatis-plus提供了幾種Wrapper實(shí)現(xiàn):

image

這其中有專用于查詢的QueryWrapper, 專用于更新操作的UpdateWrapper苔悦。
如果需要按條件刪除數(shù)據(jù)轩褐,可以用QueryWrapper傳入條件。
當(dāng)然玖详,也可用根據(jù) columnMap 條件進(jìn)行查詢把介、刪除操作。
有關(guān)Wrapper的更多使用案例蟋座,可以參考mybatis-plus core里的WrapperTest.java:

private void logSqlSegment(String explain, ISqlSegment sqlSegment) {
        System.out.println(String.format(" ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(%s)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓", explain));
        System.out.println(sqlSegment.getSqlSegment());
    }

    private <T> void logParams(QueryWrapper<T> wrapper) {
        wrapper.getParamNameValuePairs().forEach((k, v) ->
            System.out.println("key: '" + k + "'\t\tvalue: '" + v + "'"));
    }

    @Test
    public void test() {
        Wrapper<User> wrapper = new QueryWrapper<User>().lambda().eq(User::getName, 123)
            .or(c -> c.eq(User::getRoleId, 1).eq(User::getId, 2))
            .eq(User::getId, 1);
        log(wrapper.getSqlSegment());

    }

    @Test
    public void test1() {
        QueryWrapper<User> ew = new QueryWrapper<User>()
            .eq("xxx", 123)
            .and(i -> i.eq("andx", 65444).le("ande", 66666))
            .ne("xxx", 222);
        log(ew.getSqlSegment());
        ew.getParamNameValuePairs().forEach((k, v) -> System.out.println("key = " + k + " ; value = " + v));
    }
    
============================================
xxx = #{ew.paramNameValuePairs.MPGENVAL1} AND ( andx = #{ew.paramNameValuePairs.MPGENVAL2} AND ande <= #{ew.paramNameValuePairs.MPGENVAL3} ) AND xxx <> #{ew.paramNameValuePairs.MPGENVAL4}
key = MPGENVAL3 ; value = 66666
key = MPGENVAL2 ; value = 65444
key = MPGENVAL1 ; value = 123
key = MPGENVAL4 ; value = 222
============================================    


    @Test
    public void test2() {
        UpdateWrapper<User> ew = new UpdateWrapper<User>()
            .set("name", "三毛").set("id", 1)
            .eq("xxx", 123)
            .and(i -> i.eq("andx", 65444).le("ande", 66666))
            .ne("xxx", 222);
        log(ew.getSqlSet());
        log(ew.getSqlSegment());
    }
    
============================================
name=#{ew.paramNameValuePairs.MPGENVAL1},id=#{ew.paramNameValuePairs.MPGENVAL2}
xxx = #{ew.paramNameValuePairs.MPGENVAL3} AND ( andx = #{ew.paramNameValuePairs.MPGENVAL4} AND ande <= #{ew.paramNameValuePairs.MPGENVAL5} ) AND xxx <> #{ew.paramNameValuePairs.MPGENVAL6}
============================================

    @Test
    public void test3() {
        UpdateWrapper<User> ew = new UpdateWrapper<User>()
            .setSql("abc=1,def=2").eq("id", 1).ge("age", 3);
        log(ew.getSqlSet());
        log(ew.getSqlSegment());
    }
============================================
abc=1,def=2
id = #{ew.paramNameValuePairs.MPGENVAL1} AND age >= #{ew.paramNameValuePairs.MPGENVAL2}
============================================

    @Test
    public void testQueryWrapper() {
        logSqlSegment("去除第一個(gè) or,以及自動(dòng)拼接 and,以及手動(dòng)拼接 or,以及去除最后的多個(gè)or", new QueryWrapper<User>().or()
            .ge("age", 3).or().ge("age", 3).ge("age", 3).or().or().or().or());

        logSqlSegment("多個(gè) or 相連接,去除多余的 or", new QueryWrapper<User>()
            .ge("age", 3).or().or().or().ge("age", 3).or().or().ge("age", 3));

        logSqlSegment("嵌套,正常嵌套", new QueryWrapper<User>()
            .nested(i -> i.eq("id", 1)).eq("id", 1));

        logSqlSegment("嵌套,第一個(gè)套外的 and 自動(dòng)消除", new QueryWrapper<User>()
            .and(i -> i.eq("id", 1)).eq("id", 1));

        logSqlSegment("嵌套,多層嵌套", new QueryWrapper<User>()
            .and(i -> i.eq("id", 1).and(j -> j.eq("id", 1))));

        logSqlSegment("嵌套,第一個(gè)套外的 or 自動(dòng)消除", new QueryWrapper<User>()
            .or(i -> i.eq("id", 1)).eq("id", 1));

        logSqlSegment("嵌套,套內(nèi)外自動(dòng)拼接 and", new QueryWrapper<User>()
            .eq("id", 11).and(i -> i.eq("id", 1)).eq("id", 1));

        logSqlSegment("嵌套,套內(nèi)外手動(dòng)拼接 or,去除套內(nèi)第一個(gè) or", new QueryWrapper<User>()
            .eq("id", 11).or(i -> i.or().eq("id", 1)).or().eq("id", 1));

        logSqlSegment("多個(gè) order by 和 group by 拼接,自動(dòng)優(yōu)化順序,last方法拼接在最后", new QueryWrapper<User>()
            .eq("id", 11)
            .last("limit 1")
            .orderByAsc("id", "name", "sex").orderByDesc("age", "txl")
            .groupBy("id", "name", "sex").groupBy("id", "name"));

        logSqlSegment("只存在 order by", new QueryWrapper<User>()
            .orderByAsc("id", "name", "sex").orderByDesc("age", "txl"));

        logSqlSegment("只存在 group by", new QueryWrapper<User>()
            .groupBy("id", "name", "sex").groupBy("id", "name"));
    }

============================================
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(去除第一個(gè) or,以及自動(dòng)拼接 and,以及手動(dòng)拼接 or,以及去除最后的多個(gè)or)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
age >= #{ew.paramNameValuePairs.MPGENVAL1} OR age >= #{ew.paramNameValuePairs.MPGENVAL2} AND age >= #{ew.paramNameValuePairs.MPGENVAL3}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(多個(gè) or 相連接,去除多余的 or)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
age >= #{ew.paramNameValuePairs.MPGENVAL1} OR age >= #{ew.paramNameValuePairs.MPGENVAL2} OR age >= #{ew.paramNameValuePairs.MPGENVAL3}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(嵌套,正常嵌套)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} ) AND id = #{ew.paramNameValuePairs.MPGENVAL2}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(嵌套,第一個(gè)套外的 and 自動(dòng)消除)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} ) AND id = #{ew.paramNameValuePairs.MPGENVAL2}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(嵌套,多層嵌套)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} AND ( id = #{ew.paramNameValuePairs.MPGENVAL2} ) )
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(嵌套,第一個(gè)套外的 or 自動(dòng)消除)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} ) AND id = #{ew.paramNameValuePairs.MPGENVAL2}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(嵌套,套內(nèi)外自動(dòng)拼接 and)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
id = #{ew.paramNameValuePairs.MPGENVAL1} AND ( id = #{ew.paramNameValuePairs.MPGENVAL2} ) AND id = #{ew.paramNameValuePairs.MPGENVAL3}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(嵌套,套內(nèi)外手動(dòng)拼接 or,去除套內(nèi)第一個(gè) or)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
id = #{ew.paramNameValuePairs.MPGENVAL1} OR ( id = #{ew.paramNameValuePairs.MPGENVAL2} ) OR id = #{ew.paramNameValuePairs.MPGENVAL3}
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(多個(gè) order by 和 group by 拼接,自動(dòng)優(yōu)化順序,last方法拼接在最后)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
id = #{ew.paramNameValuePairs.MPGENVAL1} GROUP BY id,name,sex,id,name ORDER BY id,name,sex ASC , age,txl DESC limit 1
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(只存在 order by)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
1=1 ORDER BY id,name,sex ASC , age,txl DESC
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(只存在 group by)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
1=1 GROUP BY id,name,sex,id,name
============================================

    @Test
    public void testCompare() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
            .allEq(getMap()).allEq((k, v) -> true, getMap())
            .eq("id", 1).ne("id", 1)
            .or().gt("id", 1).ge("id", 1)
            .lt("id", 1).le("id", 1)
            .or().between("id", 1, 2).notBetween("id", 1, 3)
            .like("id", 1).notLike("id", 1)
            .or().likeLeft("id", 1).likeRight("id", 1);
        logSqlSegment("測(cè)試 Compare 下的方法", queryWrapper);
        logParams(queryWrapper);
    }
============================================
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(測(cè)試 Compare 下的方法)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
column1 = #{ew.paramNameValuePairs.MPGENVAL1} AND column0 = #{ew.paramNameValuePairs.MPGENVAL2} AND nullColumn IS NULL AND column1 = #{ew.paramNameValuePairs.MPGENVAL3} AND column0 = #{ew.paramNameValuePairs.MPGENVAL4} AND nullColumn IS NULL AND id = #{ew.paramNameValuePairs.MPGENVAL5} AND id <> #{ew.paramNameValuePairs.MPGENVAL6} OR id > #{ew.paramNameValuePairs.MPGENVAL7} AND id >= #{ew.paramNameValuePairs.MPGENVAL8} AND id < #{ew.paramNameValuePairs.MPGENVAL9} AND id <= #{ew.paramNameValuePairs.MPGENVAL10} OR id BETWEEN #{ew.paramNameValuePairs.MPGENVAL11} AND #{ew.paramNameValuePairs.MPGENVAL12} AND id NOT BETWEEN #{ew.paramNameValuePairs.MPGENVAL13} AND #{ew.paramNameValuePairs.MPGENVAL14} AND id LIKE #{ew.paramNameValuePairs.MPGENVAL15} AND id NOT LIKE #{ew.paramNameValuePairs.MPGENVAL16} OR id LIKE #{ew.paramNameValuePairs.MPGENVAL17} AND id LIKE #{ew.paramNameValuePairs.MPGENVAL18}
key: 'MPGENVAL3'        value: '1'
key: 'MPGENVAL2'        value: '0'
key: 'MPGENVAL1'        value: '1'
key: 'MPGENVAL18'       value: '1%'
key: 'MPGENVAL17'       value: '%1'
key: 'MPGENVAL16'       value: '%1%'
key: 'MPGENVAL15'       value: '%1%'
key: 'MPGENVAL14'       value: '3'
key: 'MPGENVAL13'       value: '1'
key: 'MPGENVAL9'        value: '1'
key: 'MPGENVAL12'       value: '2'
key: 'MPGENVAL8'        value: '1'
key: 'MPGENVAL11'       value: '1'
key: 'MPGENVAL7'        value: '1'
key: 'MPGENVAL10'       value: '1'
key: 'MPGENVAL6'        value: '1'
key: 'MPGENVAL5'        value: '1'
key: 'MPGENVAL4'        value: '0'
============================================

    @Test
    public void testFunc() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>()//todo in 方法是不是各個(gè)加個(gè)后綴好點(diǎn)
            .isNull("nullColumn").or().isNotNull("notNullColumn")
            .orderByAsc("id").orderByDesc("name")
            .groupBy("id", "name").groupBy("id2", "name2")
            .in("inColl", getList()).or().notIn("notInColl", getList())
            .in("inArray").notIn("notInArray", 1, 2, 3)
            .inSql("inSql", "1,2,3,4,5").notInSql("inSql", "1,2,3,4,5")
            .having("sum(age) > {0}", 1).having("id is not null");
        logSqlSegment("測(cè)試 Func 下的方法", queryWrapper);
        logParams(queryWrapper);
    }
============================================
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(測(cè)試 Func 下的方法)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
nullColumn IS NULL OR notNullColumn IS NOT NULL AND inColl IN (#{ew.paramNameValuePairs.MPGENVAL1},#{ew.paramNameValuePairs.MPGENVAL2}) OR notInColl NOT IN (#{ew.paramNameValuePairs.MPGENVAL3},#{ew.paramNameValuePairs.MPGENVAL4}) AND notInArray NOT IN (#{ew.paramNameValuePairs.MPGENVAL5},#{ew.paramNameValuePairs.MPGENVAL6},#{ew.paramNameValuePairs.MPGENVAL7}) AND inSql IN (1,2,3,4,5) AND inSql NOT IN (1,2,3,4,5) GROUP BY id,name,id2,name2 HAVING sum(age) > #{ew.paramNameValuePairs.MPGENVAL8} AND id is not null ORDER BY id ASC , name DESC
key: 'MPGENVAL3'        value: '0'
key: 'MPGENVAL2'        value: '1'
key: 'MPGENVAL1'        value: '0'
key: 'MPGENVAL8'        value: '1'
key: 'MPGENVAL7'        value: '3'
key: 'MPGENVAL6'        value: '2'
key: 'MPGENVAL5'        value: '1'
key: 'MPGENVAL4'        value: '1'
============================================


    @Test
    public void testJoin() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
            .last("limit 1").or()
            .apply("date_format(column,'%Y-%m-%d') = '2008-08-08'")
            .apply("date_format(column,'%Y-%m-%d') = {0}", LocalDate.now())
            .or().exists("select id from table where age = 1")
            .or().notExists("select id from table where age = 1");
        logSqlSegment("測(cè)試 Join 下的方法", queryWrapper);
        logParams(queryWrapper);
    }
============================================
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(測(cè)試 Join 下的方法)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
date_format(column,'%Y-%m-%d') = '2008-08-08' AND date_format(column,'%Y-%m-%d') = #{ew.paramNameValuePairs.MPGENVAL1} OR EXISTS (select id from table where age = 1) OR NOT EXISTS (select id from table where age = 1) limit 1
key: 'MPGENVAL1'        value: '2018-08-01'
============================================

    @Test
    public void testNested() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
            .and(i -> i.eq("id", 1).nested(j -> j.ne("id", 2)))
            .or(i -> i.eq("id", 1).and(j -> j.ne("id", 2)))
            .nested(i -> i.eq("id", 1).or(j -> j.ne("id", 2)));
        logSqlSegment("測(cè)試 Nested 下的方法", queryWrapper);
        logParams(queryWrapper);
    }
 
 ============================================
 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(測(cè)試 Nested 下的方法)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} AND ( id <> #{ew.paramNameValuePairs.MPGENVAL2} ) ) OR ( id = #{ew.paramNameValuePairs.MPGENVAL3} AND ( id <> #{ew.paramNameValuePairs.MPGENVAL4} ) ) AND ( id = #{ew.paramNameValuePairs.MPGENVAL5} OR ( id <> #{ew.paramNameValuePairs.MPGENVAL6} ) )
key: 'MPGENVAL3'        value: '1'
key: 'MPGENVAL2'        value: '2'
key: 'MPGENVAL1'        value: '1'
key: 'MPGENVAL6'        value: '2'
key: 'MPGENVAL5'        value: '1'
key: 'MPGENVAL4'        value: '2'
============================================

    @Test
    public void testPluralLambda() {
        TableInfoHelper.initTableInfo(null, User.class);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(User::getName,"sss");
        queryWrapper.lambda().eq(User::getName,"sss2");
        logSqlSegment("測(cè)試 PluralLambda", queryWrapper);
        logParams(queryWrapper);
    }

    private List<Object> getList() {
        List<Object> list = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            list.add(i);
        }
        return list;
    }

    private Map<String, Object> getMap() {
        Map<String, Object> map = new HashMap<>();
        for (int i = 0; i < 2; i++) {
            map.put("column" + i, i);
        }
        map.put("nullColumn", null);
        return map;
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拗踢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子向臀,更是在濱河造成了極大的恐慌巢墅,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件券膀,死亡現(xiàn)場(chǎng)離奇詭異君纫,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)芹彬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)蓄髓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人舒帮,你說(shuō)我怎么就攤上這事会喝∥螅” “怎么了浸赫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵枚尼,是天一觀的道長(zhǎng)惰匙。 經(jīng)常有香客問(wèn)我,道長(zhǎng)预茄,這世上最難降的妖魔是什么岭妖? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮反璃,結(jié)果婚禮上昵慌,老公的妹妹穿的比我還像新娘。我一直安慰自己淮蜈,他們只是感情好斋攀,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著梧田,像睡著了一般淳蔼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上裁眯,一...
    開(kāi)封第一講書(shū)人閱讀 52,713評(píng)論 1 312
  • 那天鹉梨,我揣著相機(jī)與錄音,去河邊找鬼穿稳。 笑死存皂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的逢艘。 我是一名探鬼主播旦袋,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼它改!你這毒婦竟也來(lái)了疤孕?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤央拖,失蹤者是張志新(化名)和其女友劉穎祭阀,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體鲜戒,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡专控,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了袍啡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踩官。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡却桶,死狀恐怖境输,靈堂內(nèi)的尸體忽然破棺而出蔗牡,到底是詐尸還是另有隱情,我是刑警寧澤嗅剖,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布辩越,位于F島的核電站,受9級(jí)特大地震影響信粮,放射性物質(zhì)發(fā)生泄漏黔攒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一强缘、第九天 我趴在偏房一處隱蔽的房頂上張望督惰。 院中可真熱鬧,春花似錦旅掂、人聲如沸赏胚。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)觉阅。三九已至,卻和暖如春秘车,著一層夾襖步出監(jiān)牢的瞬間典勇,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工叮趴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留割笙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓眯亦,卻偏偏與公主長(zhǎng)得像咳蔚,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子搔驼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

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

  • 不知何日夏日不見(jiàn)谈火,留得滿地白霜。 觀鏡中人舌涨,已非昨日顏糯耍。 似古稀,遂拾綴流年清淺囊嘉。 回眸間温技,一簾雨霧漣漣。 似夢(mèng)似...
    岑子辛閱讀 216評(píng)論 0 0
  • 01 我的父親是一位農(nóng)民。 他身高1.70米琢蛤,身材削瘦蜓堕,國(guó)字型臉抛虏,眼睛深邃但炯炯有神,眼睛里透露出一種很犀利的光套才。...
    親吻蒼穹閱讀 281評(píng)論 0 0
  • 自律迂猴,活成一個(gè)真正的人 老師從《黑客帝國(guó)》和《自私的基因》來(lái)澄清了自律的重要性”嘲椋《黑客帝國(guó)》里尼奧原本可以繼續(xù)活在...
    故里草木生閱讀 182評(píng)論 0 0
  • 這周傻寂,我們舉行了一個(gè)非常特別的儀式息尺,以此鼓勵(lì)在夢(mèng)想道路上一直默默堅(jiān)持的人。 用“投鈔票”代替“投選票”疾掰,過(guò)程低調(diào)掷倔,...
    了解閣閱讀 365評(píng)論 2 2