溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

mybatis中的類型轉(zhuǎn)換方式

發(fā)布時(shí)間:2021-07-08 17:51:04 來源:億速云 閱讀:566 作者:chen 欄目:大數(shù)據(jù)

本篇內(nèi)容介紹了“mybatis中的類型轉(zhuǎn)換方式”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

1.場景

日常java開發(fā)中經(jīng)常有這種需求,用0或者1這些代碼(不局限于數(shù)字)來表示某種狀態(tài)。比如用0表示女性,用1來表示男性。而且寫入數(shù)據(jù)庫可能是一個(gè)標(biāo)識(shí),從數(shù)據(jù)庫讀取又還原為具體的說明。而且一般情況下為了更好理解或者消除魔法值,通常的處理方案是定義一個(gè)枚舉:

有些枚舉是這樣定義的

 public enum GenderType{
      FEMALE,MALE,UNKNOWN
 }

那么通常很多人會(huì)這么入庫(java偽代碼)

  if(GenderType.MALE){
   // 寫入 1
  }else if(GenderType.FEMALE){
   // 寫入 0
  }else{
  //也可能是泰國回來的 那就 2
  }

讀取的時(shí)候要么同樣按照上面的再反向處理一次或者使用數(shù)據(jù)庫sql語法case when 來直接寫入DTO

 CASE gender
 WHEN 1 THEN '男'
 WHEN 0 THEN '女'
 ELSE '未知' END

這種處理方式看起來不是很優(yōu)雅。而且多了很多的判斷和處理邏輯,和我們的業(yè)務(wù)并不是非常相關(guān)。所以我們可以選擇更好的處理方式。

2.Mybatis中的TypeHandler

如果你ORM框架用的是Mybatis。那么將很容易通過TypeHandler<T>接口解決這個(gè)問題。

2.1 TypeHandler 分析

public interface TypeHandler<T> {
  
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
 
  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

源碼分析:

  • setParameter 方法 通過 傳入的T類型寫你自己的邏輯,選擇調(diào)用 PreparedStatement 對象的某個(gè)set方法將數(shù)據(jù)寫入數(shù)據(jù)庫。此方法用來寫庫。

  • getResult(ResultSet rs, String columnName) 通過字段名來讀庫并轉(zhuǎn)換為T類型。

  • getResult(ResultSet rs, int columnIndex) 通過字段索引來讀庫并轉(zhuǎn)換為T類型。

  • getResult(CallableStatement cs, int columnIndex) 調(diào)用存儲(chǔ)過程來獲取結(jié)果并轉(zhuǎn)換為T類型。

2.2 EnumOrdinalTypeHandler

我們發(fā)現(xiàn)TypeHandler有一個(gè)實(shí)現(xiàn)類EnumOrdinalTypeHandler。字面意思是可以通過枚舉的序號(hào)來處理類型。

@Override
  public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
    ps.setInt(i, parameter.ordinal());
  }

我們先不考慮setNull的情況。通過此方法我們發(fā)現(xiàn)確實(shí)存入的是枚舉的順序值(順序從0開始),拿上面的例子來說 如果是GenderType.FEMALE是0,如果是GenderType.MALE是1,但是當(dāng)GenderType.UNKNOWN時(shí)存入的是2。取的時(shí)候也是自然反向處理為具體的GenderType枚舉。

2.3 EnumTypeHandler

我們還發(fā)現(xiàn)有另外一個(gè)枚舉類型處理器。它的set方法是這樣的:

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
    if (jdbcType == null) {
      ps.setString(i, parameter.name());
    } else {
      ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
    }
  }

我們不考慮jdbcType問題發(fā)現(xiàn)都是將Enum.name()的值寫入數(shù)據(jù)庫。拿上面的例子來說 如果是GenderType.FEMALE是FEMALE,如果是GenderType.MALE是MALE,但是當(dāng)GenderType.UNKNOWN時(shí)存入的是UNKNOWN。讀庫是通過Enum.valueOf(Class<T> enumType,String name)來進(jìn)行反轉(zhuǎn)操作。

2.4 自定義TypeHandler

如果說我們的枚舉類型或者說我們使用其他方式來處理類別轉(zhuǎn)換怎么辦?當(dāng)然Mybatis不會(huì)幫你干這么具體的事情。需要你自己來實(shí)現(xiàn)了。我們還拿枚舉作為例子,然后模仿上面的兩種TypeHandler。 還是拿開始的例子來說通常我個(gè)人比較喜歡這么定義枚舉:

public enum GenderTypeEnum {
    /**
     * female.
     */
    FEMALE(0, "女"),
     /**
     * male.
     */
    MALE(1,"男"),
    /**
     * unknown.
     */
    UNKNOWN(2, "未知");

    private int value;
    private String description;

    GenderType(int value, String description) {
        this.value = value;
        this.description = description;
    }
    
 
    public int value() {
        return this.value;
    }
    
 
    public String description() {
        return this.description;
    }
}

通過繼承BaseTypeHandler實(shí)現(xiàn)該抽象類的3個(gè)鉤子方法就行了:

