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