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}