mybatisPlus(入门)

MybatisPlus

(本文档只会写入一些个人认为需要记录的内容,不会记录所有内容)

1、mybatis实现简单CRUD

导入依赖

<!--springboot父工程-->

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5+ 版本</version>
    <relativePath/>
</parent>


<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>最新版本</version>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

配置yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/student?serverTimezone=GMT%2b8
    username: root
    password: 123456
server:
  port: 8080
 //日志实现
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

需要在springboot启动类中添加@MapperScan(Mapper包路径)

注:(此注解也可写在配置类中)

@SpringBootApplication
@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

Mapper继承BaseMapper

@Repository
public interface UserMapper extends BaseMapper<User> {	//泛型类型为实体类类型
}

测试

@SpringBootTest
class Demo01ApplicationTests {
    @Autowired
    private UserMapper userMapper;
    
    @Test
    void testSelect() {
        //由于继承了父类BaseMapper,所有方法都来自父类
        List<User> users = userMapper.selectList(null);
        for (User u: users) {
            System.out.println(u);
        }
    }
    
    @Test
    void testInsert(){
        User user = new User();
        user.setName("wyz");
        user.setAge(19);
        user.setEmail("456@qq.com");
        userMapper.insert(user);	//返回的是受影响行数
        System.out.println(user);
    }

    @Test
    void testUpdate(){
        User user = new User();
        user.setId(6L);
        user.setName("wang");
        user.setEmail("789@.com");
        userMapper.updateById(user);	//虽然名字为updateById但实际上传入的参数是对象
        System.out.println(user);		//注意一点,这里的更新操作中所有要更新的字段都是动态配置的(动态sql),
       									// 你传入哪些,就修改哪些
    }
    
     @Test
    void testDelete(){
        User user = userMapper.selectById(1653234646309052418L);
        userMapper.deleteById(user);

    }

}

其他CRUD

查看官方文档https://www.baomidou.com/pages/49cc81/#mapper-crud-%E6%8E%A5%E5%8F%A3

2、CRUD扩展

看下面的代码:

 @Test
    void testInsert(){
        User user = new User();
        user.setName("wyz");
        user.setAge(19);
        user.setEmail("456@qq.com");
        userMapper.insert(user);
        System.out.println(user);
    }

测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PU9TwXsG-1683298115355)(C:\Users\31628\AppData\Roaming\Typora\typora-user-images\image-20230503132516717.png)]

思考问题:我们明明没有设置插入id。数据库也没有设置默认值,为什么id还是被插入了?

①主键策略

解答上述问题:因为mybatis存在默认主键策略为雪花算法。https://blog.youkuaiyun.com/jiaomubai/article/details/124385324

该算法为分布式id生成策略中的一种,其他方案可以看参考这篇博客了解:https://www.cnblogs.com/haoxinyue/p/5208136.html

mybatis集成了很多主键策略,使用@TableId注解

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    //要使用非自增生成id策略的话,数据库的自增必须关闭,否则不会生效
    @TableId
    private Long id;
    private String name;
    private int age;
    private String email;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime insertTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @Version
    private int version;
}
@TableId

@TableIdz有两个属性:value和type

注解源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.mybatisplus.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableId {
    String value() default "";

    IdType type() default IdType.NONE;
}

**value用于解决数据库Id和实体类Id字段名称不一致的问题:**如果数据库中的主键字段和实体类中的主键属性字段名不一样且不是驼峰之类的对应关系,就可以在value属性中设置数据库中字段名。

若数据库中主键字段名为student_id,实体类中字段为id:

@Data
public class Student{
    @Table(value = "student_id")
    private int id
}

type用于设置主键策略,默认为IdType.NONE(雪花算法)。

type源码如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.mybatisplus.annotation;

public enum IdType {
    AUTO(0),
    NONE(1),
    INPUT(2),
    ASSIGN_ID(3),
    ASSIGN_UUID(4);

    private final int key;

    private IdType(int key) {
        this.key = key;
    }

    public int getKey() {
        return this.key;
    }
}


描述
AUTO数据库自增
NONE无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
INPUT手动赋值,如果不赋值,则Id为null
ASSIGN_ID分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
ASSIGN_UUIDUUID ,String,使用时数据库字段类型需为varchar,且不能勾选字段自增

②自动填充(插入与更新时使用)

在插入和更新时,我们一般需要带有插入时间和更新时间等(甚至还有插入者,更新者等)。

实现该功能方法是有两种(省略创建字段步骤):

