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.keygen;
017
018import com.mybatisflex.annotation.KeyType;
019import com.mybatisflex.core.FlexConsts;
020import com.mybatisflex.core.FlexGlobalConfig;
021import com.mybatisflex.core.exception.FlexExceptions;
022import com.mybatisflex.core.table.IdInfo;
023import com.mybatisflex.core.table.TableInfo;
024import com.mybatisflex.core.util.StringUtil;
025import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
026import org.apache.ibatis.executor.keygen.KeyGenerator;
027import org.apache.ibatis.executor.keygen.NoKeyGenerator;
028import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
029import org.apache.ibatis.mapping.*;
030import org.apache.ibatis.session.Configuration;
031
032import java.util.ArrayList;
033import java.util.Arrays;
034import java.util.List;
035
036public class MybatisKeyGeneratorUtil {
037
038    private MybatisKeyGeneratorUtil() {}
039
040    public static KeyGenerator createTableKeyGenerator(TableInfo tableInfo, MappedStatement ms) {
041        List<IdInfo> primaryKeyList = tableInfo.getPrimaryKeyList();
042
043        //无主键
044        if (primaryKeyList == null || primaryKeyList.isEmpty()) {
045            return NoKeyGenerator.INSTANCE;
046        }
047
048        //多主键的
049        if (primaryKeyList.size() > 1) {
050            return new MultiPrimaryKeyGenerator(ms, tableInfo, primaryKeyList);
051        }
052
053        return createIdKeyGenerator(tableInfo, ms, primaryKeyList.get(0));
054    }
055
056
057    public static KeyGenerator createIdKeyGenerator(TableInfo tableInfo, MappedStatement ms, IdInfo idInfo) {
058        FlexGlobalConfig.KeyConfig globalKeyConfig = FlexGlobalConfig.getConfig(ms.getConfiguration()).getKeyConfig();
059        KeyType keyType = getKeyType(idInfo, globalKeyConfig);
060
061        if (keyType == null || keyType == KeyType.None) {
062            return NoKeyGenerator.INSTANCE;
063        }
064
065        //自增主键
066        if (keyType == KeyType.Auto) {
067            return Jdbc3KeyGenerator.INSTANCE;
068        }
069
070        //通过 java 生成的主键
071        if (keyType == KeyType.Generator) {
072            return new CustomKeyGenerator(ms.getConfiguration(), tableInfo, idInfo);
073        }
074
075        //通过序列生成的注解
076        String sequence = getKeyValue(idInfo, globalKeyConfig);
077        if (StringUtil.isBlank(sequence)) {
078            throw FlexExceptions.wrap("Please config sequence by @Id(value=\"...\") for field: %s in class: %s"
079                    , idInfo.getProperty()
080                    , tableInfo.getEntityClass().getName());
081        }
082
083
084        String selectId = ms.getId() + SelectKeyGenerator.SELECT_KEY_SUFFIX;
085        SqlSource sqlSource = ms.getLang().createSqlSource(ms.getConfiguration(), sequence.trim(), idInfo.getPropertyType());
086        MappedStatement.Builder msBuilder = new MappedStatement.Builder(ms.getConfiguration(), selectId, sqlSource, SqlCommandType.SELECT)
087                .resource(ms.getResource())
088                .fetchSize(null)
089                .timeout(null)
090                .statementType(StatementType.PREPARED)
091                .keyGenerator(NoKeyGenerator.INSTANCE)
092                .keyProperty(FlexConsts.ENTITY + "." + idInfo.getProperty())
093                .keyColumn(idInfo.getColumn())
094                .databaseId(ms.getDatabaseId())
095                .lang(ms.getLang())
096                .resultOrdered(false)
097                .resultSets(null)
098                .resultMaps(createIdResultMaps(ms.getConfiguration(), selectId + "-Inline", idInfo.getPropertyType(), new ArrayList<>()))
099                .resultSetType(null)
100                .flushCacheRequired(false)
101                .useCache(false)
102                .cache(ms.getCache());
103
104        MappedStatement keyMappedStatement = msBuilder.build();
105        ms.getConfiguration().addMappedStatement(keyMappedStatement);
106
107        //看到有的框架把 keyGenerator 添加到 mybatis 的当前配置里去,其实是完全没必要的
108        //因为只有在 xml 解析的时候,才可能存在多一个 MappedStatement 拥有同一个 keyGenerator 的情况
109        //当前每个方法都拥有一个自己的 keyGenerator 了,没必要添加
110        //this.addKeyGenerator(selectId, keyGenerator);
111        return new SelectKeyGenerator(keyMappedStatement, isKeyBefore(idInfo, globalKeyConfig));
112    }
113
114
115    private static List<ResultMap> createIdResultMaps(Configuration configuration,
116                                                      String statementId, Class<?> resultType, List<ResultMapping> resultMappings) {
117        ResultMap resultMap = new ResultMap.Builder(configuration, statementId, resultType, resultMappings, null)
118                .build();
119        return Arrays.asList(resultMap);
120    }
121
122
123    /**
124     * 获取主键的 keyType,优先通过 @id 获取,获取不到通过全局配置获取
125     */
126    public static KeyType getKeyType(IdInfo idInfo, FlexGlobalConfig.KeyConfig globalKeyConfig) {
127        KeyType keyType = idInfo.getKeyType();
128        if (keyType != KeyType.None) {
129            return keyType;
130        }
131
132        if (globalKeyConfig != null) {
133            return globalKeyConfig.getKeyType();
134        }
135
136        return keyType;
137    }
138
139
140    public static String getKeyValue(IdInfo idInfo, FlexGlobalConfig.KeyConfig globalKeyConfig) {
141        String value = idInfo.getValue();
142        if (StringUtil.isBlank(value) && globalKeyConfig != null) {
143            value = globalKeyConfig.getValue();
144        }
145        return value;
146    }
147
148
149    public static boolean isKeyBefore(IdInfo idInfo, FlexGlobalConfig.KeyConfig globalKeyConfig) {
150        Boolean before = idInfo.getBefore();
151        if (before == null && globalKeyConfig != null) {
152            return globalKeyConfig.isBefore();
153        } else {
154            return before == null || before;
155        }
156    }
157
158}