001/*
002 *  Copyright (c) 2022-2023, 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}