@MappedTypes({GenderTypeEnum.class})
@MappedJdbcTypes({JdbcType.INTEGER})
public class GenderTypeEnumTypeHandler extends BaseTypeHandler<GenderTypeEnum> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, GenderTypeEnum parameter, JdbcType jdbcType) throws SQLException {
        if (jdbcType == null) {
            ps.setInt(i, parameter.value());
        } else {
            // see r3589
            ps.setObject(i, parameter.value(), jdbcType.TYPE_CODE);
        }
    }

    @Override
    public GenderTypeEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return getGenderType(rs.getInt(columnName));
    }

    @Override
    public GenderTypeEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return getGenderType(rs.getInt(columnIndex));

    }

    @Override
    public GenderTypeEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return getGenderType(cs.getInt(columnIndex));
    }

    private GenderTypeEnum getGenderType(int value) {
        Class<GenderTypeEnum> genderTypeClass = GenderTypeEnum.class;
        return Arrays.stream(genderTypeClass.getEnumConstants())
                .filter(genderType -> genderType.value() == value)
                .findFirst().orElse(GenderTypeEnum.UNKNOWN);
    }
}

TypeHandler 實(shí)現(xiàn)寫好了,那么如何讓其發(fā)揮作用呢?我們接著往下走。

2.5 TypeHandler的核心要點(diǎn)

TypeHandler作用是javaType和jdbcType相互轉(zhuǎn)換。所以在聲明一個(gè)TypeHandler的時(shí)候一定要明確該TypeHandler處理的這兩種類型。這是必須要明確的原則。MyBatis不會(huì)通過窺探數(shù)據(jù)庫元信息來決定使用哪種JDBC類型,所以你必須在參數(shù)和結(jié)果映射中指明何種類型的字段,使其能夠綁定到正確的類型處理器上。MyBatis直到語句被執(zhí)行時(shí)才清楚數(shù)據(jù)類型。 通過上述例子中的@MappedJdbcTypes和@MappedTypes來進(jìn)行綁定類型轉(zhuǎn)換關(guān)系,也可以通過xml的typeHandler元素中的jdbcType或者javaType來指定。如果同時(shí)指定,xml的優(yōu)先級(jí)要高。 注意有可能你會(huì)覆蓋內(nèi)置的TypeHandler。所以自定義時(shí)一定要去了解Mybatis提供的一些默認(rèn)處理器。避免對其他業(yè)務(wù)的影響。所以使用自定義TypeHandler很重要的一個(gè)原則就是一定要聲明JavaType和JdbcType.上面這些雖然比較生澀但是對于使用好TypeHandler非常重要。接下來我們來講講具體的配置。

2.6 免注冊TypeHandler

我們這里只講xml中的配置:

  • 一種在rultMap元素中聲明一般用來查詢。一定要注意2.5中的一些原則。

    <resultMap id="StudentMap" type="cn.felord.mybatis.entity.Student">
       <id column="student_id" property="studentId"/>
       <result column="student_name" property="studentName"/>
       <result column="gender" property="genderType" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
       <result column="age" property="age"/>
   </resultMap>
  • 然后是在插入、更新語句中使用。它們都是相同的,這里只舉一個(gè)插入例子。

    <insert id="saveStu">
        insert into student (student_name, gender, age)
        values (#{studentName},
                #{genderType,javaType=cn.felord.mybatis.enums.GenderTypeEnum,jdbcType=INTEGER,typeHandler=cn.felord.mybatis.type.GenderTypeEnumTypeHandler},
                #{age})
    </insert>

如果注冊了別名都可以使用別名。上面的好處就是不用在TypeHandlerRegistry中進(jìn)行注冊。

2.7 注冊TypeHandler

在配置中聲明注冊TypeHandler,然后Mybatis根據(jù)兩種類型會(huì)自動(dòng)匹配。所以這里還是要強(qiáng)調(diào)2.5中的核心要點(diǎn)。

  • 如果你是xml配置需要在Configuration配置文件中的<typeHandlers>標(biāo)簽中進(jìn)行聲明式注冊

<typeHandlers>
<typeHandler jdbcType="JdbcType枚舉存在的枚舉" javaType="typeAliases的別名或者全限定類名"  handler="類全限定名"/>
<package name="指定所有typeHandler所在的包的包名"/>
</typeHandlers>
  • javaConfig 方式 ,第一你可以通過SqlSessionFactory對象取到Configuration對象將typeHandler注冊進(jìn)去。如果你使用mybatis-spring組件,可以在SqlSessionFactoryBean 的setTypeHandlersPackage方法中配置typeHandler的集中包路徑,那么框架將會(huì)自動(dòng)掃描并注冊他們。springboot中對應(yīng)的配置屬性是mybatis.typeHandlersPackage。

如果你注冊了TypeHandler。在Mapper.xml中只需要聲明jdbcType和javaType,無需再聲明具體的typeHandler。Mybatis會(huì)自動(dòng)通過jdbcType、javaType來映射到具體注冊的TypeHandler上去 。就像下面的例子

    <insert id="saveAutomaticStu">
        insert into student (student_name, gender, age)
        values (#{studentName}, #{genderType,javaType=cn.felord.mybatis.enums.GenderTypeEnum,jdbcType=INTEGER}, #{age})
    </insert>

3.總結(jié)

今天我們學(xué)習(xí)了mybatis開發(fā)中如何通過使用類型處理器進(jìn)行類型的轉(zhuǎn)換處理,如何處理枚舉,如何自定義處理器并使用它。相信對你在java開發(fā)過程中會(huì)有很大的幫助。相關(guān)的代碼在我的碼云倉庫中:https://gitee.com/felord/mybatis-test.git

“mybatis中的類型轉(zhuǎn)換方式”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI