关于java:详解-MyBatis-类型处理器让你的代码更优雅

21次阅读

共计 5625 个字符,预计需要花费 15 分钟才能阅读完成。

起源:https://www.cnblogs.com/zwwhn…

本篇次要解说在 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();
    }
}

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

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.EnumTypeHandlerorg.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>

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

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿(2022 最新版)

2. 劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4. 别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0