详解 MyBatis 类型处理器,让你的代码更优雅!

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 详解 MyBatis 类型处理器,让你的代码更优雅!

本篇主要讲解在MyBatis中如何使用类型处理器。


1. 明确需求


在设计之初,sys_role表的enabled字段有2个可选值,其中0 代表禁用,1代表启用,而且实体类中我们使用的是Interger类型:


/**
 * 有效标志
 */
private Integer enabled;
public Integer getEnabled() {
    return enabled;
}
public void setEnabled(Integer enabled) {
    this.enabled = enabled;
}


如果要新增或者更新角色信息,我们肯定要校验enabled字段的值必须是0或者1,所以最初的部分代码可能是这样的:


if (sysRole.getEnabled() == 0 || sysRole.getEnabled() == 1) {
     sysRoleMapper.updateById(sysRole);
     sysRole = sysRoleMapper.selectById(2L);
     Assert.assertEquals(0, sysRole.getEnabled());
} else {
     throw new Exception("无效的enabled值");
}


这种硬编码的方式不仅看起来不友好,而且不利于后期维护,如果维护的程序员脾气不好,还会骂你,哈哈。


所以我们的需求就是,拒绝硬编码,使用友好的编码方式来校验enabled字段的值是否有效。


2. 使用MyBatis提供的枚举类型处理器


我们通常会使用枚举来解决这种场景。


首先新建com.zwwhnly.mybatisaction.type包,然后在该包下新建枚举Enabled:


package com.zwwhnly.mybatisaction.type;
public enum Enabled {
    /**
     * 禁用
     */
    disabled,
    /**
     * 启用
     */
    enabled;
}


其中,disabled对应的索引为0,enabled对应的索引为1。


然后将SysRole类中原来为Integer类型的enabled字段修改为:


/**
 * 有效标志
 */
private Enabled enabled;
public Enabled getEnabled() {
    return enabled;
}
public void setEnabled(Enabled enabled) {
    this.enabled = enabled;
}


此时原本硬编码的代码就可以修改为:


if (sysRole.getEnabled() == Enabled.disabled || sysRole.getEnabled() == Enabled.enabled) {
    sysRoleMapper.updateById(sysRole);
    sysRole = sysRoleMapper.selectById(2L);
    Assert.assertEquals(Enabled.disabled, sysRole.getEnabled());
} else {
    throw new Exception("无效的enabled值");
}


虽然上面的代码很完美的解决了硬编码的问题,但此时又引出一个新的问题:


数据库并不能识别Enabled枚举类型,在新增,更新或者作为查询条件时,需要将枚举值转换为数据库中的int类型,在查询数据时,需要将数据库的int类型的值转换为Enabled枚举类型。


带着这个问题,我们在SysRoleMapperTest测试类中添加如下测试方法:


@Test
public void testUpdateById() {
    SqlSession sqlSession = getSqlSession();
    try {
        SysRoleMapper sysRoleMapper = sqlSession.getMapper(SysRoleMapper.class);
        // 先查询出id=2的角色,然后修改角色的enabled值为disabled
        SysRole sysRole = sysRoleMapper.selectById(2L);
        Assert.assertEquals(Enabled.enabled, sysRole.getEnabled());
        // 修改角色的enabled为disabled
        sysRole.setEnabled(Enabled.disabled);
        if (sysRole.getEnabled() == Enabled.disabled || sysRole.getEnabled() == Enabled.enabled) {
            sysRoleMapper.updateById(sysRole);
            sysRole = sysRoleMapper.selectById(2L);
            Assert.assertEquals(Enabled.disabled, sysRole.getEnabled());
        } else {
            throw new Exception("无效的enabled值");
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        sqlSession.close();
    }
}


运行测试代码,发现抛出如下异常:


10.png


Error querying database. Cause: org.apache.ibatis.executor.result.ResultMapException: Error attempting to get column 'enabled' from result set. Cause: java.lang.IllegalArgumentException: No enum constant com.zwwhnly.mybatisaction.type.Enabled.1


这是因为MyBatis在处理Java类型和数据库类型时,使用TypeHandler(类型处理器)对这两者进行转换。


MyBatis为Java类型和数据库JDBC中的常用类型类型提供了TypeHandler接口的实现。


MyBatis在启动时会加载所有的JDBC对应的类型处理器,在处理枚举类型时默认使用org.apache.ibatis.type.EnumTypeHandler处理器,这个处理器会将枚举类型转换为字符串类型的字面值使用,对于Enabled枚举来说,就是“disabled"和”enabled"字符串。


而数据库中enabled字段的类型是int,所以在查询到角色信息将int类型的值1转换为Enabled类型报错。


那么如何解决这个问题呢?


MyBatis还提供了另一个枚举处理器:org.apache.ibatis.type.EnumOrdinalTypeHandler,这个处理器使用枚举的索引进行处理,可以解决此处转换报错的问题。


使用这个处理器,需要在之前的resources/mybatis-config.xml中添加如下配置:


<typeHandlers>
    <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
                 javaType="com.zwwhnly.mybatisaction.type.Enabled"/>
</typeHandlers>


再次运行测试代码,测试通过,输出日志如下:


DEBUG [main] - ==> Preparing: SELECT id,role_name,enabled,create_by,create_time FROM sys_role WHERE id = ?
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <== Columns: id, role_name, enabled, create_by, create_time
TRACE [main] - <== Row: 2, 普通用户, 1, 1, 2019-06-27 18:21:12.0
DEBUG [main] - <== Total: 1
DEBUG [main] - ==> Preparing: UPDATE sys_role SET role_name = ?,enabled = ?,create_by=?, create_time=? WHERE id=?
DEBUG [main] - ==> Parameters: 普通用户(String), 0(Integer), 1(Long), 2019-06-27 18:21:12.0(Timestamp), 2(Long)
DEBUG [main] - <== Updates: 1


从日志中可以看出,在查询角色信息时,MyBatis将1转换为了Enabled.enabled,在更新角色信息时,MyBatis将Enabled.disabled转换为了0。


3. 使用自定义的类型处理器


假设enabled字段的值既不是枚举的字面值,也不是枚举的索引值,此时org.apache.ibatis.type.EnumTypeHandler和org.apache.ibatis.type.EnumOrdinalTypeHandler都不能满足我们的需求,这种情况下我们就需要自己来实现类型处理器了。


首先修改下枚举类Enabled代码:


package com.zwwhnly.mybatisaction.type;
public enum Enabled {
    /**
     * 启用
     */
    enabled(1),
    /**
     * 禁用
     */
    disabled(0);
    private final int value;
    private Enabled(int value) {
        this.value = value;
    }
    public int getValue() {
        return value;
    }
}


然后在com.zwwhnly.mybatisaction.type包下新建类型处理器EnabledTypeHandler:


package com.zwwhnly.mybatisaction.type;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
/**
 * Enabled类型处理器
 */
public class EnabledTypeHandler implements TypeHandler<Enabled> {
    private final Map<Integer, Enabled> enabledMap = new HashMap<Integer, Enabled>();
    public EnabledTypeHandler() {
        for (Enabled enabled : Enabled.values()) {
            enabledMap.put(enabled.getValue(), enabled);
        }
    }
    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, Enabled enabled, JdbcType jdbcType) throws SQLException {
        preparedStatement.setInt(i, enabled.getValue());
    }
    @Override
    public Enabled getResult(ResultSet resultSet, String s) throws SQLException {
        Integer value = resultSet.getInt(s);
        return enabledMap.get(value);
    }
    @Override
    public Enabled getResult(ResultSet resultSet, int i) throws SQLException {
        Integer value = resultSet.getInt(i);
        return enabledMap.get(value);
    }
    @Override
    public Enabled getResult(CallableStatement callableStatement, int i) throws SQLException {
        Integer value = callableStatement.getInt(i);
        return enabledMap.get(value);
    }
}


