mybatis-plus

mybatis-plus开发学习

1 mybatis-plus开发文档

https://baomidou.com/guides/wrapper/#like

2 mybatis-plus配置

2.1 导pom

1.只用mybais-plus实现单表的增删改查,只要在pom种引入mybatis-plus-boot-starter起步依赖就行。

2.如果要自动生成代码

3.使用mybatis-plus插件图形化生成。不过要引入swagger的依赖。

2.2 yml配置

mybatis-plus:
  # xml
  mapper-locations: classpath:mapper/*Mapper.xml
  # 实体扫描,多个package用逗号或者分号分隔
  type-aliases-package: com.jiagoushi.mybatisplusexample.entity
  # 扫描枚举类 # 支持统配符 * 或者 ; 分割
  type-enums-package: com.jiagoushi.mybatisplusexample.entity.enums
  global-config:
    db-config:
      #数据库大写下划线转换,避免字段是大写,实体类是小写的不匹配问题
      capital-mode: true
      #逻辑删除配置
      # logic-delete-field: delete_status # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑删除
      logic-not-delete-value: 0 # 正常
  configuration:
  ###自动下划线转驼峰,默认开启,此处显式配置了一下
    map-underscore-to-camel-case: true
    cache-enabled: false
    default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler

#################################### logic-delete-value: 1 # 逻辑删除
logic-not-delete-value: 0 # 正常

这两个配置牵扯到逻辑删除和物理删除的问题,

  • 物理删除就是我使用delete语句实现的删除。
  • 逻辑删除是删除的时候使用update语句修改del_flag删除标记的值,只是逻辑上删除了,但是数据库里面还是有的。
  • mybatis-plus配置完之后,执行相应的查询和修改语句时候,mybatis-plus自动给我添加标志位的字段。

逻辑标志位要加这个@TableLogic注解

image-20250413145127816

######################################

3 mp的使用学习

3.1 雪花算法

image-20250413144825625

实体类里面用@TableId里面有一个属性是type,用mp的枚举类型利用雪花算法来生成主键,不用在数据库里面手动设置自增了。

3.2 乐观锁

3.2.1 学习乐观锁之前,复习一下并发的概念

并发是怎么回事?

  • 单核 CPU:多个任务通过时间片轮转交替执行,看起来像是同时进行,但实际上是在不同时刻执行的。
  • 多核 CPU:多个任务可以真正在同一时刻执行。

并发控制又是怎么回事?

image-20250413150127807

3.2.2 乐观锁配置

1.建插件配置类

@Configuration
@MapperScan("com.jiagoushi.mybatisplusexample.mapper")
public class MyBatisPlusConfiguration {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        /*MybatisPlusInterceptor是mp内置核心拦截器,用于内部的插件管理*/
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        /*在这个插件管理对象中分别添加分页插件和乐观锁插件*/
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 分页插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁插件
        return interceptor;
    }

}

2.在实体类字段version上加上@Version

乐观锁生效是,每修改一次数据,version自动加一,但是修改方法在service层执行前要先调一次查询,获得version,然后再修改。

补一下我的困惑

image-20250413160114978

image-20250413160141528

image-20250413170317068

image-20250413170522719

3.3 为什么继承的ServiceImpl泛型第一个参数必须是自定义的接口

  • Spring 的依赖注入是基于 Bean 的类型 而非泛型参数。如果存在多个 BaseMapper 的实例(如 BaseMapper<User>BaseMapper<Order>),Spring 无法区分它们,导致注入冲突。

不过在mapper层继承basemapper的基础下,service层继承serviceimpl可以不写参数类型。spring和mp会自动根据自定义的mapper来进行映射,前提是继承basemapper,但是如果是多个service都继承但是不写泛型,会导致每个service都可以参与到其他实体类的crud中,直接混乱了。

  • 使用serviceImpl一定要有mapper层接口继承basemapper,因为service调用的默认的crud是basemapper的实现类去执行的,而mp无法生成泛型接口的动态实现类,机制限制:MyBatis-Plus 内部机制的限制:MyBatis-Plus 没有直接对泛型接口生成实现类的机制,因为动态代理无法处理泛型接口
  • 继承类basemapper一定要在serviceImpl里面写泛型参数,否则mp不知道service控制的哪个实体类。

