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.annotation.UseDataSource;
019import com.mybatisflex.core.FlexGlobalConfig;
020import com.mybatisflex.core.datasource.DataSourceKey;
021import com.mybatisflex.core.datasource.FlexDataSource;
022import com.mybatisflex.core.dialect.DbType;
023import com.mybatisflex.core.dialect.DialectFactory;
024import com.mybatisflex.core.row.RowMapper;
025import com.mybatisflex.core.table.TableInfo;
026import com.mybatisflex.core.table.TableInfoFactory;
027import com.mybatisflex.core.util.StringUtil;
028import org.apache.ibatis.reflection.ExceptionUtil;
029
030import javax.sql.DataSource;
031import java.lang.reflect.InvocationHandler;
032import java.lang.reflect.InvocationTargetException;
033import java.lang.reflect.Method;
034
035/**
036 * @author michael
037 * @author norkts
038 */
039public class MapperInvocationHandler implements InvocationHandler {
040
041    private final Object mapper;
042    private final FlexDataSource dataSource;
043
044    public MapperInvocationHandler(Object mapper, DataSource dataSource) {
045        this.mapper = mapper;
046        if (dataSource instanceof FlexDataSource) {
047            this.dataSource = (FlexDataSource) dataSource;
048        } else {
049            this.dataSource = null;
050        }
051    }
052
053
054    @Override
055    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
056        boolean needClearDsKey = false;
057        boolean needClearDbType = false;
058        try {
059            //获取用户动态指定,由用户指定数据源,则应该有用户清除
060            String dataSourceKey = DataSourceKey.get();
061            if (StringUtil.isBlank(dataSourceKey)) {
062                //通过 @UseDataSource 或者 @Table(dataSource) 去获取
063                String configDataSourceKey = getConfigDataSourceKey(method, proxy);
064                if (StringUtil.isNotBlank(configDataSourceKey)) {
065                    dataSourceKey = configDataSourceKey;
066                    DataSourceKey.use(dataSourceKey);
067                    needClearDsKey = true;
068                }
069            }
070
071            //最终通过数据源 自定义分片 策略去获取
072            String shardingDataSourceKey = DataSourceKey.getByShardingStrategy(dataSourceKey, proxy, method, args);
073            if (shardingDataSourceKey != null && !shardingDataSourceKey.equals(dataSourceKey)) {
074                DataSourceKey.use(shardingDataSourceKey);
075                needClearDsKey = true;
076            }
077
078            //优先获取用户自己配置的 dbType
079            DbType dbType = DialectFactory.getHintDbType();
080
081            if (dbType == null) {
082                if (shardingDataSourceKey != null && dataSource != null) {
083                    //使用最终分片获取数据源类型
084                    dbType = dataSource.getDbType(shardingDataSourceKey);
085                }
086
087                if (dbType == null && dataSourceKey != null && dataSource != null) {
088                    dbType = dataSource.getDbType(dataSourceKey);
089                }
090
091                //设置了dbTypeGlobal,那么就使用全局的dbTypeGlobal
092                if (dbType == null) {
093                    dbType = DialectFactory.getGlobalDbType();
094                }
095
096                if (dbType == null) {
097                    dbType = FlexGlobalConfig.getDefaultConfig().getDbType();
098                }
099
100                DialectFactory.setHintDbType(dbType);
101                needClearDbType = true;
102            }
103            return method.invoke(mapper, args);
104        } catch (Throwable e) {
105            throw ExceptionUtil.unwrapThrowable(e);
106        } finally {
107            if (needClearDbType) {
108                DialectFactory.clearHintDbType();
109            }
110            if (needClearDsKey) {
111                DataSourceKey.clear();
112            }
113        }
114    }
115
116
117    private static String getConfigDataSourceKey(Method method, Object proxy) {
118        UseDataSource useDataSource = method.getAnnotation(UseDataSource.class);
119        if (useDataSource != null && StringUtil.isNotBlank(useDataSource.value())) {
120            return useDataSource.value();
121        }
122
123        Class<?>[] interfaces = proxy.getClass().getInterfaces();
124        for (Class<?> anInterface : interfaces) {
125            UseDataSource annotation = anInterface.getAnnotation(UseDataSource.class);
126            if (annotation != null) {
127                return annotation.value();
128            }
129        }
130
131        if (interfaces[0] != RowMapper.class) {
132            TableInfo tableInfo = TableInfoFactory.ofMapperClass(interfaces[0]);
133            if (tableInfo != null) {
134                String dataSourceKey = tableInfo.getDataSource();
135                if (StringUtil.isNotBlank(dataSourceKey)) {
136                    return dataSourceKey;
137                }
138            }
139        }
140        return null;
141    }
142
143
144}