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