一起来学Mybatis Plus(二) & 内置注解

前言

目前正在出一个Mybatis Plus系列教程, 篇幅会较多, 喜欢的话,给个关注❤️ ~

之前给大家讲过Mybatis教程,而MyBatis-Plus 是一个 MyBatis 的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。大家需要注意的是它只是一个工具,大家需要掌握和重点学习的依然是Mybatis,在熟练掌握基础的情况下使用MyBatis-Plus会达到事半功倍的效果。

好了, 废话不多说直接开整吧~

注解

今天给大家讲下mybatis-plus的内置注解

@TableName

表名注解,标识实体类对应的表,作用于实体类

java 复制代码
@TableName("user")
public class User {
    private Long id;
    private String name;
    private Integer age;
}

该注解有以下几个属性:

  • value: 表名,非必填,string类型,默认""
  • schema: schema,非必填,string类型,默认""
  • keepGlobalPrefix: 是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时),非必填,boolean类型,默认false
  • resultMap: xmlresultMapid(用于满足特定类型的实体类对象绑定),非必填,string类型,默认""
  • autoResultMap: 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入),非必填,boolean类型,默认false
  • excludeProperty: 需要排除的属性名,非必填,string[]类型,默认{}

keepGlobalPrefix中关于全局表前缀配置:

yml 复制代码
mybatis-plus:
  global-config:
    db-config:
      table-prefix: sys_

关于autoResultMap,MyBatisPlus会自动构建一个 resultMap 并注入到MyBatis里(一般用不上),因为 MyBatisPlus 底层是 MyBatis,所以 MyBatisPlus 只是注入了常用 CRUDMyBatis 里,注入之前是动态的(根据实体类字段以及注解变化而变化),但是注入之后是静态的(等于 XML 配置中的内容)。

@TableId

主键注解,作用于实体类主键字段,有以下几个属性:

  • value: 主键字段名,非必填,string类型,默认""
  • type: 指定主键类型,非必填,Enum类型, 默认 IdType.NONE

例如,这里指定主键为id且类型自增,表依然使用上节内容的user表:

java 复制代码
@TableName("`user`")
public class User {
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
}

插入一条记录:

java 复制代码
 userMapper.insert(User.builder().name("xiaohong6").age(26).build());

运行之后发现报错了Field 'id' doesn't have a default value; nested exception is java.sql.SQLException: Field 'id' doesn't have a default value,然后我们看报错的语句:

sql 复制代码
SQL: INSERT INTO `user`  ( name, age )  VALUES (  ?, ?  )

很显然,这里我们指定了主键为id类型为自增,但是我们的表结构没有指定默认值,所以报错了,所以这里我们需要修改一下表结构,指定自增类型,然后再运行,发现结果正常了

arduino 复制代码
1731552348470849539, 'xiaohong6', 26

在上节中,主键id我们并没有设置自增,mybatis默认帮我们生成了一个id

IdType

  • AUTO:数据库 ID 自增
  • NONE:无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
  • INPUT: insert 前自行 set 主键值
  • ASSIGN_ID: 分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
  • ASSIGN_UUID: 分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认 default 方法)
  • ID_WORKER: 分布式全局唯一 ID 长整型类型(please use ASSIGN_ID)
  • UUID 32 位 UUID 字符串(please use ASSIGN_UUID)
  • ID_WORKER_STR 分布式全局唯一 ID 字符串类型(please use ASSIGN_ID)

@TableField

字段注解(非主键)

  • value: 数据库字段名,非必填,string类型,默认""
  • exist:是否为数据库表字段,非必填,boolean类型, 默认true
  • condition:字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s},非必填,string类型,默认""
  • update:字段 update set 部分注入,例如:当在version字段上注解update="%s+1" 表示更新时会 set version=version+1 (该属性优先级高于 el 属性),非必填,string类型,默认""
  • select:是否进行 select 查询,非必填,boolean类型, 默认true
  • keepGlobalFormat是否保持使用全局的 format 进行处理,非必填,boolean类型,默认false
  • numericScale: 指定小数点后保留的位数,非必填,string类型,默认""

比如我们指定condition,该值有以下几种情况,可以到SqlCondition类中查看:

java 复制代码
public class SqlCondition {
    public static final String EQUAL = "%s=#{%s}";
    public static final String NOT_EQUAL = "%s<>#{%s}";
    public static final String LIKE = "%s LIKE CONCAT('%%',#{%s},'%%')";
    public static final String ORACLE_LIKE = "%s LIKE CONCAT(CONCAT('%%',#{%s}),'%%')";
    public static final String LIKE_LEFT = "%s LIKE CONCAT('%%',#{%s})";
    public static final String LIKE_RIGHT = "%s LIKE CONCAT(#{%s},'%%')";

    public SqlCondition() {
    }
}

以上是常用的模糊匹配,大家也可以自行配置,下面我们一起看一下查询的例子:

java 复制代码
@Builder
@Data
@TableName("`user`")
public class User {
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @TableField(value = "name", condition = SqlCondition.LIKE)
    private String name;
    private Integer age;
}

上述实体类中,指定了字段name的查询类型为模糊匹配,下面一起测试一下

java 复制代码
@Test
    public void selectByConditionName() {
        // select all
        QueryWrapper<User> queryWrapper = new QueryWrapper<>(User.builder().name("x").build());
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);
      
    }

结果:

