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.Iterator;
034import java.util.List;
035import java.util.Locale;
036
037/**
038 * @author michael
039 * 用于增强对 Cursor 查询处理,以及 List<String> 的自动映射问题
040 */
041public class FlexResultSetHandler extends FlexDefaultResultSetHandler {
042
043    public FlexResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler
044        , ResultHandler<?> resultHandler, BoundSql boundSql, RowBounds rowBounds) {
045        super(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
046    }
047
048
049    /**
050     * 从写 handleCursorResultSets, 用于适配在事务下自动关闭 Cursor
051     */
052    @Override
053    public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {
054        Cursor<E> defaultCursor = super.handleCursorResultSets(stmt);
055
056        //in transaction
057        if (TransactionContext.getXID() != null) {
058            return new FlexCursor<>(defaultCursor);
059        }
060
061        return defaultCursor;
062    }
063
064
065    /**
066     * 修复当实体类中存在 List<String> 或者 List<Integer> 等自动映射出错的问题
067     * 本质问题应该出现 mybatis 判断有误
068     * <p>
069     * https://gitee.com/mybatis-flex/mybatis-flex/issues/I7XBQS
070     * https://gitee.com/mybatis-flex/mybatis-flex/issues/I7X7G7
071     *
072     * @param rsw
073     * @param resultMap
074     * @param columnPrefix
075     * @throws SQLException
076     */
077    @Override
078    protected Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix)
079        throws SQLException {
080        final Class<?> resultType = resultMap.getType();
081        if (!resultMap.getResultMappings().isEmpty()) {
082            final List<ResultMapping> resultMappingList = resultMap.getResultMappings();
083            final ResultMapping mapping = resultMappingList.get(0);
084            String columnName = prependPrefix(mapping.getColumn(), columnPrefix);
085            TypeHandler<?> typeHandler = mapping.getTypeHandler();
086
087            List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
088            if (columnName != null && mappedColumnNames.contains(columnName.toUpperCase(Locale.ENGLISH))) {
089                return typeHandler.getResult(rsw.getResultSet(), columnName);
090            }
091            return null;
092        } else {
093            String columnName = rsw.getColumnNames().get(0);
094            TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName);
095            return typeHandler.getResult(rsw.getResultSet(), columnName);
096        }
097    }
098
099
100    static class FlexCursor<T> implements Cursor<T> {
101
102        private final Cursor<T> originalCursor;
103
104        public FlexCursor(Cursor<T> cursor) {
105            this.originalCursor = cursor;
106            TransactionContext.holdCursor(cursor);
107        }
108
109        @Override
110        public void close() {
111            // do nothing,由 TransactionContext 去关闭
112        }
113
114        @Override
115        public boolean isOpen() {
116            return originalCursor.isOpen();
117        }
118
119        @Override
120        public boolean isConsumed() {
121            return originalCursor.isConsumed();
122        }
123
124        @Override
125        public int getCurrentIndex() {
126            return originalCursor.getCurrentIndex();
127        }
128
129        @Override
130        public Iterator<T> iterator() {
131            return originalCursor.iterator();
132        }
133
134    }
135
136}