«

java Springboot集成mybatis-plus的方法是什么

时间:2024-5-3 17:48     作者:韩俊     分类: Java


今天小编给大家分享一下java Springboot集成mybatis-plus的方法是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

    一、Mybatis-Plus介绍

    Mybatis-plus是Mybatis的增强工具包,其官网的介绍如下:

      润物细无声:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。

      效率至上:只需简单配置,即可快速进行单表CRUD操作,从而节省大量时间。

      丰富功能:代码生成、自动分页、逻辑删除、自动填充等功能一应俱全。

    其优点如下:

      无侵入:Mybatis-Plus 在 Mybatis 的基础上进行扩展,只做增强不做改变,引入 Mybatis-Plus 不会对您现有的 Mybatis 构架产生任何影响,而且 MP 支持所有 Mybatis 原生的特性

      依赖少:仅仅依赖 Mybatis 以及 Mybatis-Spring

      损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作

      通用CRUD操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求

      多种主键策略:支持多达4种主键策略(内含分布式唯一ID生成器),可自由配置,完美解决主键问题

      支持ActiveRecord:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可实现基本 CRUD 操作

      支持代码生成:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用(P.S. 比 Mybatis 官方的 Generator 更加强大!)

      支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )

      内置分页插件:基于Mybatis物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于写基本List查询

      内置性能分析插件:可输出Sql语句以及其执行时间,建议开发测试时启用该功能,能有效解决慢查询

      内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,预防误操作

    二、Spring boot 整合Mybatis-plus

    2.1 pom中引入Mybatis-plus依赖

    <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.1</version>
    </dependency>

    注:如果是gradle,引入的方式如下:

    implementation group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: '3.5.3.1'

    2.2 创建一张User表

    创建对应的数据表 Schema 的表结构和表数据:

    SET FOREIGN_KEY_CHECKS=0;
    
    -- ----------------------------
    -- Table structure for `user_base_info`
    -- ----------------------------
    DROP TABLE IF EXISTS `user_base_info`;
    CREATE TABLE `user_base_info` (
      `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
      `u_id` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户ID',
      `name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名',
      `cn_name` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '中文名',
      `sex` char(3) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '性别',
      `alias` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '别名',
      `web_chat` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '微信号',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

    表中插入数据

    -- ----------------------------
    -- Records of user_base_info
    -- ----------------------------
    INSERT INTO `user_base_info` VALUES ('1', '001', 'holmium', '钬', '1', '虎啸山村', 'holmium');
    INSERT INTO `user_base_info` VALUES ('2', '001', 'test', '测试', '0', '美女', '虎啸山村');

    2.3 Mybatis-plus配置

    #mybatis-plus
    mybatis-plus:
      ## 这个可以不用配置,因其默认就是这个路径
      mapper-locations: classpath:/mapper/*Mapper.xml
      #实体扫描,多个package用逗号或者分号分隔
      typeAliasesPackage: com.holmium.springboot.repository.*.entity
      global-config:
        # 数据库相关配置
        db-config:
          #主键类型  AUTO:"数据库ID自增", INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
          id-type: AUTO
          #字段策略 IGNORED:"忽略判断",NOT_NULL:"非 NULL 判断"),NOT_EMPTY:"非空判断"
          field-strategy: not_empty
          #驼峰下划线转换
          column-underline: true
          #数据库大写下划线转换
          #capital-mode: true
          #逻辑删除配置
          logic-delete-value: 0
          logic-not-delete-value: 1
          db-type: h3
        #刷新mapper 调试神器
        refresh: true
      # 原生配置
      configuration:
        map-underscore-to-camel-case: true
        cache-enabled: false

    2.4 创建一个实体

    //BasePo,后续可以继续扩展
    @Data
    public class BasePo {
        @TableId(value = "id", type = IdType.AUTO)
        private Integer id;
    }
    
    package com.holmium.springboot.infra.user.entity;
    
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Getter;
    import lombok.Setter;
    
    /**
     * @author holmium
     * @description:
     * @date 2023年04月20日16:19
     */
    @TableName(value = "user_base_info", autoResultMap = true)
    @Getter
    @Setter
    public class UserPo extends BasePo {
        /**
         * 用户ID
         */
        @TableField(value = "u_id")
        String uId;
        /**
         * 用户名
         */
        @TableField(value = "name")
        String name;
        /**
         * 中文名称
         */
        @TableField(value = "cn_name")
        String cnName;
        /**
         * 性别:1-男 0-女
         */
        @TableField(value = "sex")
        String sex;
        /**
         * 别名
         */
        @TableField(value = "alias")
        String alias;
        /**
         * 微信号
         */
        @TableField(value = "web_chat")
        String webChat;
    }

    2.5 创建一个Mapper接口

    package com.holmium.springboot.infra.user.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.holmium.springboot.infra.user.entity.UserPo;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface UserMapper extends BaseMapper<UserPo> {
    }

    2.6 修改服务接口

    //如果不理解,可以看之前的文章
    @RestController
    @RequestMapping("/user/center")
    public class UserApi {
        @Resource
        UserAppImpl userApp;
      
        @GetMapping(value = "/userinfo")
        public UserVo gerUserInfo(@RequestParam("id") String id) throws Exception {
            return userApp.getUserInfoByUserId(id);
        }
    }

    2.7 在启动类中添加 

    @MapperScan
     注解,扫描 Mapper 文件夹

    @SpringBootApplication
    @MapperScan("com.holmium.springboot.infra.*.mapper") //添加mapper扫描
    public class HolmiumApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(HolmiumApplication.class, args);
        }
    
    }

    2.8 测试

    2.9 小结

    通过以上的步骤,我们实现了对User表的查询功能,可以看到对于简单的CRUD操作,

    Mybatis-Plus
    只需要定义一个
    Mapper
    接口即可实现,真正做到如他所说的那样,简单配置、效率至上。

    三、Mybatis-plus 部分字段注解和查询

    3.1 主键

    3.1.1 主键生成策略

    主键生成策略一共提供的五种:

    AUTO(0),递增策略,如果使用该策略必须要求数据表的列也是递增。
    NONE(1),没有策略,必须人为的输入id值
    INPUT(2),没有策略,必须人为的输入id值
    ASSIGN_ID(3), 随机生成一个Long类型的值。该值一定是唯一。而且每次生成都不会相同。算法:雪花算法。 适合分布式主键。
    ASSIGN_UUID(4); 随机产生一个String类型的值。该值也是唯一的。

    3.1.2 使用方式

    通过配置文件中,配置

    id-type
    属性:

    mybatis-plus:
      global-config:
       # 数据库相关配置
        db-config:
          #主键类型  AUTO:"数据库ID自增", INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
          id-type: AUTO

    也可在对象属性上进行注解设置:

    @Data
    public class BasePo {
        //配置为数据库ID自增
        @TableId(value = "id", type = IdType.AUTO)
        private Integer id;
    }

    3.2 删除

    实际开发过程中,对数据都不会直接删除,都会采用逻辑删除的方式。所谓的逻辑删除,只是将数据置为一个状态,这个状态代表数据为删除状态,一般自己程序中写的话,就设置一个状态字段,给字段赋值为

    0
    D
    代表删除。而Mybatis-plus提供了
    @TableLogic
    注解,实现逻辑删除。 只对自动注入的 sql 起效:

      插入: 不作限制

      查找: 追加 where 条件过滤掉已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段

      更新: 追加 where 条件防止更新到已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段

      删除: 转变为 更新

    @Data
    public class BasePo {
        /**
         * 主键
         */
        @TableId(value = "id", type = IdType.AUTO)
        private Integer id;
    
        /**
         * 是否删除:0表示未删除 1表示删除.
         */
        @TableLogic
        private Boolean deleted;
    }

    3.3 自动填充

    实际开发中,我们会在表中记录数据创建时间、创建人、修改时间、修改人几个字段,但是几个字段如果我们每次都要进行赋值,代码比较冗余,

    Mybatis-plus
    提供的自动填充功能。

    3.3.1 增加公共属性对象:

    @Data
    public class BasePo {
        /**
         * 主键
         */
        @TableId(value = "id", type = IdType.AUTO)
        private Integer id;
    
        /**
         * 创建时间
         */
        @TableField(fill = FieldFill.INSERT)
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private Date createTime;
        /**
         * 最后更新时间
         */
        @TableField(fill = FieldFill.INSERT_UPDATE)
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private Date updateTime;
        /**
         * 创建者,默认系统User 的 id 编号
         * <p>
         * 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。
         */
        @TableField(fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)
        private String creator;
        /**
         * 更新者,默认系统User 的 id 编号
         * <p>
         * 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。
         */
        @TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.VARCHAR)
        private String updater;
    
        /**
         * 是否删除
         */
        @TableLogic
        private Boolean deleted;
    }

    3.3.2 自定义实现类 MyMetaObjectHandler

    public class DefaultDbFieldHandler implements MetaObjectHandler {
    
        @Override
        public void insertFill(MetaObject metaObject) {
            if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity) {
                BaseEntity baseDO = (BaseEntity) metaObject.getOriginalObject();
        
                Date current = new Date();
                // 创建时间为空,则以当前时间为插入时间
                if (Objects.isNull(baseDO.getCreateTime())) {
                    baseDO.setCreateTime(current);
                }
                // 更新时间为空,则以当前时间为更新时间
                if (Objects.isNull(baseDO.getUpdateTime())) {
                    baseDO.setUpdateTime(current);
                }
        
                Long userId = WebUtils.getLoginUserId();
                // 当前登录用户不为空,创建人为空,则当前登录用户为创建人
                if (Objects.nonNull(userId) && Objects.isNull(baseDO.getCreator())) {
                    baseDO.setCreator(userId.toString());
                }
                // 当前登录用户不为空,更新人为空,则当前登录用户为更新人
                if (Objects.nonNull(userId) && Objects.isNull(baseDO.getUpdater())) {
                    baseDO.setUpdater(userId.toString());
                }
            }
        }
        
        @Override
        public void updateFill(MetaObject metaObject) {
            // 更新时间为空,则以当前时间为更新时间
            Object modifyTime = getFieldValByName("updateTime", metaObject);
            if (Objects.isNull(modifyTime)) {
                setFieldValByName("updateTime", new Date(), metaObject);
            }
        
            // 当前登录用户不为空,更新人为空,则当前登录用户为更新人
            Object modifier = getFieldValByName("updater", metaObject);
            Long userId = WebUtils.getLoginUserId();
            if (Objects.nonNull(userId) && Objects.isNull(modifier)) {
                setFieldValByName("updater", userId.toString(), metaObject);
            }
        }
    }

    3.4 条件查询

    3.4.1 根据各种条件查询

    Wrapper:封装了关于查询的各种条件方法。

    有三个子类最常用:

    LambdaQueryWrapper
    查询条件
    LambdaUpdateWrapper
    修改条件
    LambdaQueryWrapper
    查询使用lambda表达式条件

      LambdaQueryWrapper

    selectPage(userDo, new LambdaQueryWrapperX<UserPo>()
                    .likeIfPresent(UserPo::getName, userDo.getName())
                    .likeIfPresent(UserPo::getSex, userDo.getSex())
                    .betweenIfPresent(UserPo::getCreateTime, userDo.getCreateTime())
                    .orderByDesc(UserPo::getId))

      LambdaUpdateWrapper

    update(update, new LambdaUpdateWrapper<UserPo>()
                   .eq(UserPo::getId, id).eq(UserPo::getName, name))

      LambdaQueryWrapper

    new LambdaQueryWrapper<UserDo>()
                    .eq(UserPo::getId, id)
                    .eq(UserPo::getName, name);

    3.5 分页查询

      添加分页拦截器

       @Bean
       public MybatisPlusInterceptor mybatisPlusInterceptor() {
           MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
           // 分页插件
           mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
           return mybatisPlusInterceptor;
       }

      分页查询

    Page<UserPo> page = userMapper.selectPage(page, wrapper);//把查询的结果自动封装到Page对象中

    3.6 小结

    到此简单的查询基本已介绍完毕,可满足他80%的日常开发需求。

    四、引入对象转换组件mapstruct

    从上述代码中可以看到,我们从数据库查询出来的结果是转换为一个想

    XXXPo
    的对象,而接口返回的是
    XXXVo
    对象。在实际项目中,一般一个project都会分很多层,每层都会定义自己的对象,如:PO、VO、DAO、BO、DTO、POJO等等。

      DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。

      DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。

      BO(Business Object):业务对象,由 Service 层输出的封装业务逻辑的对象。

      AO(ApplicationObject):应用对象,在Web层与Service层之间抽象的复用对象模型, 极为贴近展示层,复用度不高。

      VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。

      POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO。

    这些对象在传递过程中,需要相互转换,如果手工书写,往往会通过

    getxxx
    ,再
    setxxx
    ,操作起来非常繁琐,那有没有一种方式比较简单实现能?下面我们介绍一个对象转换的组件
    mapstruct

    4.1 pom中引入依赖包

    <!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct -->
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>1.5.4.Final</version>
    </dependency>

    4.2 创建一个Convert接口

    package com.holmium.springboot.app.user.convert;
    
    import com.holmium.springboot.api.user.vo.UserVo;
    import com.holmium.springboot.common.user.app.dto.UserDto;
    import org.mapstruct.Mapper; 
    import org.mapstruct.factory.Mappers;
    
    /**
     * @author holmium
     * @date 2023年04月16日 20:24
     */
    @Mapper
    public interface UserAppConvert {
        UserAppConvert INSTANCES = Mappers.getMapper(UserAppConvert.class);
        UserVo voToDto(UserDto userDto);
    }

    避坑

    这里的

    @Mapper
    注解和上述
    Mybatis-plus
    中的
    @Mapper
    注解相同,很容易引入错误,导致会报错。

    这里的

    Mapper
    import org.mapstruct.Mapper;

    mybatis-plus
    中的
    @Mapper
    ,
    import org.apache.ibatis.annotations.Mapper;

    一定要注意看清楚引入的类

    4.3 在类中引用

    @Service
    public class UserAppImpl implements UserApp {
        @Resource
        public UserDomain userDomain;
        @Override
        public UserVo getUserInfoByUserId(String userId) throws Exception {
            //这样就实现了对象转换
            return UserAppConvert.INSTANCES.voToDto(userDomain.getUserInfoByUid(userId));
        }
    }

    4.4 对象属性不同怎么转换

    上述是两个对象中属性相同话,可以不用做其他任何操作,就可以事项对象互转,但实际在开发中,两个对象属性不可能完全一样。而上述操作只能将相同属性做转换,不同的属性没法互相转换,导致对象有部分数据丢失?那怎么解决,可在转换方法上加入

    @Mappings
    注解,如下:

    @Mapper
    public interface UserAppConvert {
    
        UserAppConvert INSTANCES = Mappers.getMapper(UserAppConvert.class);
        @Mappings({
                //属性不一致转换
                @Mapping(source = "name", target = "userName"),
                //类型不一致转换
                @Mapping(target = "createTime", expression = "java(com.java.mmzsblog.util.DateTransform.strToDate(source.getCreateTime()))"),
        })
        UserVo voToDto(UserDto userDto);
    }

    这样就解决了不同对象属性和属性类型不一致转换问题。

    标签: java spring

    热门推荐