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.dialect.impl; 017 018import com.mybatisflex.core.constant.SqlConsts; 019import com.mybatisflex.core.dialect.KeywordWrap; 020import com.mybatisflex.core.dialect.LimitOffsetProcessor; 021import com.mybatisflex.core.dialect.OperateType; 022import com.mybatisflex.core.row.Row; 023import com.mybatisflex.core.row.RowCPI; 024import com.mybatisflex.core.table.TableInfo; 025import com.mybatisflex.core.util.CollectionUtil; 026import com.mybatisflex.core.util.SqlUtil; 027import com.mybatisflex.core.util.StringUtil; 028 029import java.util.*; 030 031import static com.mybatisflex.core.constant.SqlConsts.*; 032 033/** 034 * @author michael 035 */ 036public class OracleDialect extends CommonsDialectImpl { 037 038 //https://docs.oracle.com/cd/A97630_01/appdev.920/a42525/apb.htm 039 public static final Set<String> keywords = CollectionUtil.newHashSet( 040 "ACCESS", "ELSE", "MODIFY", "START", "ADD", "EXCLUSIVE", "NOAUDIT", "SELECT", 041 "ALL", "EXISTS", "NOCOMPRESS", "SESSION", "ALTER", "FILE", "NOT", "SET", "AND", "FLOAT", 042 "NOTFOUND", "SHARE", "ANY", "FOR", "NOWAIT", "SIZE", "ARRAYLEN", "FROM", "NULL", "SMALLINT", 043 "AS", "GRANT", "NUMBER", "SQLBUF", "ASC", "GROUP", "OF", "SUCCESSFUL", "AUDIT", "HAVING", 044 "OFFLINE", "SYNONYM", "BETWEEN", "IDENTIFIED", "ON", "SYSDATE", "BY", "IMMEDIATE", "ONLINE", 045 "TABLE", "CHAR", "IN", "OPTION", "THEN", "CHECK", "INCREMENT", "OR", "TO", "CLUSTER", "INDEX", 046 "ORDER", "TRIGGER", "COLUMN", "INITIAL", "PCTFREE", "UID", "COMMENT", "INSERT", "PRIOR", 047 "UNION", "COMPRESS", "INTEGER", "PRIVILEGES", "UNIQUE", "CONNECT", "INTERSECT", "PUBLIC", 048 "UPDATE", "CREATE", "INTO", "RAW", "USER", "CURRENT", "IS", "RENAME", "VALIDATE", "DATE", "LEVEL", 049 "RESOURCE", "VALUES", "DECIMAL", "LIKE", "REVOKE", "VARCHAR", "DEFAULT", "LOCK", "ROW", "VARCHAR2", 050 "DELETE", "LONG", "ROWID", "VIEW", "DESC", "MAXEXTENTS", "ROWLABEL", "WHENEVER", "DISTINCT", "MINUS", 051 "ROWNUM", "WHERE", "DROP", "MODE", "ROWS", "WITH", "ADMIN", "CURSOR", "FOUND", "MOUNT", "AFTER", "CYCLE", 052 "FUNCTION", "NEXT", "ALLOCATE", "DATABASE", "GO", "NEW", "ANALYZE", "DATAFILE", "GOTO", "NOARCHIVELOG", 053 "ARCHIVE", "DBA", "GROUPS", "NOCACHE", "ARCHIVELOG", "DEC", "INCLUDING", "NOCYCLE", "AUTHORIZATION", 054 "DECLARE", "INDICATOR", "NOMAXVALUE", "AVG", "DISABLE", "INITRANS", "NOMINVALUE", "BACKUP", "DISMOUNT", 055 "INSTANCE", "NONE", "BEGIN", "DOUBLE", "INT", "NOORDER", "BECOME", "DUMP", "KEY", "NORESETLOGS", "BEFORE", 056 "EACH", "LANGUAGE", "NORMAL", "BLOCK", "ENABLE", "LAYER", "NOSORT", "BODY", "END", "LINK", "NUMERIC", "CACHE", 057 "ESCAPE", "LISTS", "OFF", "CANCEL", "EVENTS", "LOGFILE", "OLD", "CASCADE", "EXCEPT", "MANAGE", "ONLY", "CHANGE", 058 "EXCEPTIONS", "MANUAL", "OPEN", "CHARACTER", "EXEC", "MAX", "OPTIMAL", "CHECKPOINT", "EXPLAIN", "MAXDATAFILES", 059 "OWN", "CLOSE", "EXECUTE", "MAXINSTANCES", "PACKAGE", "COBOL", "EXTENT", "MAXLOGFILES", "PARALLEL", "COMMIT", 060 "EXTERNALLY", "MAXLOGHISTORY", "PCTINCREASE", "COMPILE", "FETCH", "MAXLOGMEMBERS", "PCTUSED", "CONSTRAINT", 061 "FLUSH", "MAXTRANS", "PLAN", "CONSTRAINTS", "FREELIST", "MAXVALUE", "PLI", "CONTENTS", "FREELISTS", "MIN", 062 "PRECISION", "CONTINUE", "FORCE", "MINEXTENTS", "PRIMARY", "CONTROLFILE", "FOREIGN", "MINVALUE", "PRIVATE", 063 "COUNT", "FORTRAN", "MODULE", "PROCEDURE", "PROFILE", "SAVEPOINT", "SQLSTATE", "TRACING", "QUOTA", "SCHEMA", 064 "STATEMENT_ID", "TRANSACTION", "READ", "SCN", "STATISTICS", "TRIGGERS", "REAL", "SECTION", "STOP", "TRUNCATE", 065 "RECOVER", "SEGMENT", "STORAGE", "UNDER", "REFERENCES", "SEQUENCE", "SUM", "UNLIMITED", "REFERENCING", "SHARED", 066 "SWITCH", "UNTIL", "RESETLOGS", "SNAPSHOT", "SYSTEM", "USE", "RESTRICTED", "SOME", "TABLES", "USING", "REUSE", 067 "SORT", "TABLESPACE", "WHEN", "ROLE", "SQL", "TEMPORARY", "WRITE", "ROLES", "SQLCODE", "THREAD", "WORK", "ROLLBACK", 068 "SQLERROR", "TIME", "ABORT", "BETWEEN", "CRASH", "DIGITS", "ACCEPT", "BINARY_INTEGER", "CREATE", "DISPOSE", "ACCESS", 069 "BODY", "CURRENT", "DISTINCT", "ADD", "BOOLEAN", "CURRVAL", "DO", "ALL", "BY", "CURSOR", "DROP", "ALTER", "CASE", "DATABASE", 070 "ELSE", "AND", "CHAR", "DATA_BASE", "ELSIF", "ANY", "CHAR_BASE", "DATE", "END", "ARRAY", "CHECK", "DBA", "ENTRY", "ARRAYLEN", 071 "CLOSE", "DEBUGOFF", "EXCEPTION", "AS", "CLUSTER", "DEBUGON", "EXCEPTION_INIT", "ASC", "CLUSTERS", "DECLARE", "EXISTS", 072 "ASSERT", "COLAUTH", "DECIMAL", "EXIT", "ASSIGN", "COLUMNS", "DEFAULT", "FALSE", "AT", "COMMIT", "DEFINITION", "FETCH", 073 "AUTHORIZATION", "COMPRESS", "DELAY", "FLOAT", "AVG", "CONNECT", "DELETE", "FOR", "BASE_TABLE", "CONSTANT", "DELTA", "FORM", 074 "BEGIN", "COUNT", "DESC", "FROM", "FUNCTION", "NEW", "RELEASE", "SUM", "GENERIC", "NEXTVAL", "REMR", "TABAUTH", 075 "GOTO", "NOCOMPRESS", "RENAME", "TABLE", "GRANT", "NOT", "RESOURCE", "TABLES", "GROUP", "NULL", "RETURN", "TASK", "HAVING", 076 "NUMBER", "REVERSE", "TERMINATE", "IDENTIFIED", "NUMBER_BASE", "REVOKE", "THEN", "IF", "OF", "ROLLBACK", "TO", "IN", "ON", 077 "ROWID", "TRUE", "INDEX", "OPEN", "ROWLABEL", "TYPE", "INDEXES", "OPTION", "ROWNUM", "UNION", "INDICATOR", "OR", "ROWTYPE", 078 "UNIQUE", "INSERT", "ORDER", "RUN", "UPDATE", "INTEGER", "OTHERS", "SAVEPOINT", "USE", "INTERSECT", "OUT", "SCHEMA", "VALUES", 079 "INTO", "PACKAGE", "SELECT", "VARCHAR", "IS", "PARTITION", "SEPARATE", "VARCHAR2", "LEVEL", "PCTFREE", "SET", "VARIANCE", 080 "LIKE", "POSITIVE", "SIZE", "VIEW", "LIMITED", "PRAGMA", "SMALLINT", "VIEWS", "LOOP", "PRIOR", "SPACE", "WHEN", "MAX", "PRIVATE", 081 "SQL", "WHERE", "MIN", "PROCEDURE", "SQLCODE", "WHILE", "MINUS", "PUBLIC", "SQLERRM", "WITH", "MLSLABEL", "RAISE", "START", 082 "WORK", "MOD", "RANGE", "STATEMENT", "XOR", "MODE", "REAL", "STDDEV", "NATURAL", "RECORD", "SUBTYPE", "GEN", "KP", "L", 083 "NA", "NC", "ND", "NL", "NM", "NR", "NS", "NT", "NZ", "TTC", "UPI", "O", "S", "XA" 084 085 ); 086 087 public OracleDialect() { 088 this(LimitOffsetProcessor.ORACLE); 089 } 090 091 public OracleDialect(LimitOffsetProcessor limitOffsetProcessor) { 092 this(new KeywordWrap(false, true, keywords, "\"", "\""), limitOffsetProcessor); 093 } 094 095 public OracleDialect(KeywordWrap keywordWrap, LimitOffsetProcessor limitOffsetProcessor) { 096 super(keywordWrap, limitOffsetProcessor); 097 } 098 099 @Override 100 public String forInsertEntityBatch(TableInfo tableInfo, Collection<?> entities) { 101 /** 102 * INSERT ALL 103 * INTO t (col1, col2, col3) VALUES ('val1_1', 'val1_2', 'val1_3') 104 * INTO t (col1, col2, col3) VALUES ('val2_1', 'val2_2', 'val2_3') 105 * INTO t (col1, col2, col3) VALUES ('val3_1', 'val3_2', 'val3_3') 106 * . 107 * . 108 * . 109 * SELECT 1 FROM DUAL; 110 */ 111 StringBuilder sql = new StringBuilder(); 112 sql.append(INSERT_ALL); 113 String[] insertColumns = tableInfo.obtainInsertColumns(null, false); 114 String[] warpedInsertColumns = new String[insertColumns.length]; 115 for (int i = 0; i < insertColumns.length; i++) { 116 warpedInsertColumns[i] = wrap(insertColumns[i]); 117 } 118 119 120 Map<String, String> onInsertColumns = tableInfo.getOnInsertColumns(); 121 for (int i = 0; i < entities.size(); i++) { 122 sql.append(INTO).append(tableInfo.getWrapSchemaAndTableName(this, OperateType.INSERT)); 123 sql.append(BLANK).append(BRACKET_LEFT).append(StringUtil.join(DELIMITER, warpedInsertColumns)).append(BRACKET_RIGHT); 124 sql.append(VALUES); 125 126 StringJoiner stringJoiner = new StringJoiner(DELIMITER, BRACKET_LEFT, BRACKET_RIGHT); 127 for (String insertColumn : insertColumns) { 128 if (onInsertColumns != null && onInsertColumns.containsKey(insertColumn)) { 129 //直接读取 onInsert 配置的值,而不用 "?" 代替 130 stringJoiner.add(onInsertColumns.get(insertColumn)); 131 } else { 132 stringJoiner.add(PLACEHOLDER); 133 } 134 } 135 sql.append(stringJoiner); 136 } 137 138 return sql.append(INSERT_ALL_END).toString(); 139 } 140 141 142 @Override 143 public String forInsertBatchWithFirstRowColumns(String schema, String tableName, List<Row> rows) { 144 /** 145 * INSERT ALL 146 * INTO t (col1, col2, col3) VALUES ('val1_1', 'val1_2', 'val1_3') 147 * INTO t (col1, col2, col3) VALUES ('val2_1', 'val2_2', 'val2_3') 148 * INTO t (col1, col2, col3) VALUES ('val3_1', 'val3_2', 'val3_3') 149 * . 150 * . 151 * . 152 * SELECT 1 FROM DUAL; 153 */ 154 StringBuilder fields = new StringBuilder(); 155 Row firstRow = rows.get(0); 156 Set<String> attrs = RowCPI.getInsertAttrs(firstRow); 157 int index = 0; 158 for (String column : attrs) { 159 fields.append(wrap(column)); 160 if (index != attrs.size() - 1) { 161 fields.append(SqlConsts.DELIMITER); 162 } 163 index++; 164 } 165 166 StringBuilder sql = new StringBuilder(); 167 sql.append(INSERT_ALL); 168 169 String table = getRealTable(tableName, OperateType.INSERT); 170 String tableNameWrap = StringUtil.isNotBlank(schema) 171 ? wrap(getRealSchema(schema, table, OperateType.INSERT)) + REFERENCE + wrap(table) 172 : wrap(table); 173 String questionStrings = SqlUtil.buildSqlParamPlaceholder(attrs.size()); 174 175 for (int i = 0; i < rows.size(); i++) { 176 sql.append(INTO).append(tableNameWrap); 177 sql.append(BLANK).append(BRACKET_LEFT).append(fields).append(BRACKET_RIGHT); 178 sql.append(VALUES).append(questionStrings); 179 } 180 181 return sql.append(INSERT_ALL_END).toString(); 182 } 183 184}