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