java常量数据用枚举封装(六)mybatis优雅处理枚举类-typeHandler

前言

上篇文章讲了Mybatis中的EnumOrdinalTypeHandler的使用
但该处理类无法完全通用,对枚举类的限制较多
所以就有了这篇文章,使用自定义typeHandler,自定义枚举类处理规则。

问题处理

自定义TypeHandler

TypeHandler职责

mybatis的TypeHandler的职责在于:
封装数据进preparedStatement
对查询结果的数据进行转换
当mybatis在设置sql参数,或者是封装查询结果集时,会判断当前要设置进去的对象是否有配置对应的TypeHandler
如果有,则使用该TypeHandlersetNonNullParameter方法进行参数设置。
无,则使用默认TypeHandler
枚举类的默认TypeHandler会直接使用枚举数据的名称进行封装
前文设置的EnumOrdinalTypeHandler,则是使用ordinal进行封装。
要注意的是,mybatis调用 TypeHandler 时,设置sql参数时,传递进setNonNullParameter的数据一定是非空的,但封装查询结果集则可能为空,要注意判空处理。
看方法名称即可判别

自定义TypeHandler

mybatis提供了BaseTypeHandler,自定义TypeHandler继承该类,实现方法即可。

package com.will.cn.handler;

import com.will.cn.constants.enums.UserStatus;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @author jeff
 */
public class UserStatusEnumsTypeHandler extends BaseTypeHandler<UserStatus> {

    public UserStatusEnumsTypeHandler(){
    }


    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, UserStatus parameter, JdbcType jdbcType) throws SQLException {
        System.out.println("正在设置PreparedStatement参数:"+parameter+",value:"+parameter.getValue());
        ps.setInt(i, parameter.getValue());
    }

    UserStatus getValue(int value) {
        System.out.println("正在转换结果集数据:"+value);
        UserStatus[] values = UserStatus.values();
        for (UserStatus userStatus : values) {
            if (value==userStatus.getValue()){
                System.out.println("已匹配到数据:"+userStatus);
                return userStatus;
            }
        }
//        TODO 如果找不到数据,是否抛出异常?或者是否要给个异常值?
        System.out.println("未获取到正确数据,需要检查数据库内容");
        return null;
    }

    @Override
    public UserStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {
        int i =rs.getInt(columnName);
        if (0==i && rs.wasNull()) return null;
        return getValue(i);
    }

    @Override
    public UserStatus getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        int i =rs.getInt(columnIndex);
        if (0==i && rs.wasNull()) return null;
        return getValue(i);
    }

    @Override
    public UserStatus getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        int i =cs.getInt(columnIndex);
        if (0==i && cs.wasNull()) return null;
        return getValue(i);
    }
}
注册TypeHandler
import com.will.cn.constants.enums.UserStatus;
import com.will.cn.handler.UserStatusEnumsTypeHandler;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;

/**
 * @author jeff
 * @time 2023/4/30 22:19
 */
@Configuration
public class MybatisConfiguration implements ApplicationContextAware {


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //从spring容器获取sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = applicationContext.getBean(SqlSessionFactory.class);
        //获取typeHandler注册器
        TypeHandlerRegistry typeHandlerRegistry = sqlSessionFactory.getConfiguration().getTypeHandlerRegistry();
        //注册List的typeHandler
        typeHandlerRegistry.register(UserStatus.class, UserStatusEnumsTypeHandler.class);
    }
}
测试
数据准备

这里我准备了四条数据,两条正常,一条空,一条无法匹配的内容
在这里插入图片描述

sql语句请添加图片描述
测试结果

在这里插入图片描述

测试结果-数据设置

可以看到,使用了preparedStatement的查询,执行了setNonNullParameter
而第三个查询中,虽然也是preparedStatement语句,但因为UserStatus参数为空,因此未调用setNonNullParameter方法。

测试结果-数据封装

再看数据封装的内容,从最后一个查询中,可以看到mybatis为结果中每条数据的每个UserStatus,都调用了getNullableResult方法,至于具体是哪个,就不深究了。
可以看到的是,由于我们在getNullableResult中,获取查询结果的数据时,使用的是getInt方法,当结果数据为空时,int默认值为0,所以需要额外判断是否为Null

wasNull的坑

但是!!跟踪源码发现,wasNull这个方法,如果不先尝试获取数据,mysql-connector是不会初始化该数据的的,所以他的值就是boolean的默认值,false,所以,这里一定要先get,再判空!!!!
附上connector源码源码片段
在这里插入图片描述

然后看数据库中值为3的数据,由于未匹配到数据,最终返回了null值,这也体现在了最终返回前端的内容上。

总结

TypeHandler的基本内容到这里就结束了

数据封装问题

null值

数据库结果为null时,个人认为,常量数据,一般在设计数据库时,会规定为非空,所以null一般不用担心。

异常数据问题

这个问题,个人认为一般情况下也不会产生,因为数据的产生也是经过业务代码处理的,一般要么是空值(无法过数据库非空校验),要么就是自行在业务代码中设定的值。
所以一般情况下不会出现异常数据问题,但如果真的出现了异常,比如人为添加数据时产生了偏差,那就自行根据业务代码,判断是返回空,还是抛异常了。
不过,个人认为最合理的解决方法是,在枚举类中,定义一个通用的异常枚举,当获取不到匹配项时,返回异常枚举即可。

通用化TypeHandler

和前文提到的ConventerDeserialzerSerializer一样,我们不可能为每一个枚举,都写一套完善的转换规则。
所以就有了通用化的需求。
有空再写吧~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值