001/*
002 *  Copyright (c) 2022-2025, 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.row.Row;
021import com.mybatisflex.core.row.RowCPI;
022import com.mybatisflex.core.row.RowKey;
023import com.mybatisflex.core.util.ArrayUtil;
024import com.mybatisflex.core.util.CollectionUtil;
025import org.apache.ibatis.executor.Executor;
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.MappedStatement;
030import org.apache.ibatis.mapping.SqlCommandType;
031import org.apache.ibatis.mapping.SqlSource;
032import org.apache.ibatis.mapping.StatementType;
033
034import java.sql.Statement;
035import java.util.*;
036
037/**
038 * 为 row 的主键生成器
039 */
040public class RowKeyGenerator implements KeyGenerator, IMultiKeyGenerator {
041
042    private static final KeyGenerator[] NO_KEY_GENERATORS = new KeyGenerator[0];
043
044    private final MappedStatement ms;
045    private Set<String> autoKeyGeneratorNames;
046    private KeyGenerator[] keyGenerators;
047
048
049    public RowKeyGenerator(MappedStatement methodMappedStatement) {
050        this.ms = methodMappedStatement;
051    }
052
053    @Override
054    public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
055        Row row = (Row) ((Map<?, ?>) parameter).get(FlexConsts.ROW);
056        // 重置 autoKeyGeneratorNames fix https://gitee.com/mybatis-flex/mybatis-flex/issues/ID64KB
057        autoKeyGeneratorNames = null;
058        keyGenerators = buildRowKeyGenerators(RowCPI.obtainsPrimaryKeys(row));
059        for (KeyGenerator keyGenerator : keyGenerators) {
060            keyGenerator.processBefore(executor, ms, stmt, parameter);
061        }
062    }
063
064
065    @Override
066    public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
067        for (KeyGenerator keyGenerator : keyGenerators) {
068            keyGenerator.processAfter(executor, ms, stmt, parameter);
069        }
070    }
071
072
073    private KeyGenerator[] buildRowKeyGenerators(RowKey[] rowKeys) {
074        if (ArrayUtil.isEmpty(rowKeys)) {
075            return NO_KEY_GENERATORS;
076        }
077
078        KeyGenerator[] keyGenerators = new KeyGenerator[rowKeys.length];
079        for (int i = 0; i < rowKeys.length; i++) {
080            KeyGenerator keyGenerator = createByRowKey(rowKeys[i]);
081            keyGenerators[i] = keyGenerator;
082        }
083        return keyGenerators;
084    }
085
086
087    private KeyGenerator createByRowKey(RowKey rowKey) {
088        if (rowKey == null || rowKey.getKeyType() == KeyType.None) {
089            return NoKeyGenerator.INSTANCE;
090        }
091
092        String keyColumn = rowKey.getKeyColumn();
093        if (rowKey.getKeyType() == KeyType.Auto) {
094            if (autoKeyGeneratorNames == null) {
095                autoKeyGeneratorNames = new LinkedHashSet<>();
096            }
097            autoKeyGeneratorNames.add(keyColumn);
098            return new RowJdbc3KeyGenerator(keyColumn);
099        }
100
101        if (rowKey.getKeyType() == KeyType.Generator) {
102            return new RowCustomKeyGenerator(rowKey);
103        }
104        //通过数据库的 sequence 生成主键
105        else {
106            String selectId = "row." + SelectKeyGenerator.SELECT_KEY_SUFFIX;
107            String sequence = rowKey.getValue();
108            SqlSource sqlSource = ms.getLang().createSqlSource(ms.getConfiguration(), sequence.trim(), Object.class);
109            MappedStatement.Builder msBuilder = new MappedStatement.Builder(ms.getConfiguration(), selectId, sqlSource, SqlCommandType.SELECT)
110                .resource(ms.getResource())
111                .fetchSize(null)
112                .timeout(null)
113                .statementType(StatementType.PREPARED)
114                .keyGenerator(NoKeyGenerator.INSTANCE)
115                .keyProperty(FlexConsts.ROW + "." + keyColumn)
116                .keyColumn(keyColumn)
117                .databaseId(ms.getDatabaseId())
118                .lang(ms.getLang())
119                .resultOrdered(false)
120                .resultSets(null)
121                .resultMaps(new ArrayList<>())
122                .resultSetType(null)
123                .flushCacheRequired(false)
124                .useCache(false)
125                .cache(ms.getCache());
126
127            MappedStatement keyMappedStatement = msBuilder.build();
128            ms.getConfiguration().addMappedStatement(keyMappedStatement);
129
130            //看到有的框架把 keyGenerator 添加到 mybatis 的当前配置里去,其实是完全没必要的
131            //因为只有在 xml 解析的时候,才可能存在多一个 MappedStatement 拥有同一个 keyGenerator 的情况
132            //当前每个方法都拥有一个自己的 keyGenerator 了,没必要添加
133            //addKeyGenerator(selectId, keyGenerator)
134            return new SelectKeyGenerator(keyMappedStatement, rowKey.isBefore());
135        }
136
137    }
138
139    /**
140     * 是否需要数据库生成主键
141     *
142     * @return true 需要生成
143     */
144    @Override
145    public boolean hasGeneratedKeys() {
146        return CollectionUtil.isNotEmpty(autoKeyGeneratorNames);
147    }
148
149    /**
150     * 数据库主键定义的 key
151     *
152     * @return key 数组
153     */
154    @Override
155    public String[] getKeyColumnNames() {
156        return autoKeyGeneratorNames == null ? new String[0] : autoKeyGeneratorNames.toArray(new String[0]);
157    }
158
159}