詳解 MyBatis 類型處理器,讓你的代碼更優雅!

來源:https://www.cnblogs.com/zwwhnly/p/11238131.html

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

運行測試代碼,發現拋出如下異常:

img

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開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章