一起来学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⭐️)

博客(阅读体验较佳)

相关推荐
Dilee8 小时前
Spring AI 2.0.0 Prompt 最小 Demo:system、user、template 到底怎么分工
后端
未秃头的程序猿8 小时前
Java 26正式发布!这3个新特性,让代码量直接减半
java·后端·面试
小旭Coding9 小时前
卧靠!Go 传给前端的 int64 竟然变成了这个?
后端
用户298698530149 小时前
Word 文档文本查找与替换的 Java 实现方案
java·后端
kunge20139 小时前
深度剖析Claude Code 的CLAUDE.md加载逻辑
后端·vibecoding
米沙AI9 小时前
MSYS2 快速使用版本
后端
Csvn9 小时前
Docker 进阶 — 网络模型、数据持久化与多阶段构建
后端
用户4279254051719 小时前
《微博开放平台官方CLI开源了:70+API一行搞定,AI Agent原生支持》
后端
Csvn9 小时前
文本处理三剑客 — grep、sed、awk 实战精讲
后端