本文详细介绍了 MyBatisPlus 注解的用法及属性。
@TableName(表名称)
该注解用于指定实体类对应的数据库表名。当实体类名与数据库表名不一致,或者实体类名不是数据库表名的驼峰写法时,您需要使用这个注解来明确指定表名。
/**
* 数据库表相关
* @Documented 表示该注解将被javadoc工具文档化
* @Retention(RetentionPolicy.RUNTIME) 表示该注解在运行时依然可用,可以通过反射机制读取
* @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) 指定该注解可以应用于类和注解类型
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface TableName {
/**
* 实体对应的表名
*/
String value() default "";
/**
* schema
* <p>
* 配置此值将覆盖全局配置的 schema
*
* @since 3.1.1
*/
String schema() default "";
/**
* 是否保持使用全局的 tablePrefix 的值
* <p> 只生效于 既设置了全局的 tablePrefix 也设置了上面 {@link #value()} 的值 </p>
* <li> 如果是 false , 全局的 tablePrefix 不生效 </li>
*
* @since 3.1.1
*/
boolean keepGlobalPrefix() default false;
/**
* 实体映射结果集,
* 只生效于 mp 自动注入的 method
*/
String resultMap() default "";
/**
* 是否自动构建 resultMap 并使用,
* 只生效于 mp 自动注入的 method,
* 如果设置 resultMap 则不会进行 resultMap 的自动构建并注入,
* 只适合个别字段 设置了 typeHandler 或 jdbcType 的情况
* MyBatis-Plus 会自动构建一个 resultMap 并注入到 MyBatis 中。但是,一旦注入完成,生成的内容就是静态的,类似于 XML 配置中的内容。在使用与 resultMap 相关的操作时,请注意 typeHandler 的处理。
* MyBatis 只支持将 typeHandler 写在两个地方:
* 定义在 resultMap 中,作用于查询结果的封装。
* 定义在 insert 和 update 语句的 #{property} 中的 property 后面,例如:#{property,typehandler=xxx.xxx.xxx},并且只作用于当前设置的值。
* 除了以上两种直接指定 typeHandler 的形式,MyBatis 还有一个全局扫描自定义 typeHandler 包的配置,原理是根据您的属性类型去找其对应的 typeHandler 并使用。
* @since 3.1.2
*/
boolean autoResultMap() default false;
/**
* 需要排除的属性名
*
* @since 3.3.1
*/
String[] excludeProperty() default {};
}
@TableId(主键字段)
该注解用于标记实体类中的主键字段。如果你的主键字段名为 id,可以省略这个注解。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableId {
/**
* 字段名(该值可无)
*/
String value() default "";
/**
* 主键类型
* {@link IdType}
*/
IdType type() default IdType.NONE;
}
IdType 枚举类型定义
IdType.AUTO
:使用数据库自增 ID 作为主键。IdType.NONE
:无特定生成策略,如果全局配置中有 IdType 相关的配置,则会跟随全局配置。IdType.INPUT
:在插入数据前,由用户自行设置主键值。IdType.ASSIGN_ID
:自动分配ID
,适用于Long
、Integer
、String
类型的主键。默认使用雪花算法通过IdentifierGenerator
的nextId
实现。@since 3.3.0IdType.ASSIGN_UUID
:自动分配UUID
,适用于String
类型的主键。默认实现为IdentifierGenerator
的nextUUID
方法。@since 3.3.0
@Data
@TableName(schema = "test")
public class MybatisUser extends Model<MybatisUser> {
@TableId
private Integer id;
private String name;
private SexEnum sex;
}
@TableField(非主键字段)
该注解用于标记实体类中的非主键字段,它告诉 MyBatis-Plus 如何映射实体类字段到数据库表字段。如果你的实体类字段名遵循驼峰命名规则,并且与数据库表字段名一致,你可以省略这个注解。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableField {
/**
* 数据库字段值
* <p>
* 不需要配置该值的情况:
* <li> 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 true 时,
* (mp下默认是true,mybatis默认是false), 数据库字段值.replace("_","").toUpperCase() == 实体属性名.toUpperCase() </li>
* <li> 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 false 时,
* 数据库字段值.toUpperCase() == 实体属性名.toUpperCase() </li>
*/
String value() default "";
/**
* 是否为数据库表字段
* <p>
* 默认 true 存在,false 不存在
*/
boolean exist() default true;
/**
* 字段 where 实体查询比较条件
* <p>
* 默认 {@link SqlCondition#EQUAL}
*/
String condition() default "";
/**
* 字段 update set 部分注入, 该注解优于 el 注解使用
* <p>
* 例1:@TableField(.. , update="%s+1") 其中 %s 会填充为字段
* 输出 SQL 为:update 表 set 字段=字段+1 where ...
* <p>
* 例2:@TableField(.. , update="now()") 使用数据库时间
* 输出 SQL 为:update 表 set 字段=now() where ...
*/
String update() default "";
/**
* 字段验证策略之 insert: 当insert操作时,该字段拼接insert语句时的策略
* <p>
* IGNORED: 直接拼接 insert into table_a(column) values (#{columnProperty});
* NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
* NOT_EMPTY: insert into table_a(<if test="columnProperty != null and columnProperty!=''">column</if>) values (<if test="columnProperty != null and columnProperty!=''">#{columnProperty}</if>)
* NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL
*
* @since 3.1.2
*/
FieldStrategy insertStrategy() default FieldStrategy.DEFAULT;
/**
* 字段验证策略之 update: 当更新操作时,该字段拼接set语句时的策略
* <p>
* IGNORED: 直接拼接 update table_a set column=#{columnProperty}, 属性为null/空string都会被set进去
* NOT_NULL: update table_a set <if test="columnProperty != null">column=#{columnProperty}</if>
* NOT_EMPTY: update table_a set <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
* NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL
*
* @since 3.1.2
*/
FieldStrategy updateStrategy() default FieldStrategy.DEFAULT;
/**
* 字段验证策略之 where: 表示该字段在拼接where条件时的策略
* <p>
* IGNORED: 直接拼接 column=#{columnProperty}
* NOT_NULL: <if test="columnProperty != null">column=#{columnProperty}</if>
* NOT_EMPTY: <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
* NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL
*
* @since 3.1.2
*/
FieldStrategy whereStrategy() default FieldStrategy.DEFAULT;
/**
* 字段自动填充策略
* <p>
* 在对应模式下将会忽略 insertStrategy 或 updateStrategy 的配置,等于断言该字段必有值
*/
FieldFill fill() default FieldFill.DEFAULT;
/**
* 是否进行 select 查询
* <p>
* 大字段可设置为 false 不加入 select 查询范围
*/
boolean select() default true;
/**
* 是否保持使用全局的 columnFormat 的值
* <p>
* 只生效于 既设置了全局的 columnFormat 也设置了上面 {@link #value()} 的值
* 如果是 false , 全局的 columnFormat 不生效
*
* @since 3.1.1
*/
boolean keepGlobalFormat() default false;
/**
* {@link ResultMapping#property} and {@link ParameterMapping#property}
*
* @since 3.4.4
*/
String property() default "";
/**
* JDBC类型 (该默认值不代表会按照该值生效),
* 只生效于 mp 自动注入的 method,
* 建议配合 {@link TableName#autoResultMap()} 一起使用
* <p>
* {@link ResultMapping#jdbcType} and {@link ParameterMapping#jdbcType}
*
* @since 3.1.2
*/
JdbcType jdbcType() default JdbcType.UNDEFINED;
/**
* 类型处理器 (该默认值不代表会按照该值生效),
* 只生效于 mp 自动注入的 method,
* 建议配合 {@link TableName#autoResultMap()} 一起使用
* <p>
* {@link ResultMapping#typeHandler} and {@link ParameterMapping#typeHandler}
*
* @since 3.1.2
*/
Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;
/**
* 只在使用了 {@link #typeHandler()} 时判断是否辅助追加 javaType
* <p>
* 一般情况下不推荐使用
* {@link ParameterMapping#javaType}
*
* @since 3.4.0 @2020-07-23
*/
boolean javaType() default false;
/**
* 指定小数点后保留的位数,
* 只生效于 mp 自动注入的 method,
* 建议配合 {@link TableName#autoResultMap()} 一起使用
* <p>
* {@link ParameterMapping#numericScale}
*
* @since 3.1.2
*/
String numericScale() default "";
}
condition (字段 where 实体查询比较条件)
在执行实体查询(EntityQuery)时,指定字段的条件表达式。这允许你自定义字段在 WHERE 子句中的比较方式。如果该项有值则按设置的值为准,无值则默认为全局的 %s=#{%s}
为准。
@TableField(condition = "%s > #{%s}")
EntityQuery 是指在构建查询条件时,直接使用实体类的字段来设置查询条件,而不是手动编写 SQL 片段。
比如:
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;
}
得到的sql
SELECT * FROM sys_user WHERE age > 18;
/**
* SQL 比较条件常量定义类
*
* @author hubin
* @since 2018-01-05
*/
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},'%%')";
/**
* % 两边 % [oracle使用]
*/
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},'%%')";
}
update (字段 update set 部分注入, 该注解优于 el 注解使用)
在执行更新操作时,指定字段在 SET 子句中的表达式。这个属性的优先级高于 el 属性,允许你自定义字段的更新逻辑。
import com.baomidou.mybatisplus.annotation.TableField;
@TableName("sys_user")
public class User {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
@TableField(update="%s+1") // 自定义更新时的表达式
private Integer version;
}
@TableField(update="%s+1")
注解告诉 MyBatis-Plus,在执行更新操作时,对于 version 字段,应该使用version = version + 1
的表达式。这意味着,每次更新操作,version 字段的值都会自动增加 1。
UPDATE sys_user
SET name = 'Updated Name', age = 30, email = 'updated@example.com', version = version + 1
WHERE id = 1;
insertStrategy(新增操作如果处理字段值)
定义在插入新记录时,如何处理字段的值。这个属性允许你控制字段是否应该包含在 INSERT 语句中,以及在什么条件下包含。
package com.baomidou.mybatisplus.annotation;
/**
* 字段策略枚举类
* <p>
* 如果字段是基本数据类型则最终效果等同于 {@link #ALWAYS}
*
*/
public enum FieldStrategy {
/**
* 忽略判断
*
* @deprecated 3.5.3.2 该字段存在语义理解问题效果等于{@link #ALWAYS}
*/
@Deprecated
IGNORED,
/**
* 任何时候都加入 SQL
*/
ALWAYS,
/**
* 非NULL判断
*/
NOT_NULL,
/**
* 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
*/
NOT_EMPTY,
/**
* 默认的,一般只用于注解里
* <p>1. 在全局里代表 NOT_NULL</p>
* <p>2. 在注解里代表 跟随全局</p>
*/
DEFAULT,
/**
* 不加入 SQL
*/
NEVER
}
示例
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
@TableName("sys_user")
public class User {
@TableId
private Long id;
@TableField(insertStrategy = FieldStrategy.NOT_EMPTY) // 仅在 nickname 不为空时插入
private String nickname;
private Integer age;
private String email;
}
在这个例子中,@TableField(insertStrategy = FieldStrategy.NOT_EMPTY)
注解告诉 MyBatis-Plus,在插入新用户时,只有当 nickname 字段不为空时才将其包含在 INSERT 语句中。
如果我们执行以下插入操作MyBatis-Plus 会自动生成类似以下的 SQL 语句:
NSERT INTO sys_user (nickname, age, email) VALUES ('John Doe', 25, 'john.doe@example.com');
如果 nickname 字段为空,生成的 SQL 将不包含 nickname 字段:
INSERT INTO sys_user (age, email)
updateStrategy(更新时字段值操作)
定义在更新记录时,如何处理字段的值。这个属性允许你控制字段是否应该包含在 UPDATE 语句的 SET 子句中,以及在什么条件下包含。
示例说明
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
@TableName("sys_user")
public class User {
@TableId
private Long id;
@TableField(updateStrategy = FieldStrategy.ALWAYS) // 总是更新 nickname,忽略值的检查
private String nickname;
private Integer age;
private String email;
}
UPDATE sys_user SET nickname = 'Updated Nickname', age = 30, email = 'updated@example.com' WHERE id = 1;
whereStrategy(字段在拼接where条件时的策略)
定义在生成更新语句的 WHERE 子句时,如何处理字段的值。这个属性允许你控制字段是否应该包含在 WHERE 子句中,以及在什么条件下包含。
示例说明
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
@TableName("sys_user")
public class User {
@TableId
private Long id;
@TableField(whereStrategy = FieldStrategy.NOT_EMPTY) // 仅在 nickname 不为空时作为 WHERE 条件
private String nickname;
private Integer age;
private String email;
}
User user = new User();
user.setEmail("john.doe@example.com");
User whereEntity = new User();
whereEntity.setNickname("John Doe");
whereEntity.setAge(30);
// 使用 whereEntity 方法
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(whereEntity);
userMapper.update(user, updateWrapper);
MyBatis-Plus 会自动生成类似以下的 SQL 语句:
UPDATE sys_user SET email = 'john.doe@example.com' WHERE nickname = 'John Doe' AND age = 30;
如果 nickname 字段为空,生成的 SQL 将不包含 nickname 字段:
UPDATE sys_user SET email = 'john.doe@example.com' WHERE age = 30;
fill(字段自动填充策略)
字段自动填充策略。该属性用于指定在执行数据库操作(如插入、更新)时,如何自动填充字段的值。通过使用 FieldFill 枚举,可以灵活地控制字段的填充行为。
此功能需要配合自动填充字段功能一起使用。https://blog.csdn.net/cauyaycau/article/details/143776927#t3
/**
* 字段填充策略枚举类
*
* <p>
* 判断注入的 insert 和 update 的 sql 脚本是否在对应情况下忽略掉字段的 if 标签生成
* <if test="...">......</if>
* 判断优先级比 {@link FieldStrategy} 高
* </p>
*
*/
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入时填充字段
*/
INSERT,
/**
* 更新时填充字段
*/
UPDATE,
/**
* 插入和更新时填充字段
*/
INSERT_UPDATE
}
示例说明
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;
}
实现 MetaObjectHandler(确保你的 MyMetaObjectHandler
类被 Spring 管理,可以通过 @Component
或 @Bean
注解来实现。)
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("开始插入填充...");
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("开始更新填充...");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
- 自动填充是直接给实体类的属性设置值。
- 如果属性没有值,入库时会是
null
。MetaObjectHandler
提供的默认方法策略是:如果属性有值则不覆盖,如果填充值为null
则不填充。- 字段必须声明
@TableField
注解,并设置fill
属性来选择填充策略。- 填充处理器需要在 Spring Boot 中声明为
@Component
或@Bean
。- 使用
strictInsertFill
或strictUpdateFill
方法可以根据注解FieldFill.xxx
、字段名和字段类型来区分填充逻辑。- 如果不需区分,可以使用
fillStrategy
方法。- 在
update(T entity, Wrapper<T> updateWrapper)
时,entity
不能为空,否则自动填充失效。- 在
update(Wrapper<T> updateWrapper)
时不会自动填充,需要手动赋值字段条件。
select(字段是否进行 select 查询)
指示在执行查询操作时,该字段是否应该包含在 SELECT 语句中。这个属性允许您控制查询结果中包含哪些字段,从而提供更细粒度的数据访问控制。
当 select 属性设置为 true(默认值)时,该字段将包含在查询结果中。
当 select 属性设置为 false 时,即使该字段存在于数据库表中,它也不会包含在查询结果中。这在需要保护敏感数据或优化查询性能时非常有用。
@TableField 注解的 select 属性仅影响 MyBatis-Plus 生成的查询语句,不会影响其他框架或手动编写的 SQL 语句。此外,如果使用了 select = false 的字段,那么在自定义查询或使用其他方式访问该字段时,需要特别注意数据的安全性和完整性。
keepGlobalFormat(是否保持使用全局的 columnFormat 的值)
指示在处理字段时是否保持使用全局 DbConfig 中定义的 columnFormat规则。这个属性用于控制字段值在数据库操作中是否应用全局的列格式化规则。
jdbcType(JDBC类型 )
JDBC类型,用于指定字段在数据库中的数据类型。这个属性允许您显式地设置字段的数据库类型,以确保与数据库的兼容性,特别是在处理特殊类型或自定义类型时。
jdbcType 属性如果不与 @TableName 中的 autoResultMap = true 一起使用,也仅在 update 操作时生效。
当 jdbcType 属性设置为 JdbcType.UNDEFINED(默认值)时,MyBatis-Plus 将根据字段的 Java 类型自动推断其 JDBC 类型。
当 jdbcType 属性设置为特定的 JdbcType 枚举值时,该字段将使用指定的 JDBC 类型进行数据库操作。这可以用于解决类型映射问题,或者在需要精确控制数据库类型时使用。
示例
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import org.apache.ibatis.type.JdbcType;
@TableName("custom_type")
public class CustomType {
// 其他字段...
@TableField(value = "my_custom_field", jdbcType = JdbcType.VARCHAR)
private MyCustomType myCustomField;
// 构造函数、getter 和 setter...
}
/**
* JDBC数据类型的枚举类。
* 提供了Java数据类型与SQL数据类型之间的一对一映射。
*/
public enum JdbcType {
/*
* 添加此条目以支持ARRAY数据类型的基本功能 - 但仍需要自定义类型处理器
*/
ARRAY(Types.ARRAY),
// 表示位数据类型
BIT(Types.BIT),
// 表示小整数数据类型
TINYINT(Types.TINYINT),
// 表示短整数数据类型
SMALLINT(Types.SMALLINT),
// 表示整数数据类型
INTEGER(Types.INTEGER),
// 表示长整数数据类型
BIGINT(Types.BIGINT),
// 表示浮点数数据类型
FLOAT(Types.FLOAT),
// 表示单精度浮点数数据类型
REAL(Types.REAL),
// 表示双精度浮点数数据类型
DOUBLE(Types.DOUBLE),
// 表示数值数据类型
NUMERIC(Types.NUMERIC),
// 表示十进制数据类型
DECIMAL(Types.DECIMAL),
// 表示字符数据类型
CHAR(Types.CHAR),
// 表示可变长度字符数据类型
VARCHAR(Types.VARCHAR),
// 表示长可变长度字符数据类型
LONGVARCHAR(Types.LONGVARCHAR),
// 表示日期数据类型
DATE(Types.DATE),
// 表示时间数据类型
TIME(Types.TIME),
// 表示时间戳数据类型
TIMESTAMP(Types.TIMESTAMP),
// 表示二进制数据类型
BINARY(Types.BINARY),
// 表示可变长度二进制数据类型
VARBINARY(Types.VARBINARY),
// 表示长可变长度二进制数据类型
LONGVARBINARY(Types.LONGVARBINARY),
// 表示空值数据类型
NULL(Types.NULL),
// 表示其他数据类型
OTHER(Types.OTHER),
// 表示BLOB数据类型
BLOB(Types.BLOB),
// 表示CLOB数据类型
CLOB(Types.CLOB),
// 表示布尔数据类型
BOOLEAN(Types.BOOLEAN),
// 表示游标数据类型(Oracle)
CURSOR(-10),
// 表示未定义的数据类型
UNDEFINED(Integer.MIN_VALUE + 1000),
// 表示可变长度Unicode字符数据类型(JDK6)
NVARCHAR(Types.NVARCHAR),
// 表示固定长度Unicode字符数据类型(JDK6)
NCHAR(Types.NCHAR),
// 表示长可变长度Unicode字符数据类型(JDK6)
NCLOB(Types.NCLOB),
// 表示结构化数据类型
STRUCT(Types.STRUCT),
// 表示Java对象数据类型
JAVA_OBJECT(Types.JAVA_OBJECT),
// 表示区分类型
DISTINCT(Types.DISTINCT),
// 表示引用类型
REF(Types.REF),
// 表示数据链接类型
DATALINK(Types.DATALINK),
// 表示行ID类型(JDK6)
ROWID(Types.ROWID),
// 表示长可变长度Unicode字符数据类型(JDK6)
LONGNVARCHAR(Types.LONGNVARCHAR),
// 表示SQL XML数据类型(JDK6)
SQLXML(Types.SQLXML),
// 表示带时区的时间数据类型(JDBC 4.2 JDK8)
TIME_WITH_TIMEZONE(Types.TIME_WITH_TIMEZONE),
// 表示带时区的时间戳数据类型(JDBC 4.2 JDK8)
TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE);
/**
* JDBC类型代码。
*/
public final int TYPE_CODE;
/**
* 用于根据类型代码查找JdbcType的映射表。
*/
private static final Map<Integer, JdbcType> codeLookup = new HashMap<>();
/**
* 静态初始化块,用于填充codeLookup映射表。
*/
static {
for (JdbcType type : JdbcType.values()) {
codeLookup.put(type.TYPE_CODE, type);
}
}
/**
* 构造函数,初始化JdbcType实例。
*
* @param code JDBC类型代码
*/
JdbcType(int code) {
this.TYPE_CODE = code;
}
/**
* 根据给定的类型代码返回对应的JdbcType实例。
*
* @param code JDBC类型代码
* @return 对应的JdbcType实例
*/
public static JdbcType forCode(int code) {
return codeLookup.get(code);
}
}
typeHandler(类型处理器)
类型处理器,用于指定在数据库操作中如何处理特定字段的值。这个属性允许您自定义字段值的转换逻辑,以适应特定的数据类型或业务需求。比如json数据。
typeHandler 属性如果不与 @TableName 中的 autoResultMap = true 一起使用,也仅在 update 操作时生效。
如果字段类型和设置的值类型是 equals 关系,则只需确保 typeHandler 被 MyBatis 加载到,不需要使用注解。
当 typeHandler 属性未设置(即使用默认值 UnknownTypeHandler.class)时,MyBatis-Plus 将使用默认的类型处理器来处理字段值。
当 typeHandler 属性设置为特定的 TypeHandler 子类时,该字段将使用指定的类型处理器进行数据库操作。这可以用于处理自定义类型、特殊数据格式或非标准的数据库类型。
示例(json数据处理)
MyBatis-Plus 内置了多种 JSON 类型处理器,包括 AbstractJsonTypeHandler
及其子类 Fastjson2TypeHandler
、FastjsonTypeHandler
、GsonTypeHandler
、JacksonTypeHandler
等。这些处理器可以将 JSON 字符串与 Java 对象相互转换。
@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {
private Long id;
...
/**
* 必须开启映射注解
*
* @TableName(autoResultMap = true)
*
* 选择对应的 JSON 处理器,并确保存在对应的 JSON 解析依赖包
*/
@TableField(typeHandler = JacksonTypeHandler.class)
// 或者使用 FastjsonTypeHandler
// @TableField(typeHandler = FastjsonTypeHandler.class)
private OtherInfo otherInfo;
}
在 XML 映射文件中,可以使用 <result>
元素来指定字段的类型处理器。
<!-- 单个字段的类型处理器配置 -->
<result column="other_info" jdbcType="VARCHAR" property="otherInfo" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
<!-- 多个字段中某个字段的类型处理器配置 -->
<resultMap id="departmentResultMap" type="com.baomidou...DepartmentVO">
<result property="director" column="director" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
</resultMap>
<select id="selectPageVO" resultMap="departmentResultMap">
select id,name,director from department ...
</select>
从 MyBatis-Plus 3.5.3.2 版本开始,可以在 Wrapper 查询中直接使用 TypeHandler。
Wrappers.<H2User>lambdaQuery()
.apply("name={0,typeHandler=" + H2userNameJsonTypeHandler.class.getCanonicalName() + "}", "{\"id\":101,\"name\":\"Tomcat\"}"))
也可以自定义一个处理类
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.postgresql.util.PGobject;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 自定义Jsonb类型处理器,用于处理PostgreSQL数据库中的jsonb类型数据
* 该类型处理器继承自JacksonTypeHandler,利用Jackson库进行JSON序列化和反序列化
*
* @param <T> 泛型参数,表示可以处理的Java类型
*/
@MappedTypes({Object.class})
@MappedJdbcTypes(JdbcType.VARCHAR)
public class JsonbTypeHandler<T> extends JacksonTypeHandler<T> {
/**
* 泛型类型类
*/
private final Class<T> clazz;
/**
* 构造方法,初始化类型处理器
*
* @param clazz 泛型类型类,不可为null
* @throws IllegalArgumentException 如果clazz为null,则抛出该异常
*/
public JsonbTypeHandler(Class<T> clazz) {
if (clazz == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.clazz = clazz;
}
/**
* 自3.5.6版本开始支持泛型,需要加上此构造.
*
* @param type 泛型类型类
* @param field 字段
*/
public JsonbTypeHandler(Class<?> type, Field field) {
super(type, field);
}
/**
* 设置非空参数到PreparedStatement中
* 该方法重写自 superclass,专门用于处理jsonb类型数据的存储
*
* @param ps PreparedStatement对象,用于执行SQL语句
* @param i 参数索引
* @param parameter 参数值,类型为泛型T
* @param jdbcType JDBC类型,对于该方法固定为VARCHAR
* @throws SQLException 如果设置参数过程中发生SQL异常
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
// 创建PostgreSQL的jsonb对象
PGobject jsonbObject = new PGobject();
jsonbObject.setType("jsonb");
// 将参数转换为JSON字符串并设置到jsonb对象中
jsonObject.setValue(toJson(parameter));
// 将jsonb对象设置到PreparedStatement中
ps.setObject(i, jsonbObject);
}
}
使用自定义方法
@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {
private Long id;
...
/**
* 使用自定义的 JSONB 类型处理器
*/
@TableField(typeHandler = JsonbTypeHandler.class)
private OtherInfo otherInfo;
}
numericScale(指定小数点后保留的位数)
指定小数点后保留的位数,该属性仅在执行 update 操作时生效。它用于控制数值类型字段在更新时的小数精度。
numericScale 属性仅在 update 操作时生效。
- 当 numericScale 属性设置为空字符串(默认值)时,字段的小数精度将遵循数据库的默认设置或字段定义时的设置。
- 当 numericScale 属性设置为特定的数值(如 “2”)时,该字段在执行 update 操作时将按照指定的小数位数进行处理。
示例说明
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("product")
public class Product {
// 其他字段...
@TableField(value = "price", numericScale = "2")
private BigDecimal price;
// 构造函数、getter 和 setter...
}
@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 会自动处理这一过程。
@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注解的配置将数据库中的值转换为对应的枚举实例。
@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 会自动处理这一过程。
@KeySequence(Oracle 数据库中序列(Sequence)的名称)
该注解用于指定 Oracle 数据库中序列(Sequence)的名称,以便在实体类中生成主键值。在 Oracle 数据库中,主键通常是通过序列来生成的,而不是像其他数据库那样使用自增字段。@KeySequence注解告诉 MyBatis-Plus 使用特定的序列来生成主键。
@TableName("sys_user")
@KeySequence("SEQ_USER_ID") // 指定序列名称为 "SEQ_USER_ID"
public class User {
@TableId(type = IdType.INPUT) // 使用序列生成主键
private Long id;
@TableField("nickname") // 映射到数据库字段 "nickname"
private String name;
private Integer age;
private String email;
}
在上面的示例中,@KeySequence注解被应用于实体类User,并指定了序列名称为”SEQ_USER_ID”。这意味着在插入新记录时,MyBatis-Plus 将使用这个序列来生成id字段的值。
@KeySequence注解的value属性用于指定序列的名称,而dbType属性用于指定数据库类型。如果未指定dbType,MyBatis-Plus 将默认使用注入的IKeyGenerator实现。如果有多个IKeyGenerator实现,则必须指定dbType。
使用@KeySequence注解可以确保在 Oracle 数据库中正确地生成主键值,同时简化了主键生成的配置过程。开发者无需手动编写获取序列值的代码,MyBatis-Plus 会自动处理这一过程。
@InterceptorIgnore(忽略特定的插件)
该注解用于指定Mapper
的某个method
(注解在method
上)或者所有method
(注解在Mapper
上)在执行时是否忽略特定的插件(比如多租户)
// @InterceptorIgnore(tenantLine = "1") // 忽略多租户拦截器
public interface UserMapper extends BaseMapper<User> {
@InterceptorIgnore(tenantLine = "1") // 忽略多租户拦截器
List<User> selectUsers();
}
在上面的示例中,在执行selectUsers
方法时,多租户拦截器将被忽略
MyBatis-Plus 提供的插件在注解里都有对应的属性,比如多租户插件为tenantLine
属性,如果属性对应的值为”1”、“yes”、“on”,则表示对应的插件将被忽略,如果属性对应的值为”0”、“false”、“off”或为空,则插件将正常执行。
插件使用方式详见插件主体。
@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注解可以简化代码,避免在每次查询时都显式指定排序条件,同时提供了一种默认的排序方式,有助于提高代码的可读性和维护性。
最后,给大家讲个故事
不攀有钱人 我花不到他的钱 不小瞧穷人 他不靠我生存 不奉承小人 他入不了我的眼 我只相信三种人:陪我同甘共苦的人 落难帮我一把的人 今天疯狂星期四给我买肯德基的人
我大抵是不想上班 电脑里的字 横竖都看不清 侧卧是我 托腮也是我 这烦躁是没来由的 泡一杯洋人的咖啡 香气扑鼻 才勉强从雾气里看出四个字:“今天肯德基疯狂星期四谁请我吃?”
我都懂,我都明白,我是选项E,我是planB,是分叉的头发,洗衣机流出的泡沫,超市里被捏碎的饼干,是吃腻的奶油,是落寞的城市,是地上的草,我是被踩踏的,是西装的备用扣,是被雨淋湿的小狗,是腐烂的橘子,是过期的牛奶,是断线的风筝,是被随意丢弃的向日葵,是沉默寡言的小朋友,所以能v我52吗,今天肯德基疯狂星期四。
暗夜里,他的声音如恶魔般响起:“替我生个孩子!”他是尊贵的商界帝王,翻手为云,覆手为雨,而她只是他挑中的一枚棋子。十个月后,她被迫生下一个孩子逃之夭夭……当她再次出现时,她是落魄的小公司负责人,而他掌握她的生死。他强势夺情,“该死的女人,不想破产的话,今天就请我吃疯狂星期四!
独自一人远离家乡在广东打工,同事欺我,老板骂我,顾客打我,我流浪在上下九街头,衣衫褴褛,身无分文,活得不如一条流浪狗。天地浩大,却没有我的容身之处。我想问一问苍天,今天肯德基疯狂星期四,谁请我吃?
“我有点想你,你呢?” 前女友刚刚给我发来了这条消息,忽然间有些恍惚。 好像我们还在一起。那三年里,我们一起放羊,一起喂猪,一起下地插秧。她亲手制作的那一大束大蒜花捧美如繁星。我难以忘记,我们分开的那一天,我发出的最后一条信息:今天肯德基疯狂星期四,请vivo50?