Mybatis-Plus極速入門

MyBatis-Plus(簡(jiǎn)稱 MP)是一個(gè) MyBatis 的增強(qiáng)工具捉撮,在 MyBatis 的基礎(chǔ)上只做增強(qiáng)不做改變奋救,為簡(jiǎn)化開(kāi)發(fā)、提高效率而生。

特性

  • 無(wú)侵入:只做增強(qiáng)不做改變授嘀,引入它不會(huì)對(duì)現(xiàn)有工程產(chǎn)生影響,如絲般順滑
  • 損耗小:?jiǎn)?dòng)即會(huì)自動(dòng)注入基本 CURD锣险,性能基本無(wú)損耗蹄皱,直接面向?qū)ο蟛僮?/li>
  • 強(qiáng)大的 CRUD 操作:內(nèi)置通用 Mapper、通用 Service芯肤,僅僅通過(guò)少量配置即可實(shí)現(xiàn)單表大部分 CRUD 操作巷折,更有強(qiáng)大的條件構(gòu)造器,滿足各類使用需求
  • 支持 Lambda 形式調(diào)用:通過(guò) Lambda 表達(dá)式崖咨,方便的編寫各類查詢條件锻拘,無(wú)需再擔(dān)心字段寫錯(cuò)
  • 支持主鍵自動(dòng)生成:支持多達(dá) 4 種主鍵策略(內(nèi)含分布式唯一 ID 生成器 - Sequence),可自由配置击蹲,完美解決主鍵問(wèn)題
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式調(diào)用署拟,實(shí)體類只需繼承 Model 類即可進(jìn)行強(qiáng)大的 CRUD 操作
  • 支持自定義全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 內(nèi)置代碼生成器:采用代碼或者 Maven 插件可快速生成 Mapper 、 Model 歌豺、 Service 推穷、 Controller 層代碼,支持模板引擎类咧,更有超多自定義配置等您來(lái)使用
  • 內(nèi)置分頁(yè)插件:基于 MyBatis 物理分頁(yè)馒铃,開(kāi)發(fā)者無(wú)需關(guān)心具體操作蟹腾,配置好插件之后,寫分頁(yè)等同于普通 List 查詢
  • 分頁(yè)插件支持多種數(shù)據(jù)庫(kù):支持 MySQL区宇、MariaDB娃殖、Oracle、DB2议谷、H2珊随、HSQL、SQLite柿隙、Postgre叶洞、SQLServer 等多種數(shù)據(jù)庫(kù)
  • 內(nèi)置性能分析插件:可輸出 Sql 語(yǔ)句以及其執(zhí)行時(shí)間,建議開(kāi)發(fā)測(cè)試時(shí)啟用該功能禀崖,能快速揪出慢查詢
  • 內(nèi)置全局?jǐn)r截插件:提供全表 delete 衩辟、 update 操作智能分析阻斷,也可自定義攔截規(guī)則波附,預(yù)防誤操作

支持?jǐn)?shù)據(jù)庫(kù)

  • mysql 艺晴、mariadb 、oracle 掸屡、db2 封寞、h2 、hsql 仅财、sqlite 狈究、postgresql 、sqlserver 盏求、presto 抖锥、Gauss 、Firebird

  • Phoenix 碎罚、clickhouse 磅废、Sybase ASE 、 OceanBase 荆烈、達(dá)夢(mèng)數(shù)據(jù)庫(kù) 拯勉、虛谷數(shù)據(jù)庫(kù) 、人大金倉(cāng)數(shù)據(jù)庫(kù) 憔购、南大通用數(shù)據(jù)庫(kù) 宫峦、

框架結(jié)構(gòu)

framework

快速開(kāi)始

以springboot項(xiàng)目為例

引入mybatisPlus的依賴:
<!-- mybatis-plus -->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.4.0</version>
</dependency>

完整依賴如下:

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.tp</groupId>
    <artifactId>demo-mp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo-mp</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mysql驅(qū)動(dòng) -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional> <!-- 標(biāo)識(shí)依賴不會(huì)傳遞 -->
        </dependency>
        <!-- druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.1</version>
        </dependency>
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!-- test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
配置數(shù)據(jù)庫(kù)連接信息
server:
  port: 7269
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      url: jdbc:mysql://localhost:3306/mybatis_plus_demo?useSSL=true&serverTimezone=GMT%2B8
      username: root
      password: tp123456
logging:
  level:
    root: info
    com.tp: debug
