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