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;

 

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

posted @ 2020-11-04 14:06  小楼夜听雨QAQ  阅读(490)  评论(0编辑  收藏  举报