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.BaseMapper;
019import com.mybatisflex.core.FlexGlobalConfig;
020import com.mybatisflex.core.exception.FlexExceptions;
021import com.mybatisflex.core.util.StringUtil;
022import org.apache.ibatis.reflection.ExceptionUtil;
023import org.apache.ibatis.session.ExecutorType;
024import org.apache.ibatis.session.SqlSession;
025import org.apache.ibatis.session.SqlSessionFactory;
026import com.mybatisflex.core.util.MapUtil;
027
028import java.lang.reflect.InvocationHandler;
029import java.lang.reflect.Method;
030import java.lang.reflect.Proxy;
031import java.util.Map;
032import java.util.concurrent.ConcurrentHashMap;
033
034/**
035 * 获取 {@link BaseMapper} 对象。
036 *
037 * @author michael
038 * @author 王帅
039 */
040@SuppressWarnings("unchecked")
041public class Mappers {
042
043    private Mappers() {
044    }
045
046    private static final Map<Class<?>, Object> MAPPER_OBJECTS = new ConcurrentHashMap<>();
047
048    private static final Map<Class<?>, Class<?>> ENTITY_MAPPER_MAP = new ConcurrentHashMap<>();
049
050    /**
051     * 添加 实体类 与 {@link BaseMapper} 接口实现接口 对应,两者皆为非动态代理类。
052     *
053     * @param entityClass 实体类
054     * @param mapperClass {@link BaseMapper} 实现接口
055     */
056    static void addMapping(Class<?> entityClass, Class<?> mapperClass) {
057        ENTITY_MAPPER_MAP.put(entityClass, mapperClass);
058    }
059
060    /**
061     * 通过 实体类 获取对应 {@link BaseMapper} 对象。
062     *
063     * @param entityClass 实体类
064     * @param <E>         实体类类型
065     * @return {@link BaseMapper} 对象
066     */
067    public static <E> BaseMapper<E> ofEntityClass(Class<E> entityClass) {
068        Class<?> mapperClass = ENTITY_MAPPER_MAP.get(entityClass);
069        if (mapperClass == null) {
070            throw FlexExceptions.wrap("Can not find MapperClass by entity: " + entityClass.getName());
071        }
072        return (BaseMapper<E>) ofMapperClass(mapperClass);
073    }
074
075    /**
076     * 通过 {@link BaseMapper} 接口实现的 Class 引用直接获取 {@link BaseMapper} 代理对象。
077     *
078     * @param mapperClass {@link BaseMapper} 接口实现
079     * @return {@link BaseMapper} 对象
080     */
081    public static <M> M ofMapperClass(Class<M> mapperClass) {
082        Object mapperObject = MapUtil.computeIfAbsent(MAPPER_OBJECTS, mapperClass, clazz ->
083            Proxy.newProxyInstance(mapperClass.getClassLoader()
084                , new Class[]{mapperClass}
085                , new MapperHandler(mapperClass)));
086        return (M) mapperObject;
087    }
088    public static <M> M ofMapperClass(String environmentId, Class<M> mapperClass) {
089        Object mapperObject = MapUtil.computeIfAbsent(MAPPER_OBJECTS, mapperClass, clazz ->
090            Proxy.newProxyInstance(mapperClass.getClassLoader()
091                , new Class[]{mapperClass}
092                , new MapperHandler(environmentId, mapperClass)));
093        return (M) mapperObject;
094    }
095
096    private static class MapperHandler implements InvocationHandler {
097
098        private final Class<?> mapperClass;
099        private final ExecutorType executorType;
100        private final SqlSessionFactory sqlSessionFactory;
101
102        public MapperHandler(Class<?> mapperClass) {
103            this(null, mapperClass);
104        }
105
106        public MapperHandler(String environmentId, Class<?> mapperClass) {
107            this.mapperClass = mapperClass;
108            if(StringUtil.noText(environmentId)) {
109                this.executorType = FlexGlobalConfig.getDefaultConfig()
110                    .getConfiguration()
111                    .getDefaultExecutorType();
112                this.sqlSessionFactory = FlexGlobalConfig.getDefaultConfig()
113                    .getSqlSessionFactory();
114            } else {
115                this.executorType = FlexGlobalConfig.getConfig(environmentId)
116                    .getConfiguration()
117                    .getDefaultExecutorType();
118                this.sqlSessionFactory = FlexGlobalConfig.getConfig(environmentId)
119                    .getSqlSessionFactory();
120            }
121        }
122
123        private SqlSession openSession() {
124            return sqlSessionFactory.openSession(executorType, true);
125        }
126
127        @Override
128        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
129            try (SqlSession sqlSession = openSession()) {
130                Object mapper = sqlSession.getMapper(mapperClass);
131                return method.invoke(mapper, args);
132            } catch (Throwable throwable) {
133                throw ExceptionUtil.unwrapThrowable(throwable);
134            }
135        }
136
137    }
138
139}