創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for USER
-- ----------------------------
DROP TABLE IF EXISTS `USER`;
CREATE TABLE `USER` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `name` varchar(30) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年齡',
  `email` varchar(50) DEFAULT NULL COMMENT '郵箱',
  `manager_id` bigint(20) DEFAULT NULL COMMENT '直屬上級(jí)ID',
  `create_time` datetime DEFAULT NULL COMMENT '郵箱',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

SET FOREIGN_KEY_CHECKS = 1;
項(xiàng)目中創(chuàng)建實(shí)體User.java
@Data
@TableName("user")
public class User {

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /** 姓名 */
    private String name;

    /** 年齡 */
    private Integer age;

    /** 郵箱 */
    private String email;

    /** 直屬上級(jí)ID */
    private Integer managerId;

    /** 創(chuàng)建時(shí)間 */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") //此注解用來(lái)接收字符串類型的參數(shù)封裝成LocalDateTime類型
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") //此注解將date類型數(shù)據(jù)轉(zhuǎn)成字符串響應(yīng)出去
    private LocalDateTime createTime;

    /** 對(duì)于數(shù)據(jù)庫(kù)中沒(méi)有的字段,如果我們不處理倦始,在新增或修改的時(shí)候會(huì)出現(xiàn)錯(cuò)誤斗遏,mp為我們提供了注解@TableField解決此類問(wèn)題 */
    @TableField(exist = false) // exist = false標(biāo)識(shí)數(shù)據(jù)庫(kù)沒(méi)有此字段,告訴mp映射時(shí)忽略此字段鞋邑,防止出現(xiàn)錯(cuò)誤诵次,默認(rèn)為true
    List<Integer> roleIds;
}

注意:對(duì)于數(shù)據(jù)庫(kù)中沒(méi)有的字段账蓉,如果我們不做特殊處理,我們?cè)诓僮鲾?shù)據(jù)庫(kù)時(shí)MP會(huì)給我們自動(dòng)進(jìn)行映射逾一,但是因?yàn)閿?shù)據(jù)庫(kù)中并沒(méi)有這個(gè)字段铸本,這樣就會(huì)報(bào)錯(cuò):

org.springframework.jdbc.BadSqlGrammarException: 
### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: Unknown column 'role_ids' in 'field list'

針對(duì)這種情形,我們可以使用MP提供的注解@TableField(exist = false)來(lái)表明某個(gè)字段不是數(shù)據(jù)庫(kù)的字段遵堵,其中exist表示是否存在箱玷,默認(rèn)為true

創(chuàng)建Mapper,繼承自MP給我提供的BaseMapper
package com.tp.demo.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tp.demo.mp.entity.User;

/**
 * FileName: UserMapper
 * Author:   TP
 * Description: 用戶服務(wù)Mapper
 */
public interface UserMapper extends BaseMapper<User> {

}

接下來(lái)就可以測(cè)試?yán)材八蓿瑢?shí)際開(kāi)發(fā)中我們應(yīng)該寫Service锡足,但是這里為了方便,就不寫了

常用增刪改查

新增:
package com.tp.demo.mp;

import com.tp.demo.mp.entity.User;
import com.tp.demo.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDateTime;

/**
 * FileName: MybatisPlusInsertTests
 * Author:   TP
 * Description:
 */
@SpringBootTest
public class MybatisPlusInsertTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * 插入1條數(shù)據(jù)壳坪,mp為我們提供了insert(T entity)方法
     * 該方法插入一條記錄舶得,返回影響行數(shù)
     * <p>
     * 注意??:MP的默認(rèn)策略為:如果實(shí)體的某個(gè)屬性為null,那么新增和修改時(shí)爽蝴,會(huì)忽略null值字段
     */
    @Test
    void testInsert() {
        User user = new User();
        user.setName("向東");
        user.setAge(58);
        user.setManagerId(2);
        user.setEmail("xb@163.com");
        user.setCreateTime(LocalDateTime.now());
        int rows = userMapper.insert(user);
        System.out.println("影響記錄數(shù):" + rows);
        System.out.println("新記錄的主鍵ID:" + user.getId());
    }
}

MP為我們提供了insert(T entity)方法沐批,該方法插入一條記錄,返回影響行數(shù)蝎亚,MP的默認(rèn)策略為:如果實(shí)體的某個(gè)屬性為null九孩,那么新增和修改時(shí),會(huì)忽略null值字段发框,如果你想獲取新增的實(shí)體的ID躺彬,可以直接調(diào)用實(shí)體的getId方法獲取。

