一、 介绍
在第一节的笔记中,我们仅仅引入依赖,继承了一个 BaseMapper,就是实现了操纵数据库查询数据的目的。
那么MybatisPlus怎么知道我们要查的是数据库中的哪个表呢? 表中的字段是如何获取的呢?
接下来,就将介绍如何利用注解来联系数据库中表以及表的字段的。
二、常见注解
在第一节中我们的入门案例UserMapper接口继承了BaseMapper,同时泛型指定为了User。那么这个User类就是MP找到数据库表的关键。
MybatisPlus就是根据PO实体的信息来推断出表的信息,从而生成SQL的。默认情况下:
- MybatisPlus会把PO实体的类名驼峰转下划线作为表名
- MybatisPlus会把PO实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型
- MybatisPlus会把名为id的字段作为主键
但很多情况下,默认的实现与实际场景不符,因此MybatisPlus提供了一些注解便于我们声明表信息。
1、@TableName
该注解用于指定实体类对应的数据库表名。当实体类名与数据库表名不一致,或者实体类名不是数据库表名的驼峰写法时,需要使用这个注解来明确指定表名。
@TableName("sys_user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | “” | 表名 |
schema | String | 否 | “” | schema |
keepGlobalPrefix | boolean | 否 | false | 当全局配置了 tablePrefix 时,这个属性决定是否保持使用全局的表前缀。如果设置为 true,即使注解中指定了表名,也会自动加上全局的表前缀。 |
resultMap | String | 否 | “” | 指定在 XML 中定义的 ResultMap 的 ID,用于将查询结果映射到特定类型的实体类对象。 |
autoResultMap | boolean | 否 | false | 是否自动构建 resultMap。如果已经设置了 resultMap,这个属性不会生效。 |
|excludeProperty|String [] | 否 | {} |指定在映射时需要排除的属性名。这些属性将不会被包含在生成的 SQL 语句中。@since 3.3.1|
2、@TableId
该注解用于标记实体类中的主键字段。如果你的主键字段名为 id,你可以省略这个注解。
@TableName("sys_user")
public class User {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
}
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | “” | 指定数据库表的主键字段名。如果不设置,MyBatis-Plus 将使用实体类中的字段名作为数据库表的主键字段名。 |
type | Enum | 否 | IdType.NONE | 指定主键的生成策略。 |
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 方法) |
这里比较常见的有三种:
- AUTO:利用数据库的id自增长
- INPUT:手动生成id
- ASSIGN_ID:雪花算法生成Long类型的全局唯一id,这是默认的ID策略
3、@TableField
该注解用于标记实体类中的非主键字段,它告诉 MyBatis-Plus 如何映射实体类字段到数据库表字段。如果你的实体类字段名遵循驼峰命名规则,并且与数据库表字段名一致,你可以省略这个注解。
@TableName("sys_user")
public class User {
@TableId
private Long id;
@TableField("nickname") // 映射到数据库字段 "nickname"
private String name;
private Integer age;
private String email;
@TableField("isMarried")
private Boolean isMarried;
@TableField(value = "`order`") // 数据库字段名为 order,使用反引号转义
private String order;
}
一般情况下我们并不需要给字段添加@TableField注解,一些特殊情况除外:
- 成员变量名与数据库字段名不一致
- 成员变量是以isXXX命名,按照JavaBean的规范,MybatisPlus识别字段时会把is去除,这就导致与数据库不符。
- 成员变量名与数据库一致,但是与数据库的关键字冲突。使用@TableField注解给字段名添加转义字符:``
支持的其它属性如下:
condition (需要有MP的相关知识,初学先跳过)
在执行实体查询(EntityQuery)时,指定字段的条件表达式。这允许你自定义字段在 WHERE 子句中的比较方式。如果该项有值则按设置的值为准,无值则默认为全局的 %s=#{%s} 为准。
示例说明
假设我们有一个实体类 User,它有 id、name 和 age 三个字段。我们想要查询所有年龄大于 18 岁的用户,我们可以使用 QueryWrapper 来构建这个查询,直接传递 User 实体类实例:
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.SqlCondition;
// 实体类定义
@TableName("sys_user")
public class User {
@TableId
private Long id;
private String name;
@TableField(condition = "%s > #{%s}") // 自定义 age 字段的条件表达式
private Integer age;
private String email;
}
// 使用 EntityQuery 构建查询
public List<User> findUserAgeOver18() {
// 创建 User 实例,用于设置查询条件
User queryEntity = new User();
queryEntity.setAge(18); // 设置 age 字段的值
// 创建 QueryWrapper 实例,并传递 User 实例
QueryWrapper<User> queryWrapper = new QueryWrapper<>(queryEntity);
// 执行查询
List<User> userList = userMapper.selectList(queryWrapper);
return userList;
}
在这个例子中,我们通过 @TableField(condition = “%s > #{%s}”) 注解为 age 字段设置了自定义的条件表达式。当构建查询时,我们创建了一个 User 实例,并设置了 age 字段的值为 18。然后,我们使用这个实例来创建 QueryWrapper,MyBatis-Plus 会根据实体类上的注解自动生成相应的 SQL 查询条件。
执行 findUserAgeOver18 方法时,MyBatis-Plus 会生成类似以下的 SQL 语句:
SELECT * FROM sys_user WHERE age > 18;
通过这种方式,condition 属性允许我们自定义字段在查询时的行为,使得查询更加灵活和符合特定需求,同时避免了手动编写 SQL 片段的繁琐。
fill
字段自动填充策略。该属性用于指定在执行数据库操作(如插入、更新)时,如何自动填充字段的值。通过使用 FieldFill 枚举,可以灵活地控制字段的填充行为。
注意:此功能需要配合自动填充字段功能一起使用。(后续会介绍)
示例说明
假设有一个 User 实体类,其中包含 createTime 和 updateTime 字段,我们希望在创建用户时自动填充创建时间,在更新用户信息时自动填充更新时间。
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import java.time.LocalDateTime;
@TableName("user")
public class User {
// 其他字段...
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
// 构造函数、getter 和 setter...
}
在这个示例中,createTime 字段会在插入操作时自动填充当前时间,而 updateTime 字段会在更新操作时自动填充当前时间。这样,我们就不需要在每次数据库操作时手动设置这些时间字段的值了。
请注意,为了使自动填充功能正常工作,需要在 MyBatis-Plus 的配置中设置相应的自动填充处理器,并且确保在实体类对应的 Mapper 接口中定义了相应的插入和更新方法。(完整流程后续介绍)
4、@Version
该注解用于标记实体类中的字段作为乐观锁版本号字段。乐观锁是一种并发控制机制,它假设多个事务可以同时进行而不会互相干扰,只在提交事务时检查是否有冲突。通过在实体类中使用@Version注解,MyBatis-Plus 会在更新操作时自动检查版本号,确保在更新过程中数据没有被其他事务修改。
@TableName("sys_user")
public class User {
@TableId
private Long id;
@TableField("nickname") // 映射到数据库字段 "nickname"
private String name;
private Integer age;
private String email;
@Version // 标记为乐观锁版本号字段
private Integer version;
}
在上面的示例中,version字段被标记为乐观锁版本号。当执行更新操作时,MyBatis-Plus 会检查该字段的值是否与数据库中的值一致。如果不一致,说明在读取数据后有其他事务修改了数据,此时会抛出乐观锁异常,提示开发者进行相应的处理。
使用@Version注解可以有效地防止并发更新时出现的数据不一致问题,提高系统的并发性能和数据完整性。开发者无需手动编写版本号检查的代码,MyBatis-Plus 会自动处理这一过程。
5、@EnumValue
该注解用于标记枚举类中的字段,指定在数据库中存储的枚举值。当实体类中的某个字段是枚举类型时,使用@EnumValue注解可以告诉MyBatis-Plus在数据库中存储枚举值的哪个属性。
@TableName("sys_user")
public class User {
@TableId
private Long id;
@TableField("nickname") // 映射到数据库字段 "nickname"
private String name;
private Integer age;
private String email;
private Gender gender; // 假设 Gender 是一个枚举类型
}
public enum Gender {
MALE("M", "男"),
FEMALE("F", "女");
private String code;
private String description;
Gender(String code, String description) {
this.code = code;
this.description = description;
}
@EnumValue // 指定存储到数据库的枚举值为 code
public String getCode() {
return code;
}
}
在上面的示例中,Gender枚举类中的code字段被标记为@EnumValue,这意味着在数据库中存储User实体类的gender字段时,将存储Gender枚举的code值,而不是枚举常量本身。
使用@EnumValue注解可以灵活地控制枚举类型在数据库中的存储方式,使得数据库中的数据更加紧凑和易于处理。同时,它也简化了从数据库读取枚举值时的转换过程,因为MyBatis-Plus会自动根据@EnumValue注解的配置将数据库中的值转换为对应的枚举实例。
6、@TableLogic
该注解用于标记实体类中的字段作为逻辑删除字段。逻辑删除是一种数据管理策略,它不是真正地从数据库中删除记录,而是在记录中标记该记录为已删除状态。通过使用@TableLogic注解,MyBatis-Plus 可以在查询、更新和删除操作中自动处理逻辑删除字段的值。
@TableName("sys_user")
public class User {
@TableId
private Long id;
@TableField("nickname") // 映射到数据库字段 "nickname"
private String name;
private Integer age;
private String email;
@TableLogic(value = "0", delval = "1") // 逻辑删除字段
private Integer deleted;
}
在上面的示例中,deleted字段被标记为逻辑删除字段。@TableLogic注解的value属性指定了逻辑未删除的值(在这个例子中是0),而delval属性指定了逻辑删除的值(在这个例子中是1)。
当执行查询操作时,MyBatis-Plus 会自动过滤掉标记为逻辑删除的记录,只返回未删除的记录。在执行更新操作时,如果更新操作会导致逻辑删除字段的值变为逻辑删除值,MyBatis-Plus 会自动将该记录标记为已删除。在执行删除操作时,MyBatis-Plus 会自动将逻辑删除字段的值更新为逻辑删除值,而不是物理删除记录。
使用@TableLogic注解可以实现数据的逻辑删除,有助于维护数据的完整性和可追溯性,同时避免了物理删除操作可能带来的数据丢失风险。开发者无需手动编写逻辑删除的代码,MyBatis-Plus 会自动处理这一过程。
7、@OrderBy
该注解用于指定实体类中的字段在执行查询操作时的默认排序方式。通过在实体类字段上使用@OrderBy注解,可以确保在执行查询时,如果没有显式指定排序条件,MyBatis-Plus 将按照注解中定义的排序规则返回结果。
@TableName("sys_user")
public class User {
@TableId
private Long id;
@TableField("nickname") // 映射到数据库字段 "nickname"
private String name;
@OrderBy(asc = false, sort = 10) // 指定默认排序为倒序,优先级为10
private Integer age;
private String email;
}
在上面的示例中,age字段被标记为@OrderBy,并设置了asc属性为false,表示默认排序为倒序(降序),sort属性设置为10,表示该排序规则的优先级。
@OrderBy注解的asc属性用于指定排序是否为升序,如果设置为true,则表示升序排序;如果设置为false,则表示降序排序。sort属性用于指定排序规则的优先级,数字越小,优先级越高,即越先被应用。
需要注意的是,@OrderBy注解的排序规则优先级低于在查询时通过Wrapper条件查询对象显式指定的排序条件。如果在Wrapper中指定了排序条件,那么@OrderBy注解中定义的默认排序将被覆盖。
使用@OrderBy注解可以简化代码,避免在每次查询时都显式指定排序条件,同时提供了一种默认的排序方式,有助于提高代码的可读性和维护性。
最后几个注解来自官网介绍,学起来也很简单,大家一定要看着代码进行理解。