创建专用类型处理器,重写 BigDecimal 的转换逻辑:
import org.apache.ibatis.type.*;
import java.math.BigDecimal;
import java.sql.*;
// 使用注解声明处理的类型(非必须但推荐)
@MappedTypes(BigDecimal.class)
@MappedJdbcTypes(JdbcType.DECIMAL)
public class PlainBigDecimalTypeHandler extends BaseTypeHandler<BigDecimal> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, BigDecimal parameter, JdbcType jdbcType)
throws SQLException {
ps.setBigDecimal(i, parameter);
}
@Override
public BigDecimal getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return rs.getBigDecimal(columnName);
}
// 其他必要方法...
// 重点:覆盖结果转换逻辑
@Override
public Object getResult(CallableStatement cs, int columnIndex) throws SQLException {
BigDecimal value = cs.getBigDecimal(columnIndex);
return value != null ? value.toPlainString() : null; // 使用 toPlainString()
}
}
import org.apache.ibatis.session.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisConfig {
@Bean
public ConfigurationCustomizer typeHandlerRegistry() {
return configuration -> {
// 注册 BigDecimal 处理器(全局生效)
configuration.getTypeHandlerRegistry().register(PlainBigDecimalTypeHandler.class);
// 注册其他全局处理器
configuration.getTypeHandlerRegistry().register(CustomDateTypeHandler.class);
};
}
}
properties 配置文件
# application.properties
# 扫描整个包下的处理器(全局生效)
mybatis.type-handlers-package=com.example.handlers
@MappedTypes
和 @MappedJdbcTypes
注解决定<!-- UserMapper.xml -->
<resultMap id="userResult" type="User">
<!-- 其他字段... -->
<!-- 仅此字段使用自定义处理器 -->
<result column="sale_price" property="salePrice"
typeHandler="com.example.handlers.PlainBigDecimalTypeHandler"/>
</resultMap>
<select id="selectUser" resultMap="userResult">
SELECT * FROM user
</select>
public interface UserMapper {
@Results({
@Result(column = "sale_price", property = "salePrice",
typeHandler = PlainBigDecimalTypeHandler.class)
})
@Select("SELECT * FROM user")
List<User> selectAll();
}
在 MyBatis 中,若要对多个字段使用类型处理器,可在 @Results
注解里添加多个 @Result
元素。每个 @Result
注解能针对不同的字段进行单独配置。
public interface UserMapper {
@Results({
@Result(column = "sale_price", property = "salePrice",
typeHandler = PlainBigDecimalTypeHandler.class),
@Result(column = "original_price", property = "originalPrice",
typeHandler = PlainBigDecimalTypeHandler.class),
@Result(column = "discount_rate", property = "discountRate",
typeHandler = PlainBigDecimalTypeHandler.class)
})
@Select("SELECT * FROM user")
List<User> selectAll();
}
@Result
元素:在 @Results
注解的数组里,可以添加任意数量的 @Result
注解。@Result
注解都能对 column
(数据库列名)、property
(实体类属性名)以及 typeHandler
(类型处理器)进行单独配置。PlainBigDecimalTypeHandler
,可以为每个字段分别指定该处理器。public class TableAwareBigDecimalHandler extends PlainBigDecimalTypeHandler {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
BigDecimal parameter, JdbcType jdbcType) throws SQLException {
// 获取当前执行的SQL信息
MetaObject metaObject = SystemMetaObject.forObject(ps);
Statement stmt = (Statement) metaObject.getValue("delegate.statement");
String sql = stmt.toString().toLowerCase();
// 仅当是user表时才使用自定义处理
if (sql.contains("from user")) {
super.setNonNullParameter(ps, i, parameter, jdbcType);
} else {
ps.setBigDecimal(i, parameter); // 默认处理
}
}
}
@Configuration
public class MyBatisConfig {
@Bean
public ConfigurationCustomizer typeHandlerRegistry() {
return config -> {
// 注册基础处理器(全局)
config.getTypeHandlerRegistry().register(DefaultBigDecimalHandler.class);
// 注册特殊处理器(需在XML中显式使用)
config.getTypeHandlerRegistry().register("specialHandler", SpecialBigDecimalHandler.class);
};
}
}
在 XML 文件中:
<!-- 使用特殊处理器 -->
<result column="sale_price" property="salePrice" typeHandler="specialHandler"/>
@Bean
public ConfigurationCustomizer typeHandlerRegistry() {
return config -> {
TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
// 只注册特定包下的处理器
if (isFinanceModuleEnabled()) {
registry.register("com.example.handlers.finance.*");
}
// 公共处理器
registry.register(CommonDateHandler.class);
};
}
@SpringBootTest
class TypeHandlerTests {
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Test
void testHandlerRegistration() {
TypeHandlerRegistry registry = sqlSessionFactory.getConfiguration()
.getTypeHandlerRegistry();
// 验证全局处理器
assertTrue(registry.hasTypeHandler(BigDecimal.class));
// 验证特定处理器
TypeHandler<?> handler = registry.getTypeHandler(BigDecimal.class, JdbcType.DECIMAL);
assertInstanceOf(PlainBigDecimalTypeHandler.class, handler);
// 打印所有注册的处理器
registry.getTypeHandlers().forEach(h ->
System.out.println("Registered: " + h.getClass().getName()));
}
}
优先级顺序:(很重要)
避免冲突:
// 精确指定处理类型避免冲突
@MappedTypes(value = {BigDecimal.class})
@MappedJdbcTypes(value = {JdbcType.DECIMAL}, includeNullJdbcType = true)
public class FinanceBigDecimalHandler extends BaseTypeHandler<BigDecimal> {
// 仅处理财务模块的DECIMAL字段
}
模块化注册:
@Bean
@ConditionalOnProperty(name = "module.finance.enabled", havingValue = "true")
public ConfigurationCustomizer financeTypeHandlers() {
return config -> {
config.getTypeHandlerRegistry().register(FinanceBigDecimalHandler.class);
config.getTypeHandlerRegistry().register(CurrencyTypeHandler.class);
};
}
生效范围 | 实现方式 | 适用场景 |
---|---|---|
全局所有字段 | 配置类注册/包扫描 | 基础类型转换(如日期、枚举) |
特定字段 | XML/注解显式指定 | 特殊字段需要特殊处理 |
特定表/模块 | 条件注册+处理器内部逻辑判断 | 多模块项目中的差异化处理 |
特定查询类型 | 自定义Executor拦截器 | 只对SELECT查询生效等复杂场景 |
基于环境配置 | @Conditional 条件注册 | 不同环境使用不同转换逻辑 |
// 1. 创建条件处理器
@MappedTypes(BigDecimal.class)
@MappedJdbcTypes(JdbcType.DECIMAL)
public class FinanceBigDecimalHandler extends BaseTypeHandler<BigDecimal> {
private static final int FINANCE_SCALE = 4;
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
BigDecimal parameter, JdbcType jdbcType) {
// 财务字段固定4位小数
BigDecimal value = parameter.setScale(FINANCE_SCALE, RoundingMode.HALF_UP);
ps.setBigDecimal(i, value);
}
@Override
public BigDecimal getNullableResult(ResultSet rs, String columnName) {
return format(rs.getBigDecimal(columnName));
}
private BigDecimal format(BigDecimal value) {
return value != null ?
value.setScale(FINANCE_SCALE, RoundingMode.HALF_UP) : null;
}
// ... 其他方法
}
// 2. 条件注册
@Configuration
@ConditionalOnProperty(name = "app.module.finance", havingValue = "true")
public class FinanceModuleConfig {
@Bean
public ConfigurationCustomizer financeTypeHandlers() {
return config -> {
config.getTypeHandlerRegistry().register(FinanceBigDecimalHandler.class);
};
}
}
通过以上策略,可以灵活控制自定义类型处理器的生效范围,满足从全局统一处理到字段级精细控制的各种需求场景。