最近在開發中,涉及到了講數據庫查詢的類型,直接轉為java需要的類型。 由於對handler 理解不到位 和 使用不當。躺了一些坑。
主要涉及的有2種。
1、varchar 轉 List<T>
2、varchar 轉Map<T>
如圖是寫的兩個handler。
ListTypeHandler 為了保證 handler的通用性,采取了 將mybatis xml 配置中的type,傳入 ListTypeHandler 中直接使用。 后來發現這是個大坑。
public class ListTypeHandler<E> extends BaseTypeHandler<List<E>> { private Class<E> type; public ListTypeHandler(Class<E> type) { if (type == null) { throw new IllegalArgumentException("Type argument cannot be null"); } this.type = type; } @Override public void setNonNullParameter(PreparedStatement ps, int i, List<E> parameter, JdbcType jdbcType) throws SQLException { String x = JSON.toJSONString(parameter); ps.setString(i, x); } @Override public List<E> getNullableResult(ResultSet rs, String columnName) throws SQLException { String s = rs.getString(columnName); return StringUtils.isBlank(s) ? null : JSON.parseArray(s, type); } @Override public List<E> getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String s = rs.getString(columnIndex); return StringUtils.isBlank(s) ? null : JSON.parseArray(s, type); } @Override public List<E> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String s = cs.getString(columnIndex); return StringUtils.isBlank(s) ? null : JSON.parseArray(s, type); } }
public class MapTypeHandler extends BaseTypeHandler<Map<String, Object>> { @Override public void setNonNullParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType) throws SQLException { if(MapUtils.isNotEmpty(parameter)) { String x = JSON.toJSONString(parameter); ps.setString(i, x); }else{ ps.setString(i, null); } } @Override public Map<String, Object> getNullableResult(ResultSet rs, String columnName) throws SQLException { String s = rs.getString(columnName); return StringUtils.isBlank(s) ? null : JSON.parseObject(s, new TypeReference<Map<String, Object>>(){}); // JSON.parseObject(s, getRawType()); } @Override public Map<String, Object> getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String s = rs.getString(columnIndex); return StringUtils.isBlank(s) ? null : JSON.parseObject(s, new TypeReference<Map<String, Object>>(){}); } @Override public Map<String, Object> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String s = cs.getString(columnIndex); return StringUtils.isBlank(s) ? null : JSON.parseObject(s, new TypeReference<Map<String, Object>>(){}); } }
首先沒有在mybatis.xml 總注冊,直接通過@result指定使用
@Select("<script>SELECT " + SELECT_FILEDS_AS + " FROM " + TABLE + " WHERE valid =1 and config_id = #{configId} " + SELECT_LIMIT + " </script>") @Results(value={ @Result(column="operation_content", property="operationContent", javaType=String.class, jdbcType=JdbcType.VARCHAR,typeHandler=ListTypeHandler.class), }) List<OperateRecord> selectList(@Param("configId") long configId,@Param("offset") int offset,@Param("limit") int limit);
由於希望 ListTypeHandler 可以通用一點,因此采用了傳入的javaType 即String,使用, 這個時候查詢也沒問題。感覺還很完美。
接下來,開始寫插入
@Insert({"INSERT INTO " + TABLE + " (config_id, operation_staff, operation_time, action, operation_content, ctime,valid) values " +"( #{configId}, #{operationStaff}, #{operationTime}, #{action}, #{operationContent, javaType=String, jdbcType=VARCHAR,typeHandler=ListTypeHandler}, unix_timestamp(),1 )"}) int insert(OperateRecord record);
這個時候就開始各種報錯,最后經過一頓百度,發現是沒有注冊,於是增加注冊。
<typeHandlers> <typeHandler handler="com.support.ListTypeHandler" javaType="List" jdbcType="VARCHAR" /> </typeHandlers>
這個時候,在插入,發現成功了。 這個時候,認為全部搞定了,開始部署線下,進行聯調。結果最大的問題才剛剛開始。
后頭測試查詢的時候,發現查詢又報錯。。。 於是開始debug,發現 'name' 類型varchar,java類型 String。 也開始進入list轉換。 一臉懵逼。
於是嘗試性的去掉字段的別名,發現居然成功了。。。
然后開始進入另一個大坑。認為和別名有關。
進入下一步的嘗試
@Result(column = "initiateName" ,property = "initiateName")
為字段指定result,發現問題完美解決。
這個時候,任務只要@Results(value={}),使用過handler的,其他字段就也要指定。
這個時候,終於認為大功告成,結果。。。。
測試另一個dao的時候,發現,這個dao完全沒有使用handler, 查詢結果中的字段,也會去轉換。。
然后,又開始懵逼。。。 一頓百度,最終恍然大悟。 再mybatisxml配置之后,是全局匹配。
因為不能使用傳入的java類型, 再listhandler寫死。
結論:
1.查詢時候,不需要注冊,即可直接通過result使用。type指定也可以生效。
2.插入的時候必須注冊。
3.注冊后,通過java,jdbc類型,還有名字全局匹配
4.result中可以不寫,自動匹配