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