方式一、数据库级别(不推荐)

设置数据库中的字段默认值为CURRENT_TIMESTAMP

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ml6DnAQ4-1683298115357)(C:\Users\31628\AppData\Roaming\Typora\typora-user-images\image-20230503140153817.png)]

方式二、代码级别
老方法

每次插入\更新字段时使用Date date = new Date(),然后将其插入进去。

新方法

使用**@TableField**

@TableField

在此顺便讲解下@TableField注解的功能

属性类型必须指定默认值描述
valueString“”数据库字段名(未开启驼峰命名法时使用)
existbooleantrue是否为数据库表字段(实例类中有而数据库中没有的字段)
conditionString“”字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s}参考(opens new window)
updateString“”字段 update set 部分注入(如果update方法中也设置了该字段的值,则会忽略update中设置的字段),例如:当在version字段上注解update="%s+1" 表示更新时会 set version=version+1 (%会填充为本字段)
insertStrategyEnumFieldStrategy.DEFAULT举例:NOT_NULL insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
updateStrategyEnumFieldStrategy.DEFAULT举例:IGNORED update table_a set column=#{columnProperty}
whereStrategyEnumFieldStrategy.DEFAULT举例:NOT_EMPTY where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
fillEnumFieldFill.DEFAULT字段自动填充策略
selectbooleantrue是否进行 select 查询(该字段是否在查询时被查询出来)
keepGlobalFormatbooleanfalse是否保持使用全局的 format 进行处理
jdbcTypeJdbcTypeJdbcType.UNDEFINEDJDBC 类型 (该默认值不代表会按照该值生效)
typeHandlerClass<? extends TypeHandler>UnknownTypeHandler.class类型处理器 (该默认值不代表会按照该值生效)
numericScaleString“”指定小数点后保留的位数
FieldStrategy属性(insertStrategy,updateStrategy,whereStrategy)
描述
IGNORED忽略空值判断,实体对象的字段是什么事就用什么值更新,支持null值更新操作
NOT_NULL非 NULL 判断,字段不能为空
NOT_EMPTY非空判断(只对String类型字段,其他类型字段依然为非 NULL 判断)相当于name != null AND name != “”
DEFAULT追随全局配置
NEVER不加入SQL,更新,不论字段是否有值,都不更新
FieldFill属性
描述
DEFAULT默认不处理
INSERT插入时填充字段
UPDATE更新时填充字段
INSERT_UPDATE插入和更新时填充字段

实现自动填充,除了@TableField(fill = FieldFill.INSERT_UPDATE),还需要实现元对象处理器接口:

com.baomidou.mybatisplus.core.handlers.MetaObjectHandler

@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler
{
    @Override
    public void insertFill(MetaObject metaObject) {
      log.info("start insert fill");
      this.strictInsertFill(metaObject,"insertTime", LocalDateTime.class,LocalDateTime.now());
      this.strictInsertFill(metaObject,"updateTime", LocalDateTime.class,LocalDateTime.now());
      //在插入时,既填充插入时间,也插入更新时间
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill");
        this.strictUpdateFill(metaObject,"updateTime",LocalDateTime.class, LocalDateTime.now());
    }
}

注意事项

  • 填充原理是直接给entity属性设置值。

  • 注解则是指定该属性在对应情况下必有值,如果无值则入库会是null

  • 填充处理器MyMetaObjectHandler在 Spring Boot 中需要声明@Component@Bean注入

  • 关于其他相关信息参考官方文档https://www.baomidou.com/pages/4c6bcf/

③乐观锁

每次去拿数据的时候都认为别人不会修改,所以不会上锁,如果想要更新数据,则会在更新前检查在读取至更新这段时间别人有没有修改过这个数据,如果修改过,则重新读取,再次尝试更新,直至成功。

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

在mybatis中提供了@version设置乐观锁的注解

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    //要使用非自增生成id策略的话,数据库的自增必须关闭,否则不会生效
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    @TableField(update = "%s+1")
    private int age;
    private String email;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime insertTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @Version
    private int version;
}

同时还需要配置拦截器(配置类)

@Configuration  //替换xml配置文件
//必须扫描所有Mapper接口(也可以放到启动类中)
@MapperScan("com.example.demo01.Mapper")

@EnableTransactionManagement    //开启事务
//@Component    //为什么这里不能使用@Component:@Configuration本质上也是一个@Component,所以不能同时使用
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //创建拦截器实例
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //往拦截器中添加乐观锁
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    }

}

