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