zoukankan      html  css  js  c++  java
  • 12、SpringBoot整合mybatis plus

    概述

    官网上对mybatis plus的简介:

    mybatis plus是一款mybtais的增强工具,在mybatis的基础上只做增强不做改变,为简化开发,提高效率而生。

    正如官方所说,在保证最小代码入侵的前提下,MP(mybatis plus)给mybatis使用者带来了更好的体验。

    官网文档:https://baomidou.com/guide/

    推荐视频:https://www.bilibili.com/video/BV1yA411t782?p=1

    如何去学习?

    建议结合视频和官网文档。

    最节省时间的方式:直接看文档,按照文档写demo,写完了快速过一遍视频(倍速、快进),毕竟有些技巧文档可能并没有说明。

    最全面的学习方式:完整的看一遍视频,看完每小节后,去官方文档查找对应内容,重点看视频没有提到的。

    现在着急用:直接点击跳转到本文的 完整测试代码 ,简单看一下代码,应该可以直接复制粘贴使用。

    依赖

    官网特别提醒,如果引入了mybatis plus的依赖,就不要引入mybatis以及mybatis-spring-starter之类的依赖了,防止发生版本冲突。

            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.4.0</version>
            </dependency>

    如果你想要使用自动生成功能,需要导入(模板引擎依赖根据个人需要导入)

            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
                <version>3.4.0</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
                <version>2.2</version>
            </dependency>

    注解

    加载实体类的注解比较多,大多用于java实体类成员变量与数据库字段之间的映射,相当于传统mybatis xml文件中的resultmap。

    实体类

    以User实体类为例,说明注解的作用。

    @Data
    // @TableName(value = "user")
    public class User {
        @TableId(type = IdType.AUTO)
        private Long id;
    
        @TableField(value = "name")
        private String name;
    
        private Integer age;
    
        @TableField(fill = FieldFill.INSERT)
        private Date createTime;
    
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date updateTime;
    
        @Version
        private Integer version;
    
        private StatusEnum status;
    
        @TableLogic
        private Integer deleted;
    
        private Integer departmentId;
    }

    @TableName

    是实体类名与数据库表名之间的映射。

    MP默认是按照类名来映射的,例如类名为User,表为user,那就不需要加这个注解,可以自动映射。

    如果类名与表名不一致,就必须用这个注解标注了。

    @TableId

    加载主键上

        @TableId(type = IdType.AUTO)
        private Long id;

    它有几种配置策略,可以按需取用

        #数据库自增
        AUTO(0), 
        
        #mybatis plus 设置主键,雪花算法
        NONE(1), 
        
        #开发者,手动复制
        INPUT(2),
        
        #mybatis分配id,可以使用Long、Integer、String
        ASSIGN_ID(3),
        
        #分配一个uuid,必须是String
        ASSIGN_UUID(4),

    @TableFiled

    加在普通成员变量上

    如果变量名与字段不一致,可以用value属性标注,可以实现变量和表字段的映射。

    而fill可以实现自动填充。

        @TableField(fill = FieldFill.INSERT)
        private Date createTime;
    
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date updateTime;

    例如项目中,可能每张表都有createTime和updateTime,如果每次插入数据,都手动输入可能有点繁琐。所以可以使用fill属性。

    FiledFill.INSERT表示在插入的时候填充。

    FiledFill.INSERT_UPDATE表示插入、删除的时候都需要填充。

    当然,我们还需要添加一个处理类

    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
        @Override
        public void insertFill(MetaObject metaObject) {
            this.setFieldValByName("createTime", new Date(), metaObject);
            this.setFieldValByName("updateTime", new Date(), metaObject);
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
            this.setFieldValByName("updateTime", new Date(), metaObject);
        }
    }

    @TableFiled还有其他的一些属性

        String value() default "";
    
        #该字段数据库中没有,不需要映射
        boolean exist() default true;
    
        String condition() default "";
    
        String update() default "";
    
        FieldStrategy insertStrategy() default FieldStrategy.DEFAULT;
    
        FieldStrategy updateStrategy() default FieldStrategy.DEFAULT;
    
        FieldStrategy whereStrategy() default FieldStrategy.DEFAULT;
    
        #自动填充
        FieldFill fill() default FieldFill.DEFAULT;
    
        #查询的时候,查询结果忽略该字段
        boolean select() default true;
    
        boolean keepGlobalFormat() default false;
    
        JdbcType jdbcType() default JdbcType.UNDEFINED;
    
        Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;
    
        boolean javaType() default false;
    
        String numericScale() default "";

    @Version

    乐观锁的实现方式(取自官网):当要更新一条记录的时候,希望这条记录没有被别人更新:

    • 取出记录时,获取当前version
    • 更新时,带上这个version
    • 执行更新时, set version = newVersion where version = oldVersion
    • 如果version不对,就更新失败

    mybatis plus中使用乐观锁,只需要添加一个注解@Version

        @Version
        private Integer version;

    并配置拦截器

    @Configuration
    public class MyBatisPlusConfig {
    
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁插件
            return interceptor;
        }
    
    }

    在执行更新操作的时候,会自动在sql后面加上 and version = xx

    枚举

    可以直接将java枚举类型映射为数据库字段

    1、建需要映射的java枚举类

    加注解

    public enum StatusEnum {
        WORK(0,"上班"),
        REST(1,"休息");
        StatusEnum(Integer code, String msg){
            this.code = code;
            this.msg = msg;
        }
    
        @EnumValue
        private Integer code;
        private String msg;
    }

    2、实体类直接使用

    名字也要映射,如果和数据库字段不一致,可以用@TableField指定

     private StatusEnum status;

    3、配置文件中加上

    mybatis-plus:
      type-enums-package: com.dayrain.nums

    逻辑删除

    什么时候用到逻辑删除?逻辑删除就是并非真正的删除某个数据,它依然存在数据库中,只是我在取用的时候,把他忽略。

    具体的做法就是加一个字段,例如deleted,默认值为1 。

    如果用户要删除这条数据,将它置为0。

    每一次查询的时候,查询条件加上 and deleted = 1,这条数据表面上就完成了删除,但实际上还在数据库中,所以称为“逻辑删除”。

    1、加注解

    @TableLogic
    private Integer deleted;

    2、配置文件

    logic-not-delete-value表示正常状况的值,

    logic-delete-value表示逻辑删除后的值,

    这些是可以根据实际情况自行调整

    mybatis-plus:
      global-config:
        db-config:
          logic-not-delete-value: 1
          logic-delete-value: 0

    实战:

        @Test
        public void delete() {
            userMapper.deleteById(1);
        }

    控制台输出

    ==>  Preparing: UPDATE user SET deleted=0 WHERE id=? AND deleted=1
    ==> Parameters: 1(Integer)
    <==    Updates: 0

    可以看出,删除实际执行的update,逻辑上的删除

    此时执行查询

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

    控制台输出

    发现查询的时候,会自动带上 deleted=1

    JDBC Connection [HikariProxyConnection@1188871851 wrapping com.mysql.cj.jdbc.ConnectionImpl@36e43829] will not be managed by Spring
    ==>  Preparing: SELECT id,name,age,create_time,update_time,version,status,deleted FROM user WHERE deleted=1
    ==> Parameters: 
    <==      Total: 0

     增删改查

    查询

    @SpringBootTest
    public class UserTest {
    
        @Autowired
        UserMapper userMapper;
    
        @Test
        public void selectTest() {
            //1、不加条件,查询所有
            // userMapper.selectList(null);
    
            //2、参数查
            // SELECT id,name,age,create_time,update_time,version,status,deleted FROM user WHERE deleted=1 AND (name = ?)
            // QueryWrapper<User> wrapper = new QueryWrapper();
            // wrapper.eq("name","小李");
            // List<User> users = userMapper.selectList(wrapper);
            // users.forEach(System.out::println);
    
            //3、多个参数
            // SELECT id,name,age,create_time,update_time,version,status,deleted FROM user WHERE deleted=1 AND (name = ? AND age = ?)
            // QueryWrapper<User> wrapper = new QueryWrapper();
            // Map<String, Object>map = new HashMap<>();
            // map.put("name", "小李");
            // map.put("age", 22);
            // wrapper.allEq(map);
            // List<User> users = userMapper.selectList(wrapper);
            //也可以直接用selectByMap直接传map
            // users.forEach(System.out::println);
    
            //4、比较,
            // QueryWrapper<User> wrapper = new QueryWrapper();
            //gt表示大于,同理
            //ge(大于等于),ne(不等于),lt(小于),le(小于等于)
            // wrapper.gt("age",22);
            // List<User> users = userMapper.selectList(wrapper);
            // users.forEach(System.out::println);
    
            //5、模糊查询
            //复习一下, mysql中
            //‘%小’,表示以’小‘结尾,‘小%’表示以小开头。‘%小%’表示中间有小
            // QueryWrapper<User> wrapper = new QueryWrapper();
            // wrapper.like("name","小");     ‘%小%’
            // wrapper.likeLeft("name","小");    ‘%小’
            // wrapper.likeRight("name","小");   ‘小%’
            // List<User> users = userMapper.selectList(wrapper);
            // users.forEach(System.out::println);
    
            //6、复合查询
            // SELECT id,name,age,create_time,update_time,version,status,deleted FROM user WHERE deleted=1 AND (id IN (select id from user where id < 10))
            // QueryWrapper<User> wrapper = new QueryWrapper();
            // wrapper.inSql("id", "select id from user where id < 10");
            // List<User> users = userMapper.selectList(wrapper);
            // users.forEach(System.out::println);
    
            //7、排序
            //desc asc
            //SELECT id,name,age,create_time,update_time,version,status,deleted FROM user WHERE deleted=1 ORDER BY age DESC
            // QueryWrapper<User> wrapper = new QueryWrapper();
            // wrapper.orderByDesc("age");
            // List<User> users = userMapper.selectList(wrapper);
            // users.forEach(System.out::println);
    
            //8、将结果封装成map对象
            // QueryWrapper<User> wrapper = new QueryWrapper();
            // wrapper.orderByDesc("age");
            // List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
            // maps.forEach(System.out::println);
    
    
            //9、分页
            // Page<User> page = new Page<>(1, 2);
            // Page<User> result = userMapper.selectPage(page, null);
            // System.out.println(result);
            // 结果
            // System.out.println(result.getRecords());
    
            /**
             * ==>  Preparing: SELECT COUNT(1) FROM user WHERE deleted = 1
             * ==> Parameters:
             * <==    Columns: COUNT(1)
             * <==        Row: 1
             * <==      Total: 1
             * ==>  Preparing: SELECT id,name,age,create_time,update_time,version,status,deleted FROM user WHERE deleted=1 LIMIT ?
             * ==> Parameters: 2(Long)
             * <==    Columns: id, name, age, create_time, update_time, version, status, deleted
             * <==        Row: 1, 小李, 1, 2020-11-03 17:03:18, 2020-11-03 17:31:46, 2, 1, 1
             * <==      Total: 1
             */
    
            //10、连表查询
            //自定mapper
            List<UserDetail> userDetails = userMapper.selectUserDetail(1L);
            userDetails.forEach(System.out::println);
    
        }
    
    }

    删除

    @SpringBootTest
    public class UserTest {
    
        @Autowired
        UserMapper userMapper;
    
        @Test
        public void delete() {
            //1、根据id删除
            // userMapper.deleteById(1);
    
            //2、根据id集合删除
            // userMapper.deleteBatchIds(Arrays.asList(1,2));
    
            //3、条件删除
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.eq("age", 14);
            userMapper.delete(wrapper);
        }
    
    }

    更新

        @Test
        void updateUser() {
            // User user = new User();
            // user.setAge(1);
            // user.setName("小李");
            // user.setId(1323545415801319427L);
            // userMapper.updateById(user);
            // System.out.println(userMapper.selectById(user.getId()));
    
            //条件更新,相当于updateSelective,如果为null,则不更新
            //把年龄为25岁的人,名字都改成小明。
            User user = new User();
            user.setName("小明");
            QueryWrapper queryWrapper = new QueryWrapper();
            queryWrapper.eq("age", 25);
            userMapper.update(user, queryWrapper);
        }

    添加

        @Test
        void saveUser() {
            User user = new User();
            user.setAge(22);
            user.setName("小李");
            userMapper.insert(user);
        }

    连表查询

    MP对mybatis本身的代码没有入侵,如果你喜欢,mybatis依然可以像以前那么用。

    当MP提供的内置sql不能满足需求时,比如连表查询等,我们可以自己写sql,实现较为复杂的功能。

    注解版

    例如现在有员工表、部门表,员工有部门id,需求是查出员工对应的部门名。

    做一个简单的连表查询即可,但问题是如何去接收结果?我们这里的做法是定义一个对象来映射结果集,如果字段名和我们的成员变量名不一致,sql中可以采用“别名”的方式。

    public interface UserMapper extends BaseMapper<User> {
    
        //原生注解
        @Select("select user.name name, d.name departmentName from department d , user where user.id  = #{id}")
        List<UserDetail>selectUserDetail(Long id);
    }

    UserDetail类

    @Data
    public class UserDetail {
    
        private String name;
    
        private String departmentName;
    }

    **其实这里的@Select注解就和mybatis没有关系了,是原生mybatis提供的。包括下面的xml也同样如此。

    xml版

    传统的xml也可以继续使用,支持动态sql,功能和用法没有任何改变。

    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.dayrain.mapper.UserMapper">
    
        <select id="selectAll" resultType="com.dayrain.entity.User">
            select * from user;
        </select>
    </mapper>

    为了让Springboot扫描到这个文件,需要在配置文件中加上路径

    默认的路径就是  classpath*:/mapper/**/*.xml 

    所以xml如果是放在类路径mapper文件夹下,也可以忽略不写。

      mapper-locations: classpath*:/mapper/**/*.xml

    代码自动生成

    启动类:

    public class Main {
        public static void main(String[] args) {
    
            //创建generator对象
            AutoGenerator autoGenerator = new AutoGenerator();
            //数据源
            DataSourceConfig dataSourceConfig = new DataSourceConfig();
            dataSourceConfig.setDbType(DbType.MYSQL);
            dataSourceConfig.setUrl("jdbc:mysql://ip:3306/test?useUnicode=true&characterEncoding=UTF-8");
            dataSourceConfig.setUsername("root");
            dataSourceConfig.setPassword("root");
            dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
            autoGenerator.setDataSource(dataSourceConfig);
    
            //全局设置
            GlobalConfig globalConfig = new GlobalConfig();
            globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
            globalConfig.setOpen(false);
            globalConfig.setAuthor("dayrain");
            //如果不设置,默认都是以I开头的service
            globalConfig.setServiceName("%sService");
            autoGenerator.setGlobalConfig(globalConfig);
    
            //包信息
    //路径根据个人需要配置
    //该配置运行结果:会在com.dayrain包下生成一个generator文件夹,里面有entity、service等包。
    PackageConfig packageConfig = new PackageConfig(); packageConfig.setParent("com.dayrain"); packageConfig.setModuleName("generator"); packageConfig.setController("controller"); packageConfig.setService("service"); packageConfig.setMapper("mapper"); autoGenerator.setPackageInfo(packageConfig); //配置策略 StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig.setEntityLombokModel(true); strategyConfig.setNaming(NamingStrategy.underline_to_camel); strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); autoGenerator.setStrategy(strategyConfig); autoGenerator.execute(); } }

    添加自动生成依赖

     <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
                <version>3.4.0</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
                <version>2.2</version>
            </dependency>

    完整测试代码

    目录结构

    config

    @Configuration
    public class MyBatisPlusConfig {
    
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁插件
            return interceptor;
        }
    
    }

    entity

    用户

    @Data
    // @TableName(value = "user")
    public class User {
        @TableId(type = IdType.AUTO)
        private Long id;
    
        @TableField(value = "name")
        private String name;
    
        private Integer age;
    
        @TableField(fill = FieldFill.INSERT)
        private Date createTime;
    
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date updateTime;
    
        @Version
        private Integer version;
    
        private StatusEnum status;
    
        @TableLogic
        private Integer deleted;
    
        private Integer departmentId;
    }

    部门

    @Data
    public class Department {
        @TableId(type = IdType.AUTO)
        private Integer id;
    
        private String name;
    }

    用户详细信息

    @Data
    public class UserDetail {
    
        private String name;
    
        private String departmentName;
    }

    handler

    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
        @Override
        public void insertFill(MetaObject metaObject) {
            this.setFieldValByName("createTime", new Date(), metaObject);
            this.setFieldValByName("updateTime", new Date(), metaObject);
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
            this.setFieldValByName("updateTime", new Date(), metaObject);
        }
    }

    mapper

    public interface UserMapper extends BaseMapper<User> {
    
        //原生注解
        @Select("select user.name name, d.name departmentName from department d , user where user.id  = #{id}")
        List<UserDetail>selectUserDetail(Long id);
    
        List<User>selectAll();
    }

    enums

    public enum StatusEnum {
        WORK(0,"上班"),
        REST(1,"休息");
        StatusEnum(Integer code, String msg){
            this.code = code;
            this.msg = msg;
        }
    
        @EnumValue
        private Integer code;
        private String msg;
    }

    启动类

    @SpringBootApplication
    @MapperScan("com.dayrain.mapper")
    public class MybatisLearnApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MybatisLearnApplication.class, args);
        }
    
    }

    配置文件

    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://ip:3306/test?useUnicode=true&characterEncoding=UTF-8
        username: root
        password: root
    
    
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      type-enums-package: com.dayrain.nums
      global-config:
        db-config:
          logic-not-delete-value: 1
          logic-delete-value: 0
    
      mapper-locations: classpath*:/mapper/**/*.xml

    xml

    位于 resouces/mapper下

    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.dayrain.mapper.UserMapper">
        <select id="selectAll" resultType="com.dayrain.entity.User">
            select * from user;
        </select>
    </mapper>

     sql

    DROP TABLE IF EXISTS `user`;
    CREATE TABLE `user`  (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
      `age` int(11) NULL DEFAULT NULL,
      `create_time` datetime(0) NULL DEFAULT NULL,
      `update_time` datetime(0) NULL DEFAULT NULL,
      `version` int(11) NULL DEFAULT 1,
      `status` tinyint(4) NULL DEFAULT NULL,
      `deleted` tinyint(4) NULL DEFAULT 1,
      `department_id` int(11) NULL DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 1323545415801319428 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
    
    SET FOREIGN_KEY_CHECKS = 1;
    DROP TABLE IF EXISTS `department`;
    CREATE TABLE `department`  (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

     

    如有错误,恳请批评指正!

  • 相关阅读:
    《vi和vim》 学习手记(1)
    2013年1月第1个周末
    Oracle基础知识Oracle不同的启动关闭方式
    2013年1月第一个周末
    《vi和vim》 学习手记(2)
    Oracle基础知识数据迁移
    MySQL 显示表字段及注释等信息
    什么是UCenter Home、Discuz!、SupeSite、ECShop和SupeV 这些都是什么?
    mysql远程连接对用户的测试(吴龙波)
    MySQL与SQL的触发器的不同写法
  • 原文地址:https://www.cnblogs.com/phdeblog/p/13925092.html
Copyright © 2011-2022 走看看