查詢
package com.tp.demo.mp;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.tp.demo.mp.entity.User;
import com.tp.demo.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * FileName: MybatisPlusSelectTests
 * Author:   TP
 * Description: 查詢類測(cè)試
 */
@SpringBootTest
class MybatisPlusSelectTests {

    @Autowired
    private UserMapper userMapper;

    // ========================= 非條件構(gòu)造器查詢 =========================

    /**
     * SELECT id,name,age,email,manager_id,create_time FROM user
     */
    @Test
    void testSelectList() {
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }

    /**
     * SELECT id,name,age,email,manager_id,create_time FROM user WHERE id=?
     */
    @Test
    void testSelectById() {
        User user = userMapper.selectById(1);
        System.out.println(user);
    }

    /**
     * SELECT id,name,age,email,manager_id,create_time FROM user WHERE id IN ( ? , ? )
     */
    @Test
    void testSelectBatchIds() {
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 3));
        users.forEach(System.out::println);
    }

    /**
     * SELECT id,name,age,email,manager_id,create_time FROM user WHERE manager_id = ? AND name = ?
     */
    @Test
    void testSelectByMap() {
        Map<String, Object> queryMap = new HashMap<>();
        queryMap.put("name", "向北");
        queryMap.put("manager_id", 2); // 注意map的key為表的字段名缤底,value為要查的字段值
        List<User> users = userMapper.selectByMap(queryMap);
        users.forEach(System.out::println);
    }

    /**
     * Map查詢顾患,查詢條件值為null的不會(huì)被忽略
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE manager_id = ? AND name = ? AND email IS NULL
     * ==> Parameters: 2(Integer), 向北(String)
     */
    @Test
    void testSelectByMap2() {
        Map<String, Object> queryMap = new HashMap<>();
        queryMap.put("name", "向北");
        queryMap.put("manager_id", 2); // 注意map的key為表的字段名,value為要查的字段值
        queryMap.put("email", null);
        List<User> users = userMapper.selectByMap(queryMap);
        users.forEach(System.out::println);
    }

    // ========================= Wrapper條件構(gòu)造器查詢 =========================

    /**
     * 通過(guò)條件構(gòu)造器查詢
     * 名字為"張三"个唧,郵箱包含163,并且年齡<40
     * Preparing:  SELECT id,name,age,email,manager_id,create_time FROM user WHERE (name = ? AND email LIKE ? AND age < ?)
     * Parameters: 張三(String), %163%(String), 40(Integer)
     */
    @Test
    void testSelectByWrapper() {
        // MP提供了2種創(chuàng)建QueryWrapper的方法:
        // QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        QueryWrapper<User> queryWrapper = Wrappers.query();
        queryWrapper.eq("name", "張三").like("email", "163").lt("age", 40);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 名字中包含"雨"设预,并且年齡大于等于20且小于等于40徙歼,并且email不為空
     * Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
     * Parameters: %雨%(String), 20(Integer), 40(Integer)
     */
    @Test
    void testSelectByWrapper2() {
        // MP提供了2種創(chuàng)建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name", "雨").between("age", 20, 40).isNotNull("email");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 名字為王姓或者年齡大于等于25,按照年齡降序排列鳖枕,年齡相同時(shí)按照id升序排列
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (name LIKE ? OR age >= ?) ORDER BY age DESC,id ASC
     * ==> Parameters: 王%(String), 25(Integer)
     */
    @Test
    void testSelectByWrapper3() {
        // MP提供了2種創(chuàng)建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name", "王").or().ge("age", 25).orderByDesc("age").orderByAsc("id");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 名字為王姓魄梯,并且(年齡<40或郵箱為空)
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (name LIKE ? AND (age < ? OR email IS NOT NULL))
     * ==> Parameters: 王%(String), 40(Integer)
     */
    @Test
    void testSelectByWrapper4() {
        // MP提供了2種創(chuàng)建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name", "王").and(qw -> qw.lt("age", 40).or().isNotNull("email"));
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 字為王姓,或者(年齡<40并且年齡>20并且郵箱不為空)
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (name LIKE ? OR (age < ? AND age > ? AND email IS NOT NULL))
     * ==> Parameters: 王%(String), 40(Integer), 20(Integer)
     */
    @Test
    void testSelectByWrapper5() {
        // MP提供了2種創(chuàng)建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name", "王").
                or(qw -> qw.lt("age", 40).gt("age", 20).isNotNull("email"));
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * (年齡<40或者郵箱不為空)并且姓名為王姓
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE ((age < ? OR email IS NOT NULL) AND name LIKE ?)
     * ==> Parameters: 40(Integer), 王%(String)
     */
    @Test
    void testSelectByWrapper7() {
        // MP提供了2種創(chuàng)建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.nested(wp -> wp.lt("age", 40).or().isNotNull("email")).likeRight("name", "王");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 年齡在(31宾符,32酿秸,34,45)中的
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (age IN (?,?,?,?))
     * ==> Parameters: 31(Integer), 32(Integer), 34(Integer), 35(Integer)
     */
    @Test
    void testSelectByWrapper8() {
        // MP提供了2種創(chuàng)建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("age", Arrays.asList(31, 32, 34, 35));
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 返回滿足條件的其中一條記錄即可
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (age IN (?,?,?,?)) limit 1
     * ==> Parameters: 31(Integer), 32(Integer), 34(Integer), 35(Integer)
     */
    @Test
    void testSelectByWrapper9() {
        // MP提供了2種創(chuàng)建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("age", Arrays.asList(31, 32, 34, 35)).last("limit 1"); // last會(huì)無(wú)視優(yōu)化魏烫,直接在sql后面拼接傳入的語(yǔ)句
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 默認(rèn)MP會(huì)查詢實(shí)體全部字段辣苏,如果我們只想查詢指定字段怎么辦呢肝箱?
     * 查詢指定列方式一:指定要查詢的字段
     * ==>  Preparing: SELECT id,name,age FROM user WHERE (age < ?)
     * ==> Parameters: 40(Integer)
     */
    @Test
    void testSelectByWrapper10() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "name", "age").lt("age", 40);
        // .select(String... columns)可以放在前面,也可以放在后面稀蟋,例如下面注釋內(nèi)容
        // queryWrapper.lt("age", 40).select("id", "name", "age");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 默認(rèn)MP會(huì)查詢實(shí)體全部字段煌张,如果我們只想查詢指定字段怎么辦呢?
     * 查詢指定列方式二:動(dòng)態(tài)指定要查詢的字段退客,更有利于查詢實(shí)體類中多個(gè)字段骏融,不想像方式一那樣一個(gè)一個(gè)寫的情形
     * ==>  Preparing: SELECT id,name,age,manager_id FROM user
     */
    @Test
    void testSelectByWrapper11() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        // 排除create_time和email字段,即不查詢create_time和email字段
        queryWrapper.select(User.class, info -> !info.getColumn().equals("create_time") && !info.getColumn().equals("email"));
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 關(guān)于condition
     * 在QueryWrapper的各個(gè)查詢方法中萌狂,一般都會(huì)有重載的方法档玻,例如like
     * <p>
     * default Children like(R column, Object val) {
     * return this.like(true, column, val);
     * }
     * <p>
     * Children like(boolean condition, R column, Object val);
     * <p>
     * 其中有個(gè)重載的方法,前面需要傳一個(gè)boolean類型的condition茫藏,它的含義是:如果  condition的值為true時(shí)窃肠,則添加后面的查詢條件
     * 例如我們做頁(yè)面的列表,用戶可輸入多個(gè)查詢條件刷允,有值的就添加上查詢條件
     */
    @Test
    void testSelectByWrapper12() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        boolean condition = (int) (Math.random() * 100) % 2 == 1;
        System.out.println(condition);
        queryWrapper
                .lt("age", 40)
                .eq(condition, "name", "張三"); //如果condition為false時(shí)冤留,不會(huì)添加查詢條件:and name = "張三"
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }


    /**
     * 我們可以通過(guò)一個(gè)實(shí)體構(gòu)造一個(gè)queryWrapper,MP默認(rèn)會(huì)將實(shí)體屬性中值不為空的作為查詢條件树灶,用and連接生成sql
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE name=? AND age=?
     * ==> Parameters: 向東(String), 58(Integer)
     */
    @Test
    void testSelectByEntity() {
        User queryUser = new User();
        queryUser.setName("向東");
        queryUser.setAge(58);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>(queryUser);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }


    /**
     * 調(diào)用queryWrapper.allEq(params)      時(shí)纤怒,params中的null值會(huì)被翻譯成:字段名 is null
     * 調(diào)用queryWrapper.allEq(params,false)時(shí),params中的null值會(huì)被忽略
     */
    @Test
    void testAllEq() {
        // name = "向東" and age is null
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        Map<String, Object> params = new HashMap<>();
        params.put("name", "向東");
        params.put("age", null);
        queryWrapper.allEq(params);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);

        // name = "向東"
        QueryWrapper<User> queryWrapper2 = new QueryWrapper<>();
        Map<String, Object> params2 = new HashMap<>();
        params2.put("name", "向東");
        params2.put("age", null);
        queryWrapper2.allEq(params2, false);
        List<User> users2 = userMapper.selectList(queryWrapper2);
        users2.forEach(System.out::println);
    }

    /**
     * 查詢返回一個(gè)map天通,map的key為數(shù)據(jù)庫(kù)中的字段
     * 應(yīng)用場(chǎng)景1:當(dāng)我們查詢的字段不是所有字段泊窘,只是部分字段,如果我們用實(shí)體封裝像寒,那么實(shí)體中中未查詢的字段就為空或者默認(rèn)值烘豹,不優(yōu)雅,這個(gè)時(shí)候可以用map接收指定查詢的字段
     * 應(yīng)用場(chǎng)景2:當(dāng)我們需要將數(shù)據(jù)庫(kù)中的字段進(jìn)行轉(zhuǎn)換诺祸,或者使用函數(shù)時(shí)携悯,這時(shí)候無(wú)法用實(shí)體封裝查詢結(jié)果,我們可以選擇使用map
     */
    @Test
    void testSelectMaps() {
        // 應(yīng)用場(chǎng)景1
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", "向東");
        queryWrapper.select("name", "age"); // 只查詢部分字段
        List<Map<String, Object>> result = userMapper.selectMaps(queryWrapper);
        result.forEach(System.out::println);

        //應(yīng)用場(chǎng)景2
        QueryWrapper<User> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2
                .select("avg(age) avg_age", "min(age) min_age", "max(age) max_age", "manager_id") // 計(jì)算查詢
                .groupBy("manager_id").having("sum(age)<{0}", 50);
        List<Map<String, Object>> result2 = userMapper.selectMaps(queryWrapper2);
        result2.forEach(System.out::println);
    }

    /**
     * 根據(jù)Wrapper查詢筷笨,查詢?nèi)坑涗?     * 注意:只返回結(jié)果第一個(gè)字段的值
     */
    @Test
    void testSelectObjects() {
        // 應(yīng)用場(chǎng)景1
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "name").lt("age", 60);
        List<Object> objects = userMapper.selectObjs(queryWrapper); // 只會(huì)返回id憔鬼,不會(huì)返回name  {1,2,3,4,5,6,7,8}
        objects.forEach(System.out::println);

    }

    /**
     * 根據(jù)查詢條件進(jìn)行統(tǒng)計(jì)總記錄數(shù)
     */
    @Test
    void testSelectCount() {
        // 應(yīng)用場(chǎng)景1
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lt("age", 60);
        Integer count = userMapper.selectCount(queryWrapper);
        System.out.println(count);

    }

    /**
     * 查詢1條結(jié)果,結(jié)果必須為1條或0條
     * 如果數(shù)據(jù)庫(kù)存在多條會(huì)報(bào)錯(cuò)
     */
    @Test
    void testSelectOne() {
        // 應(yīng)用場(chǎng)景1
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", 1);
        User user = userMapper.selectOne(queryWrapper);
        System.out.println(user);
    }

    // ========================= Lambda條件構(gòu)造器查詢 =========================

    /**
     * Lambda條件構(gòu)造器查詢能有效防止我們手寫數(shù)據(jù)庫(kù)字段寫錯(cuò)問(wèn)題
     */
    @Test
    void testSelectLambda1() {
        // 創(chuàng)建Lambda條件構(gòu)造器的方式有3種:
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<User> lambdaQueryWrapper1 = Wrappers.lambdaQuery();
        LambdaQueryWrapper lambdaQueryWrapper2 = new QueryWrapper<User>().lambda();
        // User::getNamed代表要查詢name這個(gè)列胃夏,lambdaQueryWrapper的好處是避免手寫要查詢字段字符串轴或,如果手寫的也數(shù)據(jù)庫(kù)不一致造成報(bào)錯(cuò)問(wèn)題
        lambdaQueryWrapper.eq(User::getName, "向東").lt(User::getAge, 80);
        List<User> users = userMapper.selectList(lambdaQueryWrapper);
        users.forEach(System.out::println);
    }

    @Test
    void testSelectLambda2() {
        // 創(chuàng)建Lambda條件構(gòu)造器的方式有3種:
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<User> lambdaQueryWrapper1 = Wrappers.lambdaQuery();
        LambdaQueryWrapper lambdaQueryWrapper2 = new QueryWrapper<User>().lambda();
        // where name like %王% and (age < 80 or email is not null)
        lambdaQueryWrapper.likeRight(User::getName, "王").and(lqw -> lqw.lt(User::getAge, 40).or().isNotNull(User::getEmail));
        List<User> users = userMapper.selectList(lambdaQueryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * LambdaQueryChainWrapper,這個(gè)可以再次簡(jiǎn)化寫法仰禀,可以直接調(diào)用.list() 或者 .one() 返回查詢結(jié)果
     */
    @Test
    void testSelectLambdaQueryChainWrapper() {
        // 創(chuàng)建Lambda條件構(gòu)造器的方式有3種:
        LambdaQueryChainWrapper<User> lambdaQueryChainWrapper = new LambdaQueryChainWrapper<>(userMapper);
        List<User> users = lambdaQueryChainWrapper
                .likeRight(User::getName, "王")
                .and(lqw -> lqw.lt(User::getAge, 40).or().isNotNull(User::getEmail))
                .list(); // 可以直接調(diào)用.list() 或者 .one() 返回查詢結(jié)果
        users.forEach(System.out::println);
    }
}
修改
package com.tp.demo.mp;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.tp.demo.mp.entity.User;
import com.tp.demo.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * FileName: MybatisPlusUpdateTests
 * Author:   TP
 * Description:
 */
@SpringBootTest
class MybatisPlusUpdateTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * UpdateById
     * 根據(jù)主鍵ID修改實(shí)體信息的方法
     * 此更新會(huì)忽略實(shí)體null值照雁,不會(huì)更新實(shí)體中屬性值為null對(duì)應(yīng)的字段
     */
    @Test
    void testUpdateById() {
        User user = new User();
        user.setId(8);
        user.setEmail("xd@163.com");
        userMapper.updateById(user);
    }

    /**
     * 利用UpdateWrapper更新實(shí)體>>>>空參構(gòu)造器:UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
     * UpdateWrapper的條件會(huì)出現(xiàn)在where條件中
     * 實(shí)體不為空的信息是要更新的字段
     */
    @Test
    void testUpdateByWrapper() {
        // UpdateWrapper的條件會(huì)出現(xiàn)在where條件中
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("name", "李藝偉");
        updateWrapper.eq("age", 28);

        // 實(shí)體不為空的信息是要更新的字段
        User user = new User();
        user.setEmail("lyw@126.com");
        user.setManagerId(1);

        // 返回影響行數(shù)
        int rows = userMapper.update(user, updateWrapper);
        System.out.println("影響行數(shù):" + rows);

    }

    /**
     * 利用UpdateWrapper更新實(shí)體>>>>帶參構(gòu)造器:UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(whereUser);
     * 這時(shí)候updateWrapper中會(huì)將whereUser這個(gè)實(shí)體中不為空的屬性設(shè)置為where查詢條件
     * 注意:這個(gè)時(shí)候whereUser實(shí)體的查詢條件和后續(xù)updateWrapper調(diào)用查詢方法時(shí)指定的查詢條件會(huì)同時(shí)生效,如果存在相同查詢條件答恶,查詢條件會(huì)出現(xiàn)2次
     */
    @Test
    void testUpdateByWrapper2() {
        // 通過(guò)實(shí)體構(gòu)造查詢條件
        User whereUser = new User();
        whereUser.setName("李藝偉");
        whereUser.setAge(28);
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(whereUser);
        // updateWrapper.eq("manager_id", 1); // 可以繼續(xù)添加查詢條件

        // 實(shí)體不為空的信息是要更新的字段
        User user = new User();
        user.setEmail("lyw@qq.com");
        user.setManagerId(2);

        // 返回影響行數(shù)
        int rows = userMapper.update(user, updateWrapper);
        System.out.println("影響行數(shù):" + rows);

    }

    /**
     * 利用UpdateWrapper更新實(shí)體>>>>帶參構(gòu)造器:UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(whereUser);
     * 如果我們更新的字段很少饺蚊,例如實(shí)體中我們只更新1萍诱、2個(gè)字段,如果像上面的更新還需要?jiǎng)?chuàng)建一個(gè)對(duì)象卸勺,這樣有點(diǎn)不優(yōu)雅
     * 又或者我們的需求是想把某個(gè)字段設(shè)置為null
     * 這時(shí)候我們可以使用UpdateWrapper的set方法砂沛,指定設(shè)置數(shù)據(jù)庫(kù)表中具體哪個(gè)字段設(shè)置為什么值
     */
    @Test
    void testUpdateByWrapper3() {
        // 通過(guò)實(shí)體構(gòu)造查詢條件
        User whereUser = new User();
        whereUser.setName("李藝偉");
        whereUser.setAge(28);
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(whereUser);
        updateWrapper.set("age", 29).set("email", null);

        // 返回影響行數(shù)
        int rows = userMapper.update(null, updateWrapper);
        System.out.println("影響行數(shù):" + rows);

    }

    /**
     * UpdateWrapper同樣支持Lambda形式,用法類似LambdaQueryWrapper
     */
    @Test
    void testUpdateByWrapperLambda() {
        // 構(gòu)造LambdaUpdateWrapper
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(User::getName, "李藝偉").set(User::getAge, 30).set(User::getEmail, "lyw@163.com");
        // 返回影響行數(shù)
        int rows = userMapper.update(null, lambdaUpdateWrapper);
        System.out.println("影響行數(shù):" + rows);
    }

    /**
     * 同理曙求,UpdateWrapper同樣支持LambdaChain形式
     * 用法類似LambdaQueryChainWrapper
     * 但其返回值不是影響行數(shù)碍庵,而是boolean值,標(biāo)識(shí)更新成功或失敗
     */
    @Test
    void testUpdateByWrapperLambdaChain() {
        // 構(gòu)造LambdaUpdateWrapper
        LambdaUpdateChainWrapper<User> chainWrapper = new LambdaUpdateChainWrapper<>(userMapper);
        boolean result = chainWrapper
                .eq(User::getName, "李藝偉") // 設(shè)置查詢條件
                .set(User::getAge, 31).set(User::getEmail, "lyw@chain.com") //設(shè)置要更新的內(nèi)容
                .update(); //需要調(diào)用update()悟狱,執(zhí)行更新
        System.out.println("更新是否成功:" + result);

    }
}
刪除
package com.tp.demo.mp;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tp.demo.mp.entity.User;
import com.tp.demo.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * FileName: MybatisPlusDeleteTests
 * Author:   TP
 * Description:
 */
@SpringBootTest
class MybatisPlusDeleteTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * 根據(jù)ID單個(gè)刪除
     */
    @Test
    void testDeleteById() {
        int rows = userMapper.deleteById(8);
        System.out.println("影響記錄數(shù):" + rows);
    }

    /**
     * 根據(jù)ID批量刪除
     */
    @Test
    void testDeleteBatchIds() {
        int rows = userMapper.deleteBatchIds(Arrays.asList(9, 10));
        System.out.println("影響記錄數(shù):" + rows);
    }

    /**
     * 根據(jù)Map静浴,map中的元素會(huì)作為查詢條件且等值and連接
     */
    @Test
    void testDeleteByMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "向東");
        map.put("age", 31);
        map.put("manager_id", null);
        int rows = userMapper.deleteByMap(map);
        System.out.println("影響記錄數(shù):" + rows);
    }

    /**
     * 根據(jù)QueryWrapper條件構(gòu)造器刪除
     */
    @Test
    void testDeleteByWrapper() {
        QueryWrapper<User> queryWrapper = Wrappers.query();
        queryWrapper.eq("name", "向東");
        queryWrapper.eq("age", "31");
        int rows = userMapper.delete(queryWrapper);
        System.out.println("影響記錄數(shù):" + rows);
    }

    /**
     * 根據(jù)QueryWrapper條件構(gòu)造器,通過(guò)指定一個(gè)實(shí)體刪除
     */
    @Test
    void testDeleteByWrapper2() {
        User user = new User();
        user.setName("向東");
        user.setAge(31);
        QueryWrapper<User> queryWrapper = Wrappers.query(user);
        int rows = userMapper.delete(queryWrapper);
        System.out.println("影響記錄數(shù):" + rows);
    }

    /**
     * 根據(jù)LambdaQueryWrapper條件構(gòu)造器刪除
     */
    @Test
    void testDeleteByLambdaQueryWrapper() {
        LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery();
        // DELETE FROM user WHERE (name = ? OR age >= ?)
        lambdaQueryWrapper.eq(User::getName, "向東").or().ge(User::getAge, 60);
        int rows = userMapper.delete(lambdaQueryWrapper);
        System.out.println("影響記錄數(shù):" + rows);
    }

    /**
     * 根據(jù)LambdaQueryWrapper條件構(gòu)造器挤渐,通過(guò)指定一個(gè)實(shí)體刪除
     */
    @Test
    void testDeleteByLambdaQueryWrapper2() {
        User user = new User();
        user.setName("向東");
        user.setAge(50);
        LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery(user);
        int rows = userMapper.delete(lambdaQueryWrapper);
        System.out.println("影響記錄數(shù):" + rows);
    }
}

Mybatis-Plus分頁(yè)

Mybatis-Plus為我們提供了非常方便的分頁(yè)插件苹享,用法:

package com.tp.demo.mp.config;

import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * FileName: MybatisPlusPageConfig
 * Author:   TP
 * Description: 使用mybatisPlus的分頁(yè)插件
 * 只需要提供一個(gè)PaginationInnerInterceptor交給Spring管理即可
 */
@Configuration
public class MybatisPlusPageConfig {

    @Bean
    public PaginationInnerInterceptor paginationInnerInterceptor(){
        return new PaginationInnerInterceptor();
    }
}

測(cè)試:

package com.tp.demo.mp;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tp.demo.mp.entity.User;
import com.tp.demo.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;
import java.util.Map;

/**
 * FileName: MybatisPlusPageTests
 * Author:   TP
 * Description: mybatisPlus分頁(yè)測(cè)試
 */
@SpringBootTest
class MybatisPlusPageTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * 分頁(yè),結(jié)果采用實(shí)體封裝
     */
    @Test
    void testPage1() {
        Page<User> userPage = new Page<>(1, 10); //當(dāng)前頁(yè)為第1頁(yè)浴麻,每頁(yè)顯示10條
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lt("age", 80);
        Page<User> pageResult = userMapper.selectPage(userPage, queryWrapper);
        System.out.println("總頁(yè)數(shù):" + pageResult.getPages());
        System.out.println("總記錄數(shù):" + pageResult.getTotal());
        List<User> users = pageResult.getRecords();
        users.forEach(System.out::println);
    }

    /**
     * 分頁(yè)得问,結(jié)果采用Map封裝,key為列名或者列別名软免,value為值
     */
    @Test
    void testPage2() {
        Page<Map<String, Object>> userPage = new Page<>(1, 10); //當(dāng)前頁(yè)為第1頁(yè)宫纬,每頁(yè)顯示10條
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lt("age", 80).select("name", "age");
        Page<Map<String, Object>> pageResult = userMapper.selectMapsPage(userPage, queryWrapper);
        System.out.println("總頁(yè)數(shù):" + pageResult.getPages());
        System.out.println("總記錄數(shù):" + pageResult.getTotal());
        List<Map<String, Object>> users = pageResult.getRecords();
        users.forEach(System.out::println);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市膏萧,隨后出現(xiàn)的幾起案子漓骚,更是在濱河造成了極大的恐慌,老刑警劉巖榛泛,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝌蹂,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡曹锨,警方通過(guò)查閱死者的電腦和手機(jī)孤个,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)艘希,“玉大人硼身,你說(shuō)我怎么就攤上這事「蚕恚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵营袜,是天一觀的道長(zhǎng)撒顿。 經(jīng)常有香客問(wèn)我,道長(zhǎng)荚板,這世上最難降的妖魔是什么凤壁? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任吩屹,我火速辦了婚禮,結(jié)果婚禮上拧抖,老公的妹妹穿的比我還像新娘煤搜。我一直安慰自己,他們只是感情好唧席,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布擦盾。 她就那樣靜靜地躺著,像睡著了一般淌哟。 火紅的嫁衣襯著肌膚如雪迹卢。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天徒仓,我揣著相機(jī)與錄音腐碱,去河邊找鬼。 笑死掉弛,一個(gè)胖子當(dāng)著我的面吹牛症见,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播殃饿,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谋作,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了壁晒?” 一聲冷哼從身側(cè)響起瓷们,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秒咐,沒(méi)想到半個(gè)月后谬晕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡携取,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年攒钳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雷滋。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡不撑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出晤斩,到底是詐尸還是另有隱情焕檬,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布澳泵,位于F島的核電站实愚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腊敲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一击喂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碰辅,春花似錦懂昂、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至榕吼,卻和暖如春饿序,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背羹蚣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工原探, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人顽素。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓咽弦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親胁出。 傳聞我的和親對(duì)象是個(gè)殘疾皇子型型,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355