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

专栏精选

引入Mybatis

Mybatis的快速入门

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

mapper映射的参数和结果

Mybatis复杂类型的结果映射

Mybatis基于注解的结果映射

文章目录

摘要

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

引言

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

正文

枚举类型映射

简单枚举映射

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

新增字典数据

mysql 复制代码
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);

新增枚举类

java 复制代码
public enum AuthType {  
    NONE,WECHAT,QQ,MOBILE;  
}

mapper映射

java 复制代码
AuthType getAuthType(@Param("code")String code);
xml 复制代码
<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>

测试类

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

打印结果

cmd 复制代码
WECHAT

枚举顺序映射

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

代码示例:

mapper-xml

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接口

java 复制代码
Map<String,Object> getAppAuthOrder(@Param("id") Long id);

测试类:

java 复制代码
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)

txt 复制代码
auth_type: QQ

复杂枚举映射

对于返回值和枚举名称不对应的情况,可以使用自定义类型处理器的方式解决,

在类型处理器中处理数据库数据和枚举类型之间的对应关系

自定义类型处理器

java 复制代码
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;  
        }  
    }  
}

定义新的结果值类型

java 复制代码
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方法

java 复制代码
AppDict getAppStatusEnum(@Param("code")String code);

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

xml 复制代码
<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>

测试代码

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

打印结果

cmd 复制代码
AppDict{appStatus=FREEZE}

类型处理器

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

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

降低mybatis版本到3.4.4

xml 复制代码
<dependencies>  
    <dependency>        
	    <groupId>org.mybatis</groupId>  
        <artifactId>mybatis</artifactId>  
        <version>3.4.4</version>  
    </dependency>
</dependencies>

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

java 复制代码
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

java 复制代码
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

xml 复制代码
<settings .../>
<typeAliasis .../>

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

<environments .../>

<mappers .../>

测试代码

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

打印结果

cmd 复制代码
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

❗版权声明

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

相关推荐
奋进的芋圆1 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin1 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model20051 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉1 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国2 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
萧曵 丶2 小时前
Next-Key Lock、记录锁、间隙锁浅谈
数据库·sql·mysql·mvcc·可重复读·幻读
2501_941882482 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈2 小时前
两天开发完成智能体平台
java·spring·go
alonewolf_992 小时前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
沛沛老爹2 小时前
Java泛型擦除:原理、实践与应对策略
java·开发语言·人工智能·企业开发·发展趋势·技术原理