前言
目前正在出一个Mybatis Plus
系列教程, 篇幅会较多, 喜欢的话,给个关注❤️ ~
之前给大家讲过Mybatis
教程,而MyBatis-Plus
是一个 MyBatis
的增强工具,在MyBatis
的基础上只做增强不做改变,为简化开发、提高效率而生。大家需要注意的是它只是一个工具,大家需要掌握和重点学习的依然是Mybatis
,在熟练掌握基础的情况下使用MyBatis-Plus
会达到事半功倍的效果。
好了, 废话不多说直接开整吧~
注解
今天给大家讲下mybatis-plus
的内置注解
@TableName
表名
注解,标识实体类对应的表,作用于实体类
@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
中关于全局表前缀配置:
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
表:
@TableName("`user`") public class User { @TableId(value = "id", type = IdType.AUTO) private Long id; private String name; private Integer age; }
插入一条记录:
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: INSERT INTO `user` ( name, age ) VALUES ( ?, ? )
很显然,这里我们指定了主键为id
类型为自增,但是我们的表结构没有指定默认值,所以报错了,所以这里我们需要修改一下表结构,指定自增类型,然后再运行,发现结果正常了
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
类中查看:
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() { } }
以上是常用的模糊匹配,大家也可以自行配置,下面我们一起看一下查询的例子:
@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
的查询类型为模糊匹配,下面一起测试一下
@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); }
结果:
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
字段是进行了模糊匹配的,这种注解查询方式也等效于接口方式:
@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
日志信息:
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
这样在执行的时候,控制台就会输出相关的sql
信息,接着需要修改一下表结构,添加字段version
,接着我们在实体类中添加@Version
注解:
@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
:
@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; } }
紧接着我们编写测试代码:
@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
代表第一个版本值,下面执行下
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
这个很简单,大家可以自己尝试一下
结束语
上述是常用的几个注解,下节给大家讲解代码生成器
本着把自己知道的都告诉大家,如果本文对有所帮助,点赞+关注
鼓励一下呗~