@TableField

@TableField(typeHandler = PermissionLevelTypeHandler.class) 这个注解主要用于 MyBatis-Plus 自动生成的 SQL 语句中,指定某个字段的类型处理器。它不会直接影响你自己编写的 Mapper 方法中的 SQL 语句。但是,可以通过其他方式在自定义的 Mapper 方法中使用类型处理器。

解释

  1. 自动生成的方法

    • 当你使用 MyBatis-Plus 的 BaseMapperIService 接口时,MyBatis-Plus 会自动生成一些常用的 CRUD 方法。这些方法会根据你在实体类中定义的 @TableField 注解来处理字段的类型转换。
    • 例如,@TableField(typeHandler = PermissionLevelTypeHandler.class) 会在插入、查询等操作中自动使用 PermissionLevelTypeHandler 来处理 permissionLevel 字段的类型转换。
  2. 自定义的方法

    • 如果你在 Mapper 接口中编写了自定义的 SQL 语句,这些 SQL 语句不会自动应用 @TableField 注解中指定的类型处理器。
    • 但是,你可以在自定义的 SQL 语句中手动指定类型处理器,或者在 XML 配置文件中指定类型处理器。

示例

自定义 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 注解在以下情况下生效:

  1. 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>
      
  2. 自动扫描类型处理器

    • 如果你使用 MyBatis 的自动扫描功能,@MappedTypes 注解会生效。

    • mybatis-plus:
          configuration:
              type-handlers-package: top.roozen.lab.competition.config.mybatis.handler
      

具体生效时机

  • 启动时扫描

    • 当应用程序启动时,MyBatis 或 MyBatis-Plus 会扫描指定的包路径,查找带有 @MappedTypes 注解的类型处理器,并将其注册到 MyBatis 的类型处理器注册表中。
  • 使用时匹配

    • 当 MyBatis 执行 SQL 语句时,它会根据字段的 Java 类型查找相应的类型处理器。如果找到了匹配的类型处理器(即 @MappedTypes 注解指定的类型),就会使用该类型处理器进行类型转换。

示例

假设你有一个 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 类型的字段,无需在每个字段上重复指定。