使用lombok注解導(dǎo)致mybatis-plus TypeHandler失效的解決
問題描述
建立實體其中一個字段為枚舉類
/**
* @author liuxishan 2023/6/2
*/
@Data
@Builder
@TableName(value = "hot_event",autoResultMap = false)
@EqualsAndHashCode(callSuper = true)
public class HotEvent extends BaseAuditor {
@TableId(type = IdType.ASSIGN_ID)
private String id;
············
············
/**
* 創(chuàng)建方式
*/
@TableField(typeHandler = HotEventCreationMethodHandler.class,jdbcType = JdbcType.INTEGER)
private CreationMethodEnum creationMethod;
}
public enum CreationMethodEnum {
SYSTEM(0, "系統(tǒng)導(dǎo)入"),
MANUAL_CREATE(1, "手動導(dǎo)入");
/**
* 用于排序
*/
private Integer value;
private String msg;
CreationMethodEnum(Integer value, String msg) {
this.value = value;
this.msg = msg;
}
public Integer getValue() {
return value;
}
public String getMsg() {
return msg;
}
public static CreationMethodEnum valueOf(int value) {
for (CreationMethodEnum creationMethodEnum : CreationMethodEnum.values()) {
if (creationMethodEnum.value.equals(value)) {
return creationMethodEnum;
}
}
return null;
}
}
希望數(shù)據(jù)庫存的時對應(yīng)的數(shù)字
為了和數(shù)據(jù)庫進行轉(zhuǎn)換,使用了typeHandler
public class HotEventCreationMethodHandler implements TypeHandler<CreationMethodEnum> {
@Override
public void setParameter(PreparedStatement preparedStatement, int i, CreationMethodEnum creationMethodEnum, JdbcType jdbcType) throws SQLException {
preparedStatement.setInt(i,creationMethodEnum.getValue());
}
@Override
public CreationMethodEnum getResult(ResultSet resultSet, String columnName) throws SQLException {
int anInt = resultSet.getInt(columnName);
return CreationMethodEnum.valueOf(anInt);
}
@Override
public CreationMethodEnum getResult(ResultSet resultSet, int i) throws SQLException {
int anInt = resultSet.getInt(i);
return CreationMethodEnum.valueOf(anInt);
}
@Override
public CreationMethodEnum getResult(CallableStatement callableStatement, int i) throws SQLException {
int anInt = callableStatement.getInt(i);
return CreationMethodEnum.valueOf(anInt);
}
}測試發(fā)現(xiàn)
@Test
public void pageContents() {
HotEvent hotEvent = HotEvent.builder()
.eventName("測試事件名稱")
.eventOverview("測試測試")
.eventType(EventTypeEnum.FUND)
.bizId("1245454")
.creationMethod(CreationMethodEnum.MANUAL_CREATE)
.publishingTime(LocalDateTime.now())
.url("http://baidu.com")
.publishingAgency("機構(gòu)")
.build();
hotEventDao.insert(hotEvent);
List<HotEvent> hotEvents = hotEventDao.selectList(Wrappers.<HotEvent>lambdaQuery().eq(HotEvent::getCreationMethod, 1));
}
插入/更新typeHandler生效,但是查詢時在將數(shù)據(jù)庫數(shù)據(jù)映射成java實體類的時候報錯
Caused by: java.lang.IllegalArgumentException: No enum constant com.yiyouliao.rivers.content.api.enums.CreationMethodEnum.1
at java.lang.Enum.valueOf(Enum.java:238)
at org.apache.ibatis.type.EnumTypeHandler.getNullableResult(EnumTypeHandler.java:49)
at org.apache.ibatis.type.EnumTypeHandler.getNullableResult(EnumTypeHandler.java:26)
at org.apache.ibatis.type.BaseTypeHandler.getResult(BaseTypeHandler.java:85)
... 67 more
設(shè)置的HotEventCreationMethodHandler并沒有生效
我們知道,在不開啟autoResultMap時,會導(dǎo)致TableField對于查詢返回的結(jié)果不生效。(對于更新無影響)
因此首先檢查在實體類注解上已經(jīng)開啟了autoResultMap,仍然是報錯(mybatisPlus會根據(jù)我們的實體類的類型,為我們自動注入resultMap,注入的resultMap也會自動推斷出需要使用的類型轉(zhuǎn)換器當然我們也可以指定)
@TableName(value = "hot_event",autoResultMap = true)
問題排查
網(wǎng)上查了typeHandler失效的原因,大部分都說的是檢查autoResultMap。無奈只能跟源碼進行檢查
問題出在查詢結(jié)果后將結(jié)果轉(zhuǎn)換為java實體類的地方,由DefaultResultSetHandler進行處理
關(guān)鍵方法
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSet org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap, java.lang.String)
在處理一行數(shù)據(jù)是,正常情況是首先創(chuàng)建java實體對象,然后根據(jù)resultMap的字段映射 進行轉(zhuǎn)換
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
//創(chuàng)建java實體對象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, false)) {
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}但是發(fā)現(xiàn)我們在createResultObject這一步就報錯了,
Caused by: java.lang.IllegalArgumentException: No enum constant com.yiyouliao.rivers.content.api.enums.CreationMethodEnum.1
跟進去原因就找到了,在創(chuàng)建對象時
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#createResultObject(org.apache.ibatis.executor.resultset.ResultSetWrapper, org.apache.ibatis.mapping.ResultMap, java.util.List<java.lang.Class<?>>, java.util.List<java.lang.Object>, java.lang.String)
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
final Class<?> resultType = resultMap.getType();
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (hasTypeHandlerForResultObject(rsw, resultType)) {
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}這里有幾種情況。
- 有對應(yīng)typeHandler處理當前類型,由typeHandler根據(jù)resultSet創(chuàng)建
- 有constructorMappings(構(gòu)造映射)我們沒有設(shè)置
- 有默認無參構(gòu)造,創(chuàng)建一個空對象,使用resultMap進行映射
- 無默認構(gòu)造,那么根據(jù)構(gòu)造函數(shù),自動映射(不使用resultMap)進行映射
發(fā)現(xiàn)進了第四個處理邏輯導(dǎo)致報錯,那么問題就發(fā)現(xiàn)了,是因為我們的實體類沒有默認的無參構(gòu)造函數(shù)
問題結(jié)論
回頭檢查實體類發(fā)現(xiàn)我們加了@Builder注解,會為實體類創(chuàng)建構(gòu)造函數(shù),但是也導(dǎo)致實體類沒有了無參構(gòu)造。
因此加上注解NoArgsConstructor,問題解決
/**
* @author liuxishan 2023/6/2
*/
@Data
@Builder
@TableName(value = "hot_event",autoResultMap = false)
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
public class HotEvent extends BaseAuditor {
@TableId(type = IdType.ASSIGN_ID)
private String id;
············
············
/**
* 創(chuàng)建方式
*/
@TableField(typeHandler = HotEventCreationMethodHandler.class,jdbcType = JdbcType.INTEGER)
private CreationMethodEnum creationMethod;
}
Tips
在查詢時使用Wrappers構(gòu)造查詢條件時,查詢字段是枚舉時,不能直接使用枚舉,需要使用數(shù)據(jù)庫對應(yīng)的值進行查詢,因為typeHandler僅僅處理的是實體類字段和jdbcType的轉(zhuǎn)換。
- 錯誤寫法
List<HotEvent> hotEvents = hotEventDao.selectList(
Wrappers.<HotEvent>lambdaQuery().eq(HotEvent::getCreationMethod, CreationMethodEnum.MANUAL_CREATE)
);- 正確寫法
List<HotEvent> hotEvents = hotEventDao.selectList(
Wrappers.<HotEvent>lambdaQuery().eq(HotEvent::getCreationMethod,1)
);以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Eclipse+Java+Swing實現(xiàn)斗地主游戲(代碼)
這篇文章主要介紹了Eclipse+Java+Swing實現(xiàn)斗地主游戲并附上詳細的代碼實現(xiàn),正在學習的你可以當小練習練練,希望對你有所幫助2022-01-01
Spring Boot 配置MySQL數(shù)據(jù)庫重連的操作方法
這篇文章主要介紹了Spring Boot 配置MySQL數(shù)據(jù)庫重連的操作方法,需要的朋友可以參考下2018-04-04
java httpclient設(shè)置超時時間和代理的方法
這篇文章主要介紹了java httpclient設(shè)置超時時間和代理的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-02-02
簡單了解JAVA public class與class區(qū)別
這篇文章主要介紹了簡單了解JAVA public class與class區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-03-03