自定义类型处理器实现了TypeHandler接口,重写了接口中的4个方法,并且在无参构造函数中遍历了枚举类型Enabled并对字段enabledMap进行了赋值。


想要使用自定义的类型处理器,也需要在resources/mybatis-config.xml中添加如下配置:


<typeHandlers>
    <!--其他配置-->
    <typeHandler handler="com.zwwhnly.mybatisaction.type.EnabledTypeHandler"
                 javaType="com.zwwhnly.mybatisaction.type.Enabled"/>
</typeHandlers>


运行测试代码,输出日志和上面的输出日志一样,这里不再重复贴出。


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
6月前
|
Java 关系型数据库 数据库连接
Mybatis+MySQL动态分页查询数据经典案例(含代码以及测试)
Mybatis+MySQL动态分页查询数据经典案例(含代码以及测试)
|
2月前
|
XML Java 数据库连接
mybatis plus模板快速生成代码
MybatisX 是一款提升开发效率的 IDEA 插件,尤其适用于处理多表情况。通过 MybatisX-Generator,用户能轻松生成实体类、服务类、Mapper 接口及 XML 文件,显著减少工作量。安装步骤简便:通过 File -&gt; Settings -&gt; Plugins -&gt; Browse Repositories 完成搜索与安装流程。集成数据库后,右键所需操作的数据表,选择 MyBatisX-Generator 进行自动化代码生成。更多细节可参考相关教程。
73 0
|
9天前
|
SQL Java 数据库连接
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。本文讲解了最新版MP的使用教程,包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段等核心功能。
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
|
1月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
116 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
28天前
|
搜索推荐 Java 数据库连接
Java|在 IDEA 里自动生成 MyBatis 模板代码
基于 MyBatis 开发的项目,新增数据库表以后,总是需要编写对应的 Entity、Mapper 和 Service 等等 Class 的代码,这些都是重复的工作,我们可以想一些办法来自动生成这些代码。
30 6
|
1月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
333 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
|
1月前
|
SQL Java 数据库连接
mybatis使用四:dao接口参数与mapper 接口中SQL的对应和对应方式的总结,MyBatis的parameterType传入参数类型
这篇文章是关于MyBatis中DAO接口参数与Mapper接口中SQL的对应关系,以及如何使用parameterType传入参数类型的详细总结。
37 10
|
1月前
|
SQL Java 数据库连接
Mybatis中传入不同类型的值处理方案
这篇文章讲述了在Mybatis中如何处理传入不同类型参数的情况,包括单个值、列表及Map等,并提供了相应的XML映射和Java代码示例。
79 0
|
5月前
|
SQL 前端开发 Java
我这样写代码,比直接使用 MyBatis 效率提高了 100 倍
Mybatis Hibernate 等都是我们常用的 ORM, 它们有时候很好用,但某些场景下也很繁琐,比如下文要讲的一个需求,最后本文会给出比直接用这些 ORM 开发效率至少提高 100 倍的方法...
55 1
我这样写代码,比直接使用 MyBatis 效率提高了 100 倍
|
5月前
|
Java 数据库连接 Android开发
SSM框架——使用MyBatis Generator自动创建代码
SSM框架——使用MyBatis Generator自动创建代码
51 2