001/* 002 * Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.core.mybatis; 017 018import com.mybatisflex.core.transaction.TransactionContext; 019import org.apache.ibatis.cursor.Cursor; 020import org.apache.ibatis.executor.Executor; 021import org.apache.ibatis.executor.parameter.ParameterHandler; 022import org.apache.ibatis.executor.resultset.ResultSetWrapper; 023import org.apache.ibatis.mapping.BoundSql; 024import org.apache.ibatis.mapping.MappedStatement; 025import org.apache.ibatis.mapping.ResultMap; 026import org.apache.ibatis.mapping.ResultMapping; 027import org.apache.ibatis.session.ResultHandler; 028import org.apache.ibatis.session.RowBounds; 029import org.apache.ibatis.type.TypeHandler; 030 031import java.sql.SQLException; 032import java.sql.Statement; 033import java.util.Collection; 034import java.util.Iterator; 035import java.util.List; 036import java.util.Locale; 037 038/** 039 * @author michael 040 * 用于增强对 Cursor 查询处理,以及 List<String> 的自动映射问题 041 */ 042public class FlexResultSetHandler extends FlexDefaultResultSetHandler { 043 044 public FlexResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler 045 , ResultHandler<?> resultHandler, BoundSql boundSql, RowBounds rowBounds) { 046 super(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); 047 } 048 049 050 /** 051 * 从写 handleCursorResultSets, 用于适配在事务下自动关闭 Cursor 052 */ 053 @Override 054 public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException { 055 Cursor<E> defaultCursor = super.handleCursorResultSets(stmt); 056 057 //in transaction 058 if (TransactionContext.getXID() != null) { 059 return new FlexCursor<>(defaultCursor); 060 } 061 062 return defaultCursor; 063 } 064 065 066 /** 067 * 修复当实体类中存在 List<String> 或者 List<Integer> 等自动映射出错的问题 068 * 本质问题应该出现 mybatis 判断有误 069 * <p> 070 * https://gitee.com/mybatis-flex/mybatis-flex/issues/I7XBQS 071 * https://gitee.com/mybatis-flex/mybatis-flex/issues/I7X7G7 072 * 073 * @param rsw 074 * @param resultMap 075 * @param columnPrefix 076 * @throws SQLException 077 */ 078 @Override 079 protected Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) 080 throws SQLException { 081 final Class<?> resultType = resultMap.getType(); 082 if (!resultMap.getResultMappings().isEmpty()) { 083 final List<ResultMapping> resultMappingList = resultMap.getResultMappings(); 084 final ResultMapping mapping = resultMappingList.get(0); 085 String columnName = prependPrefix(mapping.getColumn(), columnPrefix); 086 TypeHandler<?> typeHandler = mapping.getTypeHandler(); 087 088 Collection<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix); 089 if (columnName != null && mappedColumnNames.contains(columnName.toUpperCase(Locale.ENGLISH))) { 090 return typeHandler.getResult(rsw.getResultSet(), columnName); 091 } 092 return null; 093 } else { 094 String columnName = rsw.getColumnNames().get(0); 095 TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName); 096 return typeHandler.getResult(rsw.getResultSet(), columnName); 097 } 098 } 099 100 101 static class FlexCursor<T> implements Cursor<T> { 102 103 private final Cursor<T> originalCursor; 104 105 public FlexCursor(Cursor<T> cursor) { 106 this.originalCursor = cursor; 107 TransactionContext.holdCursor(cursor); 108 } 109 110 @Override 111 public void close() { 112 // do nothing,由 TransactionContext 去关闭 113 } 114 115 @Override 116 public boolean isOpen() { 117 return originalCursor.isOpen(); 118 } 119 120 @Override 121 public boolean isConsumed() { 122 return originalCursor.isConsumed(); 123 } 124 125 @Override 126 public int getCurrentIndex() { 127 return originalCursor.getCurrentIndex(); 128 } 129 130 @Override 131 public Iterator<T> iterator() { 132 return originalCursor.iterator(); 133 } 134 135 } 136 137}