ini 复制代码
User(id=1731552348403740673, name=xiaohong2, age=22)
User(id=1731552348403740674, name=xiaohong3, age=23)
User(id=1731552348470849537, name=xiaohong4, age=24)
User(id=1731552348470849538, name=xiaohong5, age=25)
User(id=1731552348470849539, name=xiaohong6, age=26)

可以看到name字段是进行了模糊匹配的,这种注解查询方式也等效于接口方式:

java 复制代码
    @Test
    public void selectByName() {
        // select all
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.like("name", "x%");
        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);
        //User(id=1731552348403740673, name=xiaohong2, age=22)
        //User(id=1731552348403740674, name=xiaohong3, age=23)
        //User(id=1731552348470849537, name=xiaohong4, age=24)
        //User(id=1731552348470849538, name=xiaohong5, age=25)
        //User(id=1731552348470849539, name=xiaohong6, age=26)
    }

大家可以根据情况使用,推荐大家使用接口的方式,不要在类字段上作用太多的注解,也方便维护

@Version

乐观锁注解, 作用于字段上,主要解决并发更新问题,为了方便看问题,我们可以配置sql日志信息:

yml 复制代码
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

这样在执行的时候,控制台就会输出相关的sql信息,接着需要修改一下表结构,添加字段version,接着我们在实体类中添加@Version注解:

java 复制代码
@TableName("`user`")
public class User {
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @TableField(value = "name", condition = SqlCondition.LIKE)
    private String name;
    private Integer age;

    /**
     * 乐观锁
     */
    @Version
    private Integer version;
}

接着配置MybatisPlus乐观锁插件,我们修改下配置类MyBatisPlusConfig:

java 复制代码
@EnableTransactionManagement
@Configuration
@MapperScan("com.springboot.all.mybatisplus.mapper")
public class MyBatisPlusConfig {
    // 注册乐观锁插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }

}

紧接着我们编写测试代码:

java 复制代码
@Test
public void updateTest() {
    userMapper.updateById(User.builder().id(1731552348470849539L).name("hhhhh").version(1).build());
    userMapper.updateById(User.builder().id(1731552348470849539L).name("hhhhh").version(1).build());
}

上述代码更新了ID为1731552348470849539name值,并指定了版本为1,这里的version一定要指定不能传空,否则会更新不成功,我们添加的version字段初始值设置为1代表第一个版本值,下面执行下

sql 复制代码
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@28f9fedd] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@6fbc1bb] will not be managed by Spring
==>  Preparing: UPDATE `user` SET name=?, version=? WHERE id=? AND version=?
==> Parameters: hhhhh(String), 2(Integer), 1731552348470849539(Long), 1(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@28f9fedd]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@619f2afc] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@6fbc1bb] will not be managed by Spring
==>  Preparing: UPDATE `user` SET name=?, version=? WHERE id=? AND version=?
==> Parameters: hhhhh(String), 2(Integer), 1731552348470849539(Long), 1(Integer)
<==    Updates: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@619f2afc]

从日志情况来看,只有第一条更新成功了,通过sql语句可以看到version也参与了查询条件,然后我们看下表中的记录更新情况

1731552348470849539	hhhhh	26	2	

可以看到version值自动变为2了,所以第二条更新失败

@OrderBy

内置 SQL 默认指定排序,优先级低于 wrapper 条件查询

  • asc:是否倒序查询, 非必填,boolean类型, 默认true
  • sort:数字越小越靠前, 非必填short类型,默认值Short.MAX_VALUE

这个很简单,大家可以自己尝试一下

结束语

上述是常用的几个注解,下节给大家讲解代码生成器

本着把自己知道的都告诉大家,如果本文对有所帮助,点赞+关注鼓励一下呗~

MybatisPlus教程相关文章

往期Nginx教程相关文章

往期Docker教程相关文章

往前Shell脚本编程相关文章

往期Linux相关文章

往期面试题相关文章

项目源码(源码已更新 欢迎star⭐️)

往期设计模式相关文章

设计模式项目源码(源码已更新 欢迎star⭐️)

Kafka 专题学习

项目源码(源码已更新 欢迎star⭐️)

ElasticSearch 专题学习

项目源码(源码已更新 欢迎star⭐️)

往期并发编程内容推荐

推荐 SpringBoot & SpringCloud (源码已更新 欢迎star⭐️)

博客(阅读体验较佳)

相关推荐
励碼12 分钟前
Spring Security 6.3 权限异常处理实战解析
spring boot
张铁铁是个小胖子1 小时前
MyBatis学习
java·学习·mybatis
0zxm1 小时前
06 - Django 视图view
网络·后端·python·django
m0_748257181 小时前
Spring Boot FileUpLoad and Interceptor(文件上传和拦截器,Web入门知识)
前端·spring boot·后端
小_太_阳2 小时前
Scala_【1】概述
开发语言·后端·scala·intellij-idea
智慧老师2 小时前
Spring基础分析13-Spring Security框架
java·后端·spring
lxyzcm2 小时前
C++23新特性解析:[[assume]]属性
java·c++·spring boot·c++23
搬码后生仔4 小时前
asp.net core webapi项目中 在生产环境中 进不去swagger
chrome·后端·asp.net
迷糊的『迷』4 小时前
vue-axios+springboot实现文件流下载
vue.js·spring boot
凡人的AI工具箱4 小时前
每天40分玩转Django:Django国际化
数据库·人工智能·后端·python·django·sqlite