MyBatisPlus

MyBatisPlus

  • MyBatisPlus简称mp,是mybatis的增强工具,在mybatis的基础上只做增强,不做修改
  • 特性
    1. 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
    2. 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
    3. 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
    4. 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
    5. 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
    6. 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
    7. 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
    8. 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
    9. 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
    10. 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
    11. 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
    12. 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
  • 构建mybatis-plus-demo
    1. 导入依赖
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.4.3</version>
              <relativePath/>
          </parent>
      
          <dependencies>
      
              <dependency>
                  <groupId>com.baomidou</groupId>
                  <artifactId>mybatis-plus-boot-starter</artifactId>
                  <version>3.4.0</version>
              </dependency>
      
              <dependency>
                  <groupId>com.alibaba</groupId>
                  <artifactId>druid-spring-boot-starter</artifactId>
                  <version>1.1.17</version>
              </dependency>
      
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.22</version>
              </dependency>
      
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
              </dependency>
      
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <version>1.18.16</version>
                  <scope>provided</scope>
              </dependency>
      
          </dependencies>
      
    2. 新建application.properties文件
          server.port=80
          #这里记得配置下时区serverTimezone(东八区),否则可能会报错
          spring.datasource.url=jdbc:mysql:///mybatis-plus?useUnicode=true&\
          characterEncoding=utf8&serverTimezone=GMT%2B8
          spring.datasource.username=root
          spring.datasource.password=admin
          spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
          # 配置slq打印日志
          logging.level.cn.wolfcode.mp.mapper=debug
      
    3. 按照以往常规操作,我们做crud,需要编写mapper接口,并在mapper.xml中编写对应的sql语句,但是在mybatisPlus中,只需创建好对应的mapper接口,继承下 BaseMapper<实体类泛型(如:Employee)>,常规的crud都能在BaseMapper中找到,所以就无须我们在自己的mapper接口中写了(如:EmployeeMapper),具体操作如下
      • 当spring容器启动后,mybatisPlus就开始解析EmployeeMapper,上面指定了泛型Employee
      • 通过反射拿到类型Employee,然后得到其类名作为数据库表名,得到其属性作为数据库表中对应的列名
      • 将解析出来的数据根据调用的方法来拼接出不同的语句.
    4. mybatisPlus中的方法及注意点:
      • 常用注解:
        1. @TableName表名注解:指当前类映射数据库中哪一张表,默认实体类与表名一致,当不一致的使用时:如数据库表名为t_employee,实体类名为Employee,因为名字不一致,所以直接肯定是映射不到的,此时只需在实体类Employee上贴上@TableName("t_employee")即可,表示该实体类映射到t_employee这张表
        2. @TableId主键注解:标记当前属性映射表主键:如
              @TableName("employee")
              public class Employee {
                  //value="id"对应数据库的列名,IdType.AUTO表示自增长(还可以有其他的设置方式,如自己输入,详情可查看对应的api)
                  @TableId(value="id", type=IdType.AUTO)
                  private Long id;
              }
          
        3. @TableField字段注解(非主键):指定当前属性映射数据库表哪一列, 默认是跟属性名一致,如:
              //其中:exist属性表示当前属性是否映射数据库列,当我们是自定义的属性,就额外加的,不是和数据库对应的,那么就必须@TableField(exist = false)这样设置下,否则会报错
               @TableField(value="employename",exist = true)
              private String name;
          
      • updateById:根据id修改,等价于update employee set xxx=xxx where id = xxx
      • updateById的注意点:(它的set片段会根据传入的实体对象(如:employee)对应的属性是否为null来判断是否拼接)
        1. 当对应的属性为null时,那么set片段中就不会把对应的属性拼接上去(即:执行更新操作后,数据库中对应的列值不会发生变化)
        2. 当对应的属性不为null时,便会拼接到set片段中,当属性为基本类型时(都有默认值),那么始终都会拼接上去发生修改,那要如何解决这个问题呢?
          • 将基本数据类型改为包装类型
          • 使用update方法,而不是使用updateById方法
      • update:等价于:update employee set xxx = xxx where 其他列= xxx,具体的条件有updateWrapper提供
            //将id为20的员工,名字更新为laoqi
            UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
            wrapper.eq("id",20);
            //这种写法的意思是将name值为zs的数据修改器age值为18
            //wrapper.eq("name","zs").set("age",18);
            wrapper.set("name","laoqi");
            employeeMapper.update(null,wrapper);
        
        由上可知update方法,第一个参数(实体类对象)可以为空,条件都写在UpdateWrapper中即可,因为在UpdateWrapper中传入了对应的泛型,且条件和修改都在wrapper.eq和wrapper.set中写了,所以可以这样操作,使用update时,只有将条件设入进来了后才会拼接到对应的set片段中,就不会出现updateById那种自动将基本数据类型设入进set片段的问题了
      • update与updateById如何选用?
        1. 使用updateById场景
          • where条件是id时候update场景
          • 进行全量(所有字段)更新时候
        2. 使用update场景
          • where条件是不确定,或者多条件的update场景
          • 进行部分字段更新时候
      • 删除相关
        1. deleteById根据主键删除:
        2. deleteByMap根据条件删除,多个条件是就只能自动拼接成and了,而不能处理or情况
        3. delete通过QueryWrapper可以处理and,or等各种复杂的情况
        4. deleteBatchIds批量删除
            public void testDelete(){
        
            //1.根据主键删除:DELETE FROM employee WHERE id=?
            //employeeMapper.deleteById(21);
        
            //2.根据条件删除:DELETE FROM employee WHERE name = ?
            // HashMap<String, Object> map = new HashMap<>();
            // map.put("name","zs");
            // employeeMapper.deleteByMap(map);
        
            //3.根据 entity 条件,删除记录,除了更新条件操作使用UpdateWrapper外,
            // 其余的操作都用QueryWrapper
            //DELETE FROM employee WHERE (id = ? AND name = ? OR age = ?)
            QueryWrapper<Employee> wrapper = new QueryWrapper<>();
            wrapper.eq("id",2L);
            wrapper.eq("name","zs").or().eq("age",18);
            employeeMapper.delete(wrapper);
        
            //4.批量删除:DELETE FROM employee WHERE id IN ( ? , ? , ? )
            //employeeMapper.deleteBatchIds(Arrays.asList(33,44,55));
            }
        
      • select相关:
            @Test
            public void testSelect(){
        
                //1.根据主键查询:
                // 得到语句:SELECT id,name,password,email,age,admin,dept_id
                // FROM employee WHERE id=?
                // employeeMapper.selectById(20L);
        
                //2.根据id批量查询
                //得到语句:SELECT id,name,password,email,age,admin,dept_id
                // FROM employee WHERE id IN ( ? , ? , ? )
                // employeeMapper.selectBatchIds(Arrays.asList(1,2,3));
        
                //3.根据columnMap条件
                //得到语句:SELECT id,name,password,email,age,admin,dept_id
                // FROM employee WHERE name = ? AND age = ?
                // HashMap<String, Object> map = new HashMap<>();
                // map.put("name","zs");
                // map.put("age",18);
                // employeeMapper.selectByMap(map);
        
                //4. 根据 entity 条件,查询一条记录
                //得到的语句:SELECT id,name,password,email,age,admin,dept_id
                // FROM employee WHERE (name = ?)
                //如果查出的结果不止一个就会报错如:
                //Expected one result (or null) to be returned by selectOne(), but found: 20
                // QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                // wrapper.eq("name","zs");
                // employeeMapper.selectOne(wrapper);
        
                //5.根据 Wrapper 条件,查询总记录数
                //得到语句:SELECT COUNT( 1 ) FROM employee这里的count(1)与count(*)效果一样
                //所以也可以不要wrapper直接写employeeMapper.selectCount(null)就是表示查所有
                // QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                // employeeMapper.selectCount(wrapper);
        
                //6. 根据 entity 条件,查询全部记录(selectList会把结果封装成实体类对象)
                //得到语句:SELECT id,name,password,email,age,admin,dept_id FROM employee
                //如果wrapper中没有条件,则可以直接写成employeeMapper.selectList(null),查全部
                // QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                // List<Employee> selectList = employeeMapper.selectList(wrapper);
                // selectList.forEach(System.out::println);
        
                //7. 根据 Wrapper 条件,查询全部记录(selectMaps会把结果封装成map)
                //得到语句: SELECT id,name,password,email,age,admin,dept_id FROM employee
                //如果查询全部则可以直接写成employeeMapper.selectMaps(null);
                // QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                // List<Map<String, Object>> maps = employeeMapper.selectMaps(wrapper);
                // maps.forEach(System.out::println);
        
                //8.分页查询(注意:一定要先配置分页插件,这里的分页查询才起效果)
                //得到语句:SELECT id,name,password,email,age,admin,dept_id
                // FROM employee LIMIT ?
                QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                //参数表示:当前页为1,每页显示4条数据
                Page<Employee> page = new Page<>(1,4);
                //wrapper是分页的条件
                //这里采用的是链式编程,查询完后的结果还是封装在传入的那个page中的
                employeeMapper.selectPage(page,wrapper);
                System.out.println("当前页:" + page.getCurrent());
                System.out.println("每页显示条数:" + page.getSize());
                System.out.println("总页数:" + page.getPages());
                System.out.println("总数:" + page.getTotal());
                System.out.println("当前页数据:" + page.getRecords());
            }
        
    5. 构造器相关:
      • 条件构造器:根据名字可以简单理解为条件拼接对象,用于生成 sql 的 where 条件
        1. Wrapper: 条件构造抽象类,最顶端父类
        2. AbstractWrapper: 用于查询条件封装,生成 sql 的 where 条件
        3. QueryWrapper: Entity 对象封装操作类,不是用lambda语法
        4. UpdateWrapper: Update 条件封装,用于Entity对象更新操作
        5. AbstractLambdaWrapper: Lambda 语法使用 Wrapper统一处理解析 lambda 获取column。
        6. LambdaQueryWrapper:看名称也能明白就是用于Lambda语法使用的查询Wrapper
        7. LambdaUpdateWrapper: Lambda 更新封装Wrapper
      • 独有的方法(updateWrapper类型与QueryWrappe类型都是继承的AbstractWrapper,那么各自独有的方法是什么呢?)
        1. UpdateWrapper与LambdaUpadateWrapper独有的是:set,setSql
        2. QueryWrapper与LambdaQueryWrapper独有的是:select
            //需求:将id=zs
            @Test
            public void testUpdate2(){
                UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
                wrapper.eq("id", 1L);
                wrapper.set("name", "zs");
                //wrapper.setSql("name='zs'");
                employeeMapper.update(null, wrapper);
            }
        
            //set与setSql的区别?
            //set是采用的占位符形式:UPDATE employee SET name=? WHERE (id = ?)
            //setSql是直接把sql写死的:UPDATE employee SET name='zs' WHERE (id = ?)
        
      • LambdaUpdateWrapper更新
            @Test
            public void testUpdate4(){
                //将id=1的用户name改为zs
                //UPDATE employee SET name=? WHERE (id = ?)
                LambdaUpdateWrapper<Employee> wrapper = new LambdaUpdateWrapper<>();
                wrapper.eq(Employee::getId, 1L);
                wrapper.set(Employee::getName,"zs");
                employeeMapper.update(null, wrapper);
            }
        
      • 查询
            @Test
            public void testQuery(){
                //1:查询所有员工,返回员工name,age列
                //得到语句:SELECT name,age FROM employee
                QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                wrapper.select("name,age");
                //这种写法也可以
                // wrapper.select("name","age");
                employeeMapper.selectList(wrapper);
            }
        
            @Test
            public void testQuery1(){
                //1:查询所有员工, 返回员工 以a字母开头的列(注意了是列名为a,不是列值)
                //得到语句:SELECT id,age,admin FROM employee
                QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                wrapper.select(Employee.class,
                        tableFieldInfo -> tableFieldInfo.getProperty().startsWith("a"));
                employeeMapper.selectList(wrapper);
            }
        
      • 排序:orderByAsc/orderByDesc
            @Test
            public void testQuery5(){
                //1.根据age,id进行升序查询,password降序
                //得到语句:SELECT id,name,password,email,age,admin,dept_id
                // FROM employee ORDER BY age ASC,id ASC,password DESC
                QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                wrapper.orderByAsc("age", "id");
                //降序
                wrapper.orderByDesc("password");
                employeeMapper.selectList(wrapper);
            }
        
      • 条件运算符
        1. 比较运算符
          • 全等匹配
                @Test
                public void testQuery8(){
                    //1.查询name为zs,age为18的员工
                    QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                    Map<String, Object> map = new HashMap<>();
                    map.put("name", "zs");
                    map.put("age", 18);
                    map.put("dept_id", null);
                    //allEq(map)方式得到语句:ELECT id,name,password,email,
                    // age,admin,dept_id
                    // FROM employee WHERE (name = ? AND dept_id IS NULL AND age = ?)
                    wrapper.allEq(map);
            
                    //allEq(map,false)方式得到语句:SELECT id,name,password,email,
                    // age,admin,dept_id
                    // FROM employee WHERE (name = ? AND age = ?)
                    //null2IsNull:该参数表示为ture时:map中与key对应的value为null时就会
                    // 调用key is null 的方法,为false时:就直接将value为null的条件剔除了
                    //null2IsNull默认是为true的,所以我们在 wrapper.allEq(map);方法中
                    //看到的语句是带isnull的
                    // wrapper.allEq(map,false);
                    employeeMapper.selectList(wrapper);
                }
            
          • 全等匹配(带条件过滤的)
                @Test
                public void testQuery9() {
                    //需求:查询满足条件员工信息, 注意传入的map条件中, 包含a的列才参与条件查询
                    QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                    Map<String, Object> map = new HashMap<>();
                    map.put("name", "zs");
                    map.put("age", 18);
                    //这样写得到的语句为:SELECT id,name,password,email,age,admin,dept_id
                    // FROM employee WHERE (age = ?)
                    // wrapper.allEq((k, v) -> k.contains("g"), map);
            
                    //这样写得到的语句为:SELECT id,name,password,email,age,admin,dept_id
                    // FROM employee WHERE (name = ? AND age = ?)
                    wrapper.allEq((k, v) -> k.contains("a"), map);
            
                    //这个表示只有列值为zs的条件才生效,否则被抛弃
                    //wrapper.allEq((k, v) -> v.equals("zs"), map);
            
                    //总结:k.contains("xx")表示列名必须为xx的才参与查询
            
                    //filter : 过滤函数,是否允许字段传入比对条件中
                    //例1: allEq((k,v) -> k.indexOf("a") >= 0,
                    // {id:1,name:"老王",age:null})--->name = '老王' and age is null
                    //例2: allEq((k,v) -> k.indexOf("a") >= 0,
                    // {id:1,name:"老王",age:null}, false)--->name = '老 王'
                    //总结:allEq((k,v) -> k.indexOf("a") >= 0,条件,false)当条件后面设置的是false时
                    //那么条件中value为null的条件就直接丢弃了
                    employeeMapper.selectList(wrapper);
                }
            
          • 其他相关:
            1. eq::单个参数判断是否相等

            2. ne: 不等于

            3. gt : 大于 >

            4. ge:大于等于 >=

            5. lt:小于 <

            6. le:小于等于 <=

            7. between:位于两者之间,比如年龄在18到30之间( between("age", 18, 30)得到语句--->age between 18 and 30)

            8. notBetween:位于两者之外,用法同上(notBetween("age", 18, 30))

            9. isNull/isNotNull:字段(IS NULL)/( IS NOT NULL),比如( isNull("name")得到语句--->name is null),( isNotNull("name")得到语句--->name is not null)

            10. in/notIn:字段IN (value1, value2, ...)/字段NOT IN (value1, value2, ...)比如:wrapper.in("age", 1L, 2L)得到语句-->age in (1,2);同理notIn得到语句age not in (1,2)

            11. inSql/notInSql:字段 IN ( sql语句 )用法同in与notIn只是insql与notInSql没有使用占位符,是写死的sql语句

            12. like/notLike: LIKE '%值%'(NOT LIKE '%值%'),得到的sql语句为: like("name", "王")得到sql--->name like(not like) '%王%'

            13. likeLeft/likeRight:%在左边或者右边,如: likeLeft("name", "王")得到sql--->name like '%王, likeRight("name", "王")得到sql--->name like '王%'

            14. or:是用来拼接or的,主动调用表示下一个条件不是用and连接(默认是用and连接),而是用or连接,举例如下:

                  @Test
                  public void testQuery24(){
                      QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                      //age为18并且name为zs,或者id我1的用户
                      //得到语句为:SELECT id,name,password,email,age,admin,dept_id 
                      // FROM employee WHERE (age = ? AND name = ? OR id = ?)
                      wrapper.eq("age", 18)
                              .eq("name", "zs")
                              .or()
                              .eq("id", 1L);
                      employeeMapper.selectList(wrapper);
                  }
              
            15. 嵌套OR:要用or(wr-> wr.ge("age",18).le("age",30));

                  @Test
                  public void testQuery25(){
                      //需求:查询name含有zs字样的,或者 年龄在18到30之间的用户
                      //得到语句: SELECT id,name,password,email,age,admin,dept_id 
                      // FROM employee WHERE (name LIKE ? OR (age >= ? AND age <= ?))
                      QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                  wrapper.like("name","zs").or(wr-> wr.ge("age",18).le("age",30));
                      employeeMapper.selectList(wrapper);
                  }
              
            16. groupBy:分组查询:注音分组查询,查询的只能是分组的条件,这里的就是dept_id,不能是其他列名,否则会报错(MySQL默认显示的是
              分组数据的第一条,其他数据库就会报错),如果硬要用其他列名,那么只能在统计函数中,如count(id):

                  @Test
                  public void testQuery26(){
                      //需求: 以部门id进行分组查询,查每个部门员工个数
                      //得到语句:SELECT dept_id,count(id) count FROM employee GROUP BY dept_id
                      //注音分组查询,查询的只能是分组的条件,这里的就是dept_id,不能是其他列名,否则会报错(MySQL默认显示的是
                      // 分组数据的第一条,其他数据库就会报错),如果硬要用其他列名,那么只能在统计函数中,如count(id)
                      QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                      wrapper.groupBy("dept_id");
                      wrapper.select("dept_id","count(id) count");
                      employeeMapper.selectList(wrapper);
                  }
              
            17. having:分组完后执行的的条件

                   @Test
                  public void testQuery27() {
                      // 以部门id进行分组查询,查每个部门员工个数, 将大于3人的部门过滤出来
                      QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                      wrapper.groupBy("dept_id");
                      wrapper.select("dept_id", "count(id) count");
              
                      //得到语句:SELECT dept_id,count(id) count 
                      // FROM employee GROUP BY dept_id HAVING count>2
                      wrapper.having("count>2");
              
                      //得到语句:SELECT dept_id,count(id) count
                      // FROM employee GROUP BY dept_id HAVING count>?
                      // wrapper.having("count>{0}",2);
                      employeeMapper.selectList(wrapper);
                  }
              
            18. 自定义SQL:1.在resouces下面建立相同路径的XxxMapper.xml文件,像以前一样写好对应的mapper接口方法和对应的sql,然后新建好对应的service,此时service只需要在接口那里继承下IService<实体类>,便可获得常用的crud方法的声明,同时在对应的接口实现类再继承下ServiceImpl<实体类Mapper,实体类>,便可获得常用crud的实现方法,如果我们有自己额外的需求,还是像以前一样在接口处声明,实现类对应实现即可,这个有一个注意点,就是我们自己在实体类里面定义的数据库表中没有的属性时,一定要加上@TableField(exist = false),否则否则mybatisPlus帮我们实现的crud方法就会报错

            19. 分页,这里的IPage就等价于之前的PageInfo(分页信息封装对象),里面有各种分页相关信息

                   @Override
                  public Page<Employee> queryPage(EmployeeQueryObject qo) {
                      Page<Employee> page = new Page<>(qo.getCurrentPage(),qo.getPageSize());
                      QueryWrapper<Employee> wrapper = new QueryWrapper<>();
                      // wrapper.like("name",qo.getKeyWord());
                      wrapper.like(StringUtils.hasText(qo.getKeyWord()),"name",qo.getKeyWord());
                      // return super.page(page,wrapper);
                      return super.page(page,wrapper);
                  }
              
                  @Test
                  public void testPage(){
                      EmployeeQueryObject qo = new EmployeeQueryObject();
                      qo.setPageSize(5);
                      qo.setCurrentPage(2);
                      IPage<Employee> page = employeeService.queryPage(qo);
                      System.out.println("当前页:" + page.getCurrent());
                      System.out.println("总页数:" + page.getPages());
                      System.out.println("每页显示条数:" + page.getSize());
                      System.out.println("总记录数:" + page.getTotal());
                      System.out.println("当前页显示记录:" + page.getRecords());
                  }
              
            20. 写web项目,导入freemarker后一定要做以下配置

                  #暴露session对象的属性
                  spring.freemarker.expose-session-attributes=true
                  #配置为传统模式,空值自动处理(如果不配置,那么页面很多地方显示不出来)
                  spring.freemarker.settings.classic_compatible=true
                  #重新指定模板文件后缀 springboot 2.2.x 后 默认后缀为 .ftlh
                  spring.freemarker.suffix=.ftl
              
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 一、MP 是什么 MP全称Mybatis-Plus,套用官方的解释便是成为 MyBatis 最好的搭档,简称基友。...
    陈二狗想吃肉阅读 1,490评论 0 4
  • 前言: mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过...
    久迷_138a阅读 486评论 0 1
  • 1. MyBatisPlus概述 为什么要学习它呢?MyBatisPlus可以节省我们大量工作时间,所有的CRUD...
    弹钢琴的崽崽阅读 1,939评论 0 3
  • MyBatis-Plus MyBatis-Plus是一个MyBatis的增强工具,在MyBatis的基础上只做增强...
    禹王穆阅读 656评论 0 0
  • 一、重点知识 git 监视的是文件内容的修改 $ git checkout -- abc.txt : 其实是用版本...
    一花一世界yu阅读 865评论 0 2