您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“mybatis中的類型轉(zhuǎn)換方式”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
日常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)。所以我們可以選擇更好的處理方式。
如果你ORM框架用的是Mybatis。那么將很容易通過TypeHandler<T>接口解決這個(gè)問題。
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類型。
我們發(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枚舉。
我們還發(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)操作。
如果說我們的枚舉類型或者說我們使用其他方式來處理類別轉(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ā)揮作用呢?我們接著往下走。
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非常重要。接下來我們來講講具體的配置。
我們這里只講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)行注冊。
在配置中聲明注冊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>
今天我們學(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í)用文章!
免責(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)容。