1. 程式人生 > >MyBatis從入門到精通(十四):在MyBatis中使用型別處理器

MyBatis從入門到精通(十四):在MyBatis中使用型別處理器

最近在讀劉增輝老師所著的《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();
    }
}

執行測試程式碼,發現丟擲如下異常:

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>

執行測試程式碼,輸出日誌和上面的輸出日誌一樣,這裡不再重複貼出。

4. 原始碼及參考

原始碼地址:https://github.com/zwwhnly/mybatis-action.git,歡迎下載。

劉增輝《MyBatis從入門到精通》

5. 最後

打個小廣告,歡迎掃碼關注微信公眾號:「申城異鄉人」,定期分享Java技術乾貨,讓我們一起進步。

相關推薦

MyBatis入門精通()MyBatis使用型別處理器

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 本篇部落格主要講解在MyBatis中如何使用型別處理器。 1. 明確需求 在設計之初,sys_role表的enabled欄位有2個可選值,其中0 代表禁用,1

MyBatis入門精通(一)MyBatis高階結果對映之一對多對映

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 本篇部落格主要講解MyBatis中如何使用collection標籤實現查詢結果一對多對映。 1. 使用collection標籤 需求:根據使用者id查詢使用者

RabbitMQ入門教程()RabbitMQ單機叢集搭建

叢集簡介 理解叢集先理解一下元資料 佇列元資料:佇列的名稱和宣告佇列時設定的屬性(是否持久化、是否自動刪除、佇列所屬的節點) 交換機元資料:交換機的名稱、型別、屬性(是否持久化等) 繫結元資料:一張簡單的表格展示瞭如何將訊息路由到佇列。包含的列有 交換機名

NDK開發 入門到放棄(JNI函式、C與C++呼叫函式的區別)

前言 在之前的文章中我們看到過如下C++程式碼: jclass clazz = env->FindClass("***/***/JNIDynamicUtils"); FindClass就屬於jni函式。此文就來稍微講解下一些常用的jni函式。

MyBatis入門精通()MyBatis XML方式的基本用法之增刪改

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 1. insert用法 1.1 簡單的insert方法 假如現在我們想新增一個使用者,該如何操作呢? 首先,在介面SysUserMapper中新增如下方法。

MyBatis入門精通()使用association標籤實現巢狀查詢

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 本篇部落格主要講解使用association標籤實現巢狀查詢的方法。 1. 明確需求 仍然延用上篇部落格中的需求:根據使用者id查詢使用者資訊的同時獲取該使用

MyBatis入門精通(二)使用collection標籤實現巢狀查詢

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 本篇部落格主要講解使用collection標籤實現巢狀查詢的方法。 1. 需求升級 在上篇部落格中,我們實現了需求:根據使用者id查詢使用者資訊的同時獲取使用

Mybatis入門精通》讀書筆記(

第九章. Spring整合Mybaits MyBatis-Spring可以幫助我們將Mybaits程式碼無縫整合到Spring中。使用這個類庫中的類,Spring將會載入必要的Mybaits工廠類和

MyBatis入門精通第一章配置MyBatis

從入門到精通 sim efault 入門到 eve lse version 例如 div <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC

MyBatis入門精通第一章實體類與Mapper.xml文件

1.0 style 返回值 spa map 命名 定義 當前 入門到精通 實體類: package tk.mybatis.simple.model; public class Country { public Long getId() {

MyBatis入門精通choose的用法

sele mod 需求 優先 ati create use choose oos <!-- 4.2 choose用法 需求: 在已有的sys_user表中,除

MyBatis入門精通if的用法

pass 用戶信息 插入數據 clas 不為 ike 需要 where條件 cti <!-- 4.1.1 在WHERE條件中使用if 需求: 實現一個用戶管理高級查詢功能,根

MyBatis入門精通(一)MyBatis入門

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 1. MyBatis簡介 ​ 2001年,Clinton Begin發起了一個名為iBATIS的開源專案,最初側重於密碼軟體的研發,後來發展成為一款基於Jav

MyBatis入門精通(二)MyBatis XML方式的基本用法之Select

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 1. 明確需求 書中提到的需求是一個基於角色的許可權控制需求(RBAC,即Role-Based Access Control),提到許可權管理,相信大家都不陌

MyBatis入門精通(三)MyBatis XML方式的基本用法之多表查詢

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 1. 多表查詢 上篇部落格中,我們示例的2個查詢都是單表查詢,但實際的業務場景肯定是需要多表查詢的,比如現在有個需求: 查詢某個使用者擁有的所有角色。這個需求

MyBatis入門精通(五)MyBatis 註解方式的基本用法

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 1. @Select 註解 1.1 使用Sql語句設定別名方式 假設現在有個需求:根據id查詢角色資訊。使用註解方式該如何實現呢? 首先,在介面SysRole

MyBatis入門精通(六)MyBatis動態Sql之if標籤的用法

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 本篇部落格主要講解如何使用if標籤生成動態的Sql,主要包含以下3個場景: 根據查詢條件實現動態查詢 根據引數值實現動態更新某些列 根據引數值實現動態插入某

MyBatis入門精通(七)MyBatis動態Sql之choose,where,set標籤的用法

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 本篇部落格主要講解如何使用choose,where,set標籤生成動態的Sql。 1. choose 用法 假設有這樣1個需求:當引數id有值時優先使用id查

MyBatis入門精通(八)MyBatis動態Sql之foreach標籤的用法

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 本篇部落格主要講解如何使用foreach標籤生成動態的Sql,主要包含以下3個場景: foreach 實現in集合 foreach 實現批量插入 forea

MyBatis入門精通(九)MyBatis高階結果對映之一對一對映

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 本篇部落格主要講解MyBatis中實現查詢結果一對一對映的3種方式: 使用別名實現自動對映 使用resultMap配置 使用resultMap的associ