MyBatis——TypeHandler
@TableField
@TableField(typeHandler = PermissionLevelTypeHandler.class)
这个注解主要用于 MyBatis-Plus 自动生成的 SQL 语句中,指定某个字段的类型处理器。它不会直接影响你自己编写的 Mapper 方法中的 SQL 语句。但是,可以通过其他方式在自定义的 Mapper 方法中使用类型处理器。
解释
自动生成的方法:
- 当你使用 MyBatis-Plus 的
BaseMapper
或IService
接口时,MyBatis-Plus 会自动生成一些常用的 CRUD 方法。这些方法会根据你在实体类中定义的@TableField
注解来处理字段的类型转换。 - 例如,
@TableField(typeHandler = PermissionLevelTypeHandler.class)
会在插入、查询等操作中自动使用PermissionLevelTypeHandler
来处理permissionLevel
字段的类型转换。
- 当你使用 MyBatis-Plus 的
自定义的方法:
- 如果你在 Mapper 接口中编写了自定义的 SQL 语句,这些 SQL 语句不会自动应用
@TableField
注解中指定的类型处理器。 - 但是,你可以在自定义的 SQL 语句中手动指定类型处理器,或者在 XML 配置文件中指定类型处理器。
- 如果你在 Mapper 接口中编写了自定义的 SQL 语句,这些 SQL 语句不会自动应用
示例
自定义 Mapper 方法
假设你有一个自定义的 Mapper 方法,需要处理 permissionLevel
字段的类型转换:
package top.roozen.lab.competition.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import top.roozen.lab.competition.domain.entity.User;
import top.roozen.lab.competition.domain.enums.PermissionLevel;
import java.util.List;
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT * FROM user WHERE permission_level = #{permissionLevel, typeHandler=top.roozen.lab.competition.config.mybatis.handler.PermissionLevelTypeHandler}")
List<User> selectByPermissionLevel(@Param("permissionLevel") PermissionLevel permissionLevel);
}
在这个例子中,我们在 @Select
注解的 SQL 语句中手动指定了 typeHandler
属性,以确保 permissionLevel
字段的类型转换使用 PermissionLevelTypeHandler
。
使用 XML 配置
如果你更喜欢使用 XML 配置文件来编写 SQL 语句,也可以在 XML 文件中指定类型处理器:
<mapper namespace="top.roozen.lab.competition.mapper.UserMapper">
<select id="selectByPermissionLevel" resultType="top.roozen.lab.competition.domain.entity.User">
SELECT * FROM user WHERE permission_level = #{permissionLevel, typeHandler=top.roozen.lab.competition.config.mybatis.handler.PermissionLevelTypeHandler}
</select>
</mapper>
总结
@TableField(typeHandler = PermissionLevelTypeHandler.class)
主要用于 MyBatis-Plus 自动生成的 SQL 语句中。- 在自定义的 Mapper 方法中,需要手动指定类型处理器,可以在注解中或 XML 配置文件中指定。
@MappedTypes
@MappedTypes(PermissionLevel.class)
注解用于指定类型处理器(Type Handler)可以处理的 Java 类型。这个注解在 MyBatis 和 MyBatis-Plus 中用于注册类型处理器,使其能够识别并处理特定类型的字段。具体来说,@MappedTypes
注解在以下情况下生效:
MyBatis 配置文件中注册类型处理器:
如果你在 MyBatis 的配置文件(通常是
mybatis-config.xml
)中显式地注册了类型处理器,@MappedTypes
注解会生效。例如:
<typeHandlers> <typeHandler javaType="top.roozen.lab.competition.domain.enums.PermissionLevel" handler="top.roozen.lab.competition.config.mybatis.handler.PermissionLevelTypeHandler"/> </typeHandlers>
自动扫描类型处理器:
如果你使用 MyBatis 的自动扫描功能,
@MappedTypes
注解会生效。mybatis-plus: configuration: type-handlers-package: top.roozen.lab.competition.config.mybatis.handler
具体生效时机
启动时扫描:
- 当应用程序启动时,MyBatis 或 MyBatis-Plus 会扫描指定的包路径,查找带有
@MappedTypes
注解的类型处理器,并将其注册到 MyBatis 的类型处理器注册表中。
- 当应用程序启动时,MyBatis 或 MyBatis-Plus 会扫描指定的包路径,查找带有
使用时匹配:
- 当 MyBatis 执行 SQL 语句时,它会根据字段的 Java 类型查找相应的类型处理器。如果找到了匹配的类型处理器(即
@MappedTypes
注解指定的类型),就会使用该类型处理器进行类型转换。
- 当 MyBatis 执行 SQL 语句时,它会根据字段的 Java 类型查找相应的类型处理器。如果找到了匹配的类型处理器(即
示例
假设你有一个 PermissionLevelTypeHandler
类,并且使用了 @MappedTypes
注解:
package top.roozen.lab.competition.config.mybatis.handler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import top.roozen.lab.competition.domain.enums.PermissionLevel;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@MappedTypes(PermissionLevel.class)
public class PermissionLevelTypeHandler extends BaseTypeHandler<PermissionLevel> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, PermissionLevel parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.getLevel());
}
@Override
public PermissionLevel getNullableResult(ResultSet rs, String columnName) throws SQLException {
int level = rs.getInt(columnName);
return PermissionLevel.fromLevel(level);
}
@Override
public PermissionLevel getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int level = rs.getInt(columnIndex);
return PermissionLevel.fromLevel(level);
}
@Override
public PermissionLevel getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int level = cs.getInt(columnIndex);
return PermissionLevel.fromLevel(level);
}
}
并且你在 PermissionLevel
枚举类中添加了一个静态方法 fromLevel
:
package top.roozen.lab.competition.domain.enums;
public enum PermissionLevel {
SUPER_ADMIN(3),
ADMIN(2),
USER(1);
private final int level;
PermissionLevel(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
public static PermissionLevel fromLevel(int level) {
for (PermissionLevel permissionLevel : PermissionLevel.values()) {
if (permissionLevel.getLevel() == level) {
return permissionLevel;
}
}
throw new IllegalArgumentException("Unknown permission level: " + level);
}
}
然后在 User
实体类中使用 @TableField
注解:
package top.roozen.lab.competition.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import top.roozen.lab.competition.config.mybatis.handler.PermissionLevelTypeHandler;
import top.roozen.lab.competition.domain.enums.PermissionLevel;
@TableName("user")
public class User {
private Long id;
private String username;
private String password;
@TableField(typeHandler = PermissionLevelTypeHandler.class)
private PermissionLevel permissionLevel;
// Getters and Setters
}
这样,当应用程序启动时,PermissionLevelTypeHandler
会被自动扫描并注册,@MappedTypes(PermissionLevel.class)
注解会生效,使得 PermissionLevel
类型的字段在 MyBatis 处理时使用 PermissionLevelTypeHandler
进行类型转换。
总结
- 启动时扫描:当应用程序启动时,MyBatis 或 MyBatis-Plus 会扫描指定的包路径,查找带有
@MappedTypes
注解的类型处理器,并将其注册。 - 使用时匹配:当 MyBatis 执行 SQL 语句时,它会根据字段的 Java 类型查找相应的类型处理器,并使用
@MappedTypes
注解指定的类型处理器进行类型转换。
确保你的类型处理器位于 @MapperScan
指定的包路径下,并且正确配置了 @MappedTypes
注解,这样它就能在需要时生效。
@MappedTypes
和 @TableField(typeHandler = ...)
在 MyBatis 和 MyBatis-Plus 中,@MappedTypes
和 @TableField(typeHandler = ...)
注解分别用于不同的目的,因此它们的作用和使用场景有所不同。下面详细解释这两个注解的作用及其是否需要同时使用。
@MappedTypes
注解
- 作用:
@MappedTypes
注解用于指定类型处理器可以处理的 Java 类型。它告诉 MyBatis 在遇到指定类型的字段时,应该使用该类型处理器进行类型转换。 - 生效时机:当 MyBatis 或 MyBatis-Plus 启动时,会扫描带有
@MappedTypes
注解的类型处理器,并将其注册到类型处理器注册表中。 - 适用场景:适用于全局注册类型处理器,使得 MyBatis 能够自动识别并使用该类型处理器处理指定类型的字段。
@TableField(typeHandler = ...)
注解
- 作用:
@TableField
注解用于指定实体类字段的额外配置,包括类型处理器。它明确指定了某个字段应该使用哪个类型处理器进行类型转换。 - 生效时机:当 MyBatis 处理特定字段时,会根据
@TableField
注解中的typeHandler
属性来确定使用哪个类型处理器。 - 适用场景:适用于局部指定字段使用的类型处理器,特别是在需要覆盖全局配置或使用不同的类型处理器时。
是否需要同时使用
使用 @MappedTypes
的情况
如果你希望 PermissionLevelTypeHandler
能够全局处理 PermissionLevel
类型的字段,那么只需要使用 @MappedTypes
注解即可。MyBatis 会在处理 PermissionLevel
类型的字段时自动使用 PermissionLevelTypeHandler
。
package top.roozen.lab.competition.config.mybatis.handler;
import org.apache.ibatis.type.MappedTypes;
import top.roozen.lab.competition.domain.enums.PermissionLevel;
@MappedTypes(PermissionLevel.class)
public class PermissionLevelTypeHandler extends BaseEnumTypeHandler<PermissionLevel> {
public PermissionLevelTypeHandler() {
super(PermissionLevel.class);
}
}
在这种情况下,你不需要在每个 PermissionLevel
字段上使用 @TableField(typeHandler = PermissionLevelTypeHandler.class)
,MyBatis 会自动识别并使用该类型处理器。
使用 @TableField
的情况
如果你希望某个特定字段使用 PermissionLevelTypeHandler
,即使全局没有注册该类型处理器,或者你希望覆盖全局配置,可以使用 @TableField
注解。
package top.roozen.lab.competition.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import top.roozen.lab.competition.config.mybatis.handler.PermissionLevelTypeHandler;
import top.roozen.lab.competition.domain.enums.PermissionLevel;
@TableName("user")
public class User {
private Long id;
private String username;
private String password;
@TableField(typeHandler = PermissionLevelTypeHandler.class)
private PermissionLevel permissionLevel;
// Getters and Setters
}
在这种情况下,即使没有使用 @MappedTypes
注解,MyBatis 也会使用 PermissionLevelTypeHandler
处理 permissionLevel
字段。
总结
- 全局注册:如果你希望
PermissionLevelTypeHandler
能够全局处理PermissionLevel
类型的字段,只需使用@MappedTypes(PermissionLevel.class)
注解。 - 局部指定:如果你希望某个特定字段使用
PermissionLevelTypeHandler
,即使全局没有注册该类型处理器,或者你希望覆盖全局配置,可以使用@TableField(typeHandler = PermissionLevelTypeHandler.class)
注解。
推荐做法
通常情况下,推荐使用 @MappedTypes
注解进行全局注册,以简化配置并减少重复代码。只有在需要特别指定某个字段的类型处理器时,才使用 @TableField(typeHandler = ...)
注解。
例如:
// PermissionLevelTypeHandler.java
package top.roozen.lab.competition.config.mybatis.handler;
import org.apache.ibatis.type.MappedTypes;
import top.roozen.lab.competition.domain.enums.PermissionLevel;
@MappedTypes(PermissionLevel.class)
public class PermissionLevelTypeHandler extends BaseEnumTypeHandler<PermissionLevel> {
public PermissionLevelTypeHandler() {
super(PermissionLevel.class);
}
}
// User.java
package top.roozen.lab.competition.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import top.roozen.lab.competition.domain.enums.PermissionLevel;
@TableName("user")
public class User {
private Long id;
private String username;
private String password;
// 不需要再次指定 typeHandler,MyBatis 会自动使用 PermissionLevelTypeHandler
private PermissionLevel permissionLevel;
// Getters and Setters
}
这样配置后,PermissionLevelTypeHandler
会全局处理 PermissionLevel
类型的字段,无需在每个字段上重复指定。
- 感谢你赐予我前进的力量