④分页插件

mybatis继承了分页功能,我们无需再自己写limit

配置类中添加分页拦截器
@Configuration  //替换xml配置文件
//必须扫描所有Mapper接口(也可以放到启动类中)
@MapperScan("com.example.demo01.Mapper")

@EnableTransactionManagement    //开启事务
//@Component    //为什么这里不能使用@Component:@Configuration本质上也是一个@Component,所以不能同时使用
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //创建拦截器实例
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //往拦截器中新增分页
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}

测试,简单实现分页
 @Test
    void testSelectpage(){
        Page<User> userPage = new Page<>(1,5);	//这里是使用Page的构造器,也有其他构造器,建议看源码配合文档使用
        userPage.setSize(4);
        userMapper.selectPage(userPage,null);
        userPage.getRecords().forEach(System.out::println);	//语法糖
    }

⑤逻辑删除

通常删除:直接从数据库中彻底删除、

逻辑删除:没有彻底删除,在用户看来删除了,但数据在数据库中依然存在。

通过deleted字段判断是否为逻辑删除

新增deleted字段

 @TableLogic //逻辑删除
    private int deleted;

配置yml

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

补充

该逻辑删除只对自动注入的sql起效

  • 查找: 追加 where 条件过滤掉已删除数据,如果使用 wrapper.entity 生成的 where 条件也会自动追加该字段
  • 更新: 追加 where 条件防止更新到已删除数据,如果使用 wrapper.entity 生成的 where 条件也会自动追加该字段
  • 删除: 转变为 更新

字段类型支持说明:

  • 支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)
  • 如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now()

附录:

  • 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
  • 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。

⑥条件构造器

条件构造器提供了便捷的方式实现where中的各种条件。

AbstractWrapper

AbstractWrapper是QueryWrapper和UpdateWrapper的父类,用于生成sql的where条件,entity也用于生成sql的where条件

该类中有许多的方法可用于代替复杂的where语句,具体方法API参考官方文档https://www.baomidou.com/pages/10c804/#abstractwrapper

举一个栗子:

查询age不为空且名称带有w的数据。

 @Test
    void WrapperTest(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        //查询age不为空且名称带有w的数据。
        wrapper
                .isNotNull("age")
                .like("name","w");  //支持链式编程
        userMapper.selectList(wrapper);

    }

⑦代码生成器

代码生成器可以仅根据数据库表就为我们生成MVC,甚至可以帮我们设置mybatlsplus中的一些插件,扩展等,大幅度提升我们的编码效率。爽歪歪!

要想使用代码生成器,要遵循以下方法:

首先我们需要导包

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>最新版本</version>
</dependency>

要想使用代码生成器,需要有一个代码生成器类,可以我们自己编写。

public class CodeGenerator{
    public void testCodeGenerator(){	
        FastAutoGenerator.create("url", "username", "password")		//设置数据库信息,代码生成器通过这个数据库中的表
            														//生成代码
            
       /**
         * 全局配置
         */
    .globalConfig(builder -> {			
        builder.author("baomidou") // 设置作者
            .enableSwagger() // 开启 swagger 模式
            .fileOverride() // 覆盖已生成文件
            .outputDir("D://"); // 指定输出目录
    })
            
         /**
         * 包配置
         */
    .packageConfig(builder -> {
        builder.parent("com.baomidou.mybatisplus.samples.generator") // 设置父包名
            .moduleName("system") // 设置父包模块名
            .pathInfo(Collections.singletonMap(OutputFile.xml, "D://")); // 设置mapperXml生成路径
    })
 
         /**
         * 策略配置
         */
    .strategyConfig(builder -> {
        builder.addInclude("t_simple") // 设置需要生成的表名
            .addTablePrefix("t_", "c_"); // 设置过滤表前缀
    })
  
            
        /**
         * 模板配置
         */     
    .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
    .execute();
    }
}

nfig(builder -> {
builder.parent(“com.baomidou.mybatisplus.samples.generator”) // 设置父包名
.moduleName(“system”) // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, “D://”)); // 设置mapperXml生成路径
})

     /**
     * 策略配置
     */
.strategyConfig(builder -> {
    builder.addInclude("t_simple") // 设置需要生成的表名
        .addTablePrefix("t_", "c_"); // 设置过滤表前缀
})

        
    /**
     * 模板配置
     */     
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}

}


上述只是部分的配置信息,要想知晓所有的配置信息,参阅官方文档:https://www.baomidou.com/pages/981406/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值