3.4 mybatis和mybatis-plus都怎样识别表名

mybatis通过xml中的sql语句的开发者定义的表名映射。

mybatis-plus

  • 如果表名和实体类名一样,则不用配置自动生成的sql语句以user为表名
  • 如果表名有前缀,就要配置了,自己无法发现,一种是使用@TableName注解,一种是yml里面全局配置前缀。不过你要不用他简单的,自己写xml不用配置也行。

4 springboot整合mybatisplus开发笔记

配合springboot_rbac和springboot_mybatisplus连个demo学习

1.导pom
   <!-- mybatis-plus起步依赖插件 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>
        <!--mp自动生成代码,api文档注释需要swagger注解需要的依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger2.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger2.version}</version>
        </dependency>


2.配置yml文件(#格线是mp的配置)

# tomcat服务器端口配置
server:
  port: 8080
  servlet:
    context-path: /rbac


# spring数据源配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/rbac?serverTimezone=Asia/Shanghai&useTimezone=true/&characterEncoding=utf8
    username: root
    password: Yin2040411
    max-wait: 10000
    initial-size: 5


# 配置jackson的日期时间格式
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

#####################################################################################################
# mybatis-plus框架配置
mybatis-plus:
# 配置映射文件和别名
  mapper-locations: classpath*:/com/yzh/springboot_rbac/mapper/*.xml
  type-aliases-package: com.yzh.springboot_rbac.entity
# mp逻辑删除配置:
  global-config:
    db-config:
      logic-delete-field: del_flag
      logic-delete-value: 0 #逻辑删除
      logic-not-delete-value: 1 #正常
#  数据库字段大写转小写配置
      capital-mode: true
  configuration:
#  下划线自动转托驼峰配置(不配置也是开启的,这里显式配置一下啦)
    map-underscore-to-camel-case: true
#   让sql语句日志打印到控制台配置
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl


#  控制台打印日志的详细程度配置
# trace:最详细日志信息
logging:
  level:
    root: info  # 默认就是info
    com.yzh.springboot_rbac.service.impl: trace
    com.yzh.springboot_rbac.controller: trace


#日志生成路径配置
logback:
  logDir: log/dev
  appName: springboot_rbac
  fileType: log
#######################################################################################


logback-spring.xml要注意一下该环境否则启动后一直卡在那里
logback-spring.xml里面这地方复制过来要改一下环境默认的default,这里需要改为default环境,
要不然项目一直卡在哪里启动不了,因为我没有配置dev的yml文件,所以只能使用default的文件。
<!--开发环境:打印控制台-->
    <!--这里需要改为default环境,要不然项目一直卡在哪里启动不了,因为我没有配置dev的yml文件,所以只能使用
    default的文件-->
    <springProfile name="default">
        <root level="info">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="DEBUG_FILE"/>
            <appender-ref ref="INFO_FILE"/>
            <appender-ref ref="WARN_FILE"/>
            <appender-ref ref="ERROR_FILE"/>
        </root>
    </springProfile>


3.
在springboot_mybatisplus项目中练习mp提供的方法记录

###################################################
如果想要在测试类中链式调用set方法设置对象的值,一定要把实体类里面的对应
set方法返回类型改为实体类形,retrun this
    public User setName(String name) {
        this.name = name;
        return this;
    }
    public User setAge(Integer age) {
        this.age = age;
        return this;
    }
    public User setBir(Date bir) {
        this.bir = bir;
        return this;
    }

        public void testInsert() {
            User user = new User();
            user.setAge(20).setName("张三").setBir(new Date());
            userMapper.insert(user);
        }
#############################################################

4.
springboot中自定义测试类实现测试两种方法
- 1.测试类要加@SpringBootTest这个注解,springboot项目测试类要加这个注解,
    否则无法测试,因为springboot项目需要依赖springboot来管理bean,否则usermapper无法注入。
- 2.自定义测试类继承默认生成的测试类,因为默认生成的测试类里面使用了这个注解

5.
雪花算法的:ASSIGN_ID实体类使用Long/String,数据库字段使用bigInt/varchar
          ASSIGN_UUID实体类使用String,数据库字段使用Varchar
          按照上述即可,实体类Integer和int数值范围对应数据库int范围太小,报错。
ASSIGN_ID设置后,mp自动生成1911670271488589825并插入到数据库中,数据库不用设置自增
ASSIGN_UUID:d7d3d88d136905d4362bcb7e1cef04cf,数据库不用设置自增
AUTO:需要依赖数据库设置自增才可以添加id,否则报错。

6.lambda表达式和引用方式如下:效果一样
   List<User> users = userMapper.selectList(null);
   users.forEach(System.out::println);
   users.forEach(user -> System.out.println(user));


7.常用的方法与条件构造器

@SpringBootTest
public class testBaseController {
    /*此测试对自定义mapper继承BaseController测试*/

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelectAllUser() {
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
        users.forEach(user -> System.out.println(user));
    }

    @Test
    public void testSelectByCondition() {
        /*查询常用的有两个:
         * selectById:根据id查,参数是id的值
         * selectList:查询所有用户,null空表示无条件所有用户*/
//        User user = userMapper.selectById(1);
//        System.out.println(user);

        /*基于条件的查询*/
//        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//        queryWrapper.like("name", "李");
//        List<User> list = userMapper.selectList(queryWrapper);
//        list.forEach(user -> System.out.println(user));

        /*lambda的查询*/
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.like(true, User::getName, "多");
        List<User> list = userMapper.selectList(lambdaQueryWrapper);
        list.forEach(user -> System.out.println(user));

    }

    @Test
    public void testInsert() {
        User user = new User();
        user.setAge(20).setName("李四").setBir(new Date());
        userMapper.insert(user);
        /*insert(T entity):插入方法只有一个,参数为实体类*/
    }

    @Test
    public void testdelete() {
        /*删除常用的有两个:
         * deleteById:genjuid删除,参数是id的值
         * delete:根据条件删除,参数是条件构造器*/
        /*普通删除*/
//        userMapper.deleteById(2);

        /*基于条件修改
         *   querywrapper形式*/
//        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//        queryWrapper.eq(true, "id", 3);
//        userMapper.delete(queryWrapper);

        /*lambdaqueryWrapper形式*/
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(true, User::getId, 4);
        userMapper.delete(lambdaQueryWrapper);
    }

    @Test
    public void testUpdate1() {
        /*updateById():参数是实体对象,基于id修改:先查询,把查询的对象赋值给user,然后修改*/
        User user = userMapper.selectById(1);
        user.setAge(10).setName("伍六七");
        userMapper.updateById(user);
    }

    @Test
    public void testUpdate2() {
        /*基于条件修改:
            update()参数是修改后的实体类和条件构造器里面的条件
        *   QueryWrapper相当于sql中的where表达式eq相当于where age = 10*/
        User user = userMapper.selectById("1");
        user.setName("多阔霍");
//        QueryWrapper<User> updateWrapper = new QueryWrapper<>();
//        updateWrapper.eq("age", 10);
//        userMapper.update(user, updateWrapper);

        /*写一个lambdaWrapper,这个是使用lambda的方法引用实现正向映射数据库字段的值
         *   注意,这里的方法引用不是lambda里面的获取的值,而是只获取方法名,即属性名
         *        方法的引用根据应用上下文来执行,lambda中方法引用会输出一个值,但是mp中输出列名*/
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(true, User::getAge, 10);
        userMapper.update(user, lambdaQueryWrapper);

    }


}


8.
注意:
- Iservice使用的话要和BaseMapper一起使用,所以一定要自定义一个具体的接口继承BaseMapper,
如果直接施一公Iservice,spring无法注入泛型接口BaseMapper,必须指定具体实例接口

- 但是可以不适用Iservice,只用BaseMapper的crud即可,Iservice提供了更复杂的方法,而且不用自己写方法在service层。
posted @ 2025-04-28 20:59  JSESSIONID  阅读(30)  评论(0)    收藏  举报