Mybatis枚举类型处理和类型处理器

专栏精选

引入Mybatis

Mybatis的快速入门

Mybatis的增删改查扩展功能说明

mapper映射的参数和结果

Mybatis复杂类型的结果映射

Mybatis基于注解的结果映射

Mybatis枚举类型处理和类型处理器

再谈动态SQL

Mybatis配置入门

Mybatis行为配置之Ⅰ—缓存

Mybatis行为配置之Ⅱ—结果相关配置项说明

Mybatis行为配置之Ⅲ—其他行为配置项说明

Mybatis行为配置之Ⅳ—日志

Mybatis整合Spring详解

Mybatis插件入门

Mybatis专栏代码资源

摘要

在这篇文章中,我们将进入Mybatis类型转换器的世界,了解Mybatis中如何使用枚举类型和Mybatis类型转换器的基本用法,其中的很多观点或内容都能在一定程度上让我们的开发之旅更加轻松方便,这是一个菜鸟提升技术能力,老鸟巩固基础知识的好机会。准备好开启今天的神奇之旅了吗?

引言

大家好,我是奇迹老李,一个专注于分享开发经验和基础教程的博主。这里是我的其中一个技术分享平台,欢迎广大朋友们点赞评论提出意见,重要的是点击关注喔 🙆。今天要和大家分享的内容是枚举类型处理和类型处理器。做好准备,Let’s go🚎🚀

正文

首图

枚举类型映射

简单枚举映射

如果需要返回枚举类型的查询结果,如果返回值和枚举值一一对应,可以直接使用枚举类型接收返回结果。

新增字典数据

INSERT INTO dict_test (dict_name, dict_code, dict_type, dict_sort) VALUES ('NONE', '1', 'app_auth_type', 0);
INSERT INTO dict_test (dict_name, dict_code, dict_type, dict_sort) VALUES ('MOBILE', '2', 'app_auth_type', 2);
INSERT INTO dict_test (dict_name, dict_code, dict_type, dict_sort) VALUES ('WECHAT', '3', 'app_auth_type', 3);
INSERT INTO dict_test (dict_name, dict_code, dict_type, dict_sort) VALUES ('QQ', '4', 'app_auth_type', 4);

新增枚举类

public enum AuthType {  
    NONE,WECHAT,QQ,MOBILE;  
}

mapper映射

AuthType getAuthType(@Param("code")String code);
<select id="getAuthType" resultType="top.sunyog.common.entity.AuthType">  
    select dict_name from dict_test where dict_type='app_auth_type' and dict_code=#{code}  
</select>

测试类

private void testEnumResultService(SimpleQueryMapper mapper){  
    AuthType authType = mapper.getAuthType("3");  
    System.out.println(authType);  
}

打印结果

WECHAT

枚举顺序映射

mybatis内置了EnumOrdinalTypeHandler类型处理器,来实现字典顺序号和枚举类型之间的映射。注意枚举类型的顺序号从0开始。
代码示例:
mapper-xml

<resultMap id="app-auth-order" type="map">  
    <result property="auth_type" column="auth_type" javaType="top.sunyog.common.entity.AuthType" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>  
</resultMap>  
<select id="getAppAuthOrder" resultMap="app-auth-order">  
    select auth_type from app_test where id=#{id}  
</select>

mapper接口

Map<String,Object> getAppAuthOrder(@Param("id") Long id);

测试类:

public class SimpleQueryService extends MybatisService<SimpleQueryMapper>{
	private void testEnumOrdder(SimpleQueryMapper mapper) {  
	    Map<String, Object> map = mapper.getAppAuthOrder(2L);  
	    map.entrySet().forEach(o-> System.out.println(o.getKey()+": "+o.getValue()));  
	}
}

打印结果(auth_type=2)

auth_type: QQ

复杂枚举映射

对于返回值和枚举名称不对应的情况,可以使用自定义类型处理器的方式解决,
在类型处理器中处理数据库数据和枚举类型之间的对应关系

自定义类型处理器

public class AppAuthTypeHandler extends BaseTypeHandler<AppStatus> {  
    @Override  
    public void setNonNullParameter(PreparedStatement ps, int i, AppStatus parameter, JdbcType jdbcType) throws SQLException {  
        ps.setString(i,this.appStatusToString(parameter));  
    }  
  
    @Override  
    public AppStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {  
        String str = rs.getString(columnName);  
        return this.stringToAppStatus(str);  
    }  
  
    @Override  
    public AppStatus getNullableResult(ResultSet rs, int columnIndex) throws SQLException {  
        String str = rs.getString(columnIndex);  
        return this.stringToAppStatus(str);  
    }  
  
    @Override  
    public AppStatus getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {  
        String str = cs.getString(columnIndex);  
        return this.stringToAppStatus(str);  
    }  
  
    private String appStatusToString(AppStatus status){  
        switch (status){  
            case FREEZE:  
                return "冻结应用";  
            case NORMAL:  
                return "正常应用";  
            case OVERDUE:  
                return "过期应用";  
            case TEMPORARY:  
            default:  
                return "临时应用";  
        }  
    }  
  
    private AppStatus stringToAppStatus(String str){  
        switch (str){  
            case "冻结应用":  
                return AppStatus.FREEZE;  
            case "正常应用":  
                return AppStatus.NORMAL;  
            case "过期应用":  
                return AppStatus.OVERDUE;  
            default:  
                return AppStatus.TEMPORARY;  
        }  
    }  
}

定义新的结果值类型

public class AppDict {  
    private AppStatus appStatus;  
  
    public AppStatus getAppStatus() {  
        return appStatus;  
    }  
  
