前言
目前正在出一个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
:xml
中resultMap
的id
(用于满足特定类型的实体类对象绑定),非必填,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
只是注入了常用 CRUD
到 MyBatis
里,注入之前是动态的(根据实体类字段以及注解变化而变化),但是注入之后是静态的(等于 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为1731552348470849539
的name
值,并指定了版本为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脚本编程相关文章
- 一起来学Shell脚本编程(一)
- 一起来学Shell脚本编程(二)
- 一起来学Shell脚本编程(三)
- 一起来学Shell脚本编程(四)
- 一起来学Shell脚本编程(五)
- 一起来学Shell脚本编程(六)
- 一起来学Shell脚本编程(七)
往期Linux相关文章
- 一起来学Linux命令(一)
- 一起来学Linux命令(二)
- 一起来学Linux命令(三)
- 一起来学Linux命令(四)
- 一起来学Linux命令(五)
- 一起来学Linux命令(六)
- 一起来学Linux命令(七)
- 一起来学Linux命令(八)
- 一起来学Linux命令(九)
- 一起来学Linux命令(十)
往期面试题相关文章
- 查漏补缺第一期(Redis相关)
- 查漏补缺第二期(synchronized & 锁升级)
- 查漏补缺第三期(分布式事务相关)
- 查漏补缺第四期(Mysql相关)
- 查漏补缺第五期(HashMap & ConcurrentHashMap)
- 查漏补缺第六期(京东一面)
- 查漏补缺第七期(美团到店一面)
- 查漏补缺第八期(阿里一面)
- 查漏补缺第九期(阿里二面)
- 查漏补缺第十期(网易实习一面)
- 查漏补缺第十一期(网易实习二面)
- 查漏补缺第十二期(网易实习三面)
- 查漏补缺第十三期(滴滴实习一面)
- 查漏补缺第十四期(滴滴实习二面)
- 查漏补缺第十五期(华为一面)
- 查漏补缺第十六期(华为二面)
- 查漏补缺第十七期(华为三面)
- 查漏补缺第十八期(你了解class文件吗)
项目源码(源码已更新 欢迎star⭐️)
往期设计模式相关文章
- 一起来学设计模式之认识设计模式
- 一起来学设计模式之单例模式
- 一起来学设计模式之工厂模式
- 一起来学设计模式之建造者模式
- 一起来学设计模式之原型模式
- 一起来学设计模式之适配器模式
- 一起来学设计模式之桥接模式
- 一起来学设计模式之组合模式
- 一起来学设计模式之装饰器模式
- 一起来学设计模式之外观模式
- 一起来学设计模式之享元模式
- 一起来学设计模式之代理模式
- 一起来学设计模式之责任链模式
- 一起来学设计模式之命令模式
- 一起来学设计模式之解释器模式
- 一起来学设计模式之迭代器模式
- 一起来学设计模式之中介者模式
- 一起来学设计模式之备忘录模式
- 一起来学设计模式之观察者模式
- 一起来学设计模式之状态模式
- 一起来学设计模式之策略模式
- 一起来学设计模式之模板方法模式
- 一起来学设计模式之访问者模式
- 一起来学设计模式之依赖注入模式
设计模式项目源码(源码已更新 欢迎star⭐️)
Kafka 专题学习
- 一起来学kafka之Kafka集群搭建
- 一起来学kafka之整合SpringBoot基本使用
- 一起来学kafka之整合SpringBoot深入使用(一)
- 一起来学kafka之整合SpringBoot深入使用(二)
- 一起来学kafka之整合SpringBoot深入使用(三)
项目源码(源码已更新 欢迎star⭐️)
ElasticSearch 专题学习
项目源码(源码已更新 欢迎star⭐️)
往期并发编程内容推荐
- Java多线程专题之线程与进程概述
- Java多线程专题之线程类和接口入门
- Java多线程专题之进阶学习Thread(含源码分析)
- Java多线程专题之Callable、Future与FutureTask(含源码分析)
- 面试官: 有了解过线程组和线程优先级吗
- 面试官: 说一下线程的生命周期过程
- 面试官: 说一下线程间的通信
- 面试官: 说一下Java的共享内存模型
- 面试官: 有了解过指令重排吗,什么是happens-before
- 面试官: 有了解过volatile关键字吗 说说看
- 面试官: 有了解过Synchronized吗 说说看
- Java多线程专题之Lock锁的使用
- 面试官: 有了解过ReentrantLock的底层实现吗?说说看
- 面试官: 有了解过CAS和原子操作吗?说说看
- Java多线程专题之线程池的基本使用
- 面试官: 有了解过线程池的工作原理吗?说说看
- 面试官: 线程池是如何做到线程复用的?有了解过吗,说说看
- 面试官: 阻塞队列有了解过吗?说说看
- 面试官: 阻塞队列的底层实现有了解过吗? 说说看
- 面试官: 同步容器和并发容器有用过吗? 说说看
- 面试官: CopyOnWrite容器有了解过吗? 说说看
- 面试官: Semaphore在项目中有使用过吗?说说看(源码剖析)
- 面试官: Exchanger在项目中有使用过吗?说说看(源码剖析)
- 面试官: CountDownLatch有了解过吗?说说看(源码剖析)
- 面试官: CyclicBarrier有了解过吗?说说看(源码剖析)
- 面试官: Phaser有了解过吗?说说看
- 面试官: Fork/Join 有了解过吗?说说看(含源码分析)
- 面试官: Stream并行流有了解过吗?说说看