    public void setAppStatus(AppStatus appStatus) {  
        this.appStatus = appStatus;  
    }  
  
    @Override  
    public String toString() {  
        return "AppDict{" +  
                "appStatus=" + appStatus +  
                '}';  
    }  
}

新增mapper方法

AppDict getAppStatusEnum(@Param("code")String code);

定义映射文件,通过resultMap设置类型处理器

<resultMap id="app-status-enum" type="top.sunyog.common.entity.AppDict">  
    <result property="appStatus" column="dict_name" typeHandler="top.sunyog.mybatis.handler.AppAuthTypeHandler"/>  
</resultMap>  
<select id="getAppStatusEnum" resultMap="app-status-enum">  
    select dict_name from dict_test where dict_type='app_status' and dict_code=#{code}  
</select>

测试代码

private void testEnumStatusService(SimpleQueryMapper mapper){  
    AppDict appDict = mapper.getAppStatusEnum("1");  
    System.out.println(appDict);  
}

打印结果

AppDict{appStatus=FREEZE}

类型处理器

以上对复杂枚举映射的解决方式即是类型处理器的简单应用,在开发过程中更常见的是对LocalDateTime等事件类型的转换。

这是因为在Mybatis的早期版本中,对于日期类型的数据通常使用 Java.util.Date类型接收,如果使用 java.time.LocalDateTime类型接收该字段会造成结果值为空的情况,这时候要么升级Mybatis版本,要么通过自定义类型处理器实现

降低mybatis版本到3.4.4

<dependencies>  
    <dependency>        
	    <groupId>org.mybatis</groupId>  
        <artifactId>mybatis</artifactId>  
        <version>3.4.4</version>  
    </dependency>
</dependencies>

此时重新启动项目会报错,需要修改启动类

public class MybatisAppContext {  
    private static SqlSessionFactory sqlSessionFactory = null;  
  
    private Map<String, MybatisService> serviceMap = new ConcurrentHashMap<>();  
  
    /**  
     * 注册SqlSessionFactory  
     */    static {  
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();  
        try (InputStream in = MybatisApp.class.getResourceAsStream("/mybatis-config.xml");  
                InputStreamReader reader=new InputStreamReader(in)) {  
            sqlSessionFactory = builder.build(reader);  
        } catch (IOException e) {  
            System.out.println("文件路径读取错误");  
        }  
    }
	
	...
}

此时再启动项目仍会报错,提示没有对应的类处理器
新增类型处理器 LocalDateHandler

package top.sunyog.mybatis.handler;  
  
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;  
import java.time.LocalDate;  
  
public class LocalDateHandler extends BaseTypeHandler<LocalDate> {  
    @Override  
    public void setNonNullParameter(PreparedStatement ps, int i, LocalDate parameter, JdbcType jdbcType) throws SQLException {  
        ps.setObject(i,parameter);  
    }  
  
    @Override  
    public LocalDate getNullableResult(ResultSet rs, String columnName) throws SQLException {  
        return rs.getObject(columnName,LocalDate.class);  
    }  
  
    @Override  
    public LocalDate getNullableResult(ResultSet rs, int columnIndex) throws SQLException {  
        return rs.getObject(columnIndex,LocalDate.class);  
    }  
  
    @Override  
    public LocalDate getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {  
        return cs.getObject(columnIndex,LocalDate.class);  
    }  
}

配置文件添加配置项typeHandler

<settings .../>
<typeAliasis .../>

<typeHandlers>  
    <typeHandler handler="top.sunyog.mybatis.handler.LocalDateHandler"/>  
</typeHandlers>

<environments .../>

<mappers .../>

测试代码

public class SimpleQueryService extends MybatisService<SimpleQueryMapper>{  
    @Override  
    public void doService() {  
        SimpleQueryMapper mapper = super.getMapper(SimpleQueryMapper.class);  
        this.testHashMapParam(mapper);
    }
}

打印结果

AppTestEntity{id=5, appName='名称1', appCode='code-1', authType='2', createDate=2023-11-03, creator='admin3', appStatus='null', authTypeDict=null, appStatusDict=null, services=null}
AppTestEntity{id=6, appName='name2', appCode='code-2', authType='2', createDate=2023-11-03, creator='admin3', appStatus='null', authTypeDict=null, appStatusDict=null, services=null}
AppTestEntity{id=7, appName='jack liu', appCode='code-3', authType='2', createDate=2023-11-03, creator='admin3', appStatus='null', authTypeDict=null, appStatusDict=null, services=null}

注意:以上处理方式只能解决由于Mybatis版本原因造成的LocalDateTimeLocalDate等的类型转换失败问题。但类型转换失败有可能是数据库驱动、或连接池的版本问题造成的,实际开发过程中遇到过在Oracle数据库中ojdbc7驱动接收LocalDateTime类时间数据失败报错的问题,一般通过升级到ojdbc8都能解决。如果项目版本升级比较麻烦,可以使用Date类型接收日期时间数据,在service层再做转换或不转换,通过@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="GMT+8")注解的方式指定时区保证时间的准确性

总结

本文我们介绍了在Mybatis中如何使用枚举类型接收查询结果,并以此引入Mybatis 的类型处理器。通过日期类型处理器类认识了类型处理器的简单使用,在业务开发过程中,可以通过设计功能更强大的类型处理器来更优雅的实现各种相关业务需求。

我们在Mybatis的增删改查扩展功能说明这篇文章最后提到的疑问4和疑问5也得到了解决。


📩 联系方式
邮箱:qijilaoli@foxmail.com

❗版权声明
本文为原创文章,版权归作者所有。未经许可,禁止转载。更多内容请访问奇迹老李的博客首页

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李奇技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值