diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/DialectFactory.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/DialectFactory.java index 5f37b20a..b894b0c3 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/DialectFactory.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/DialectFactory.java @@ -38,7 +38,6 @@ public class DialectFactory { * 此 map 中,用于覆盖系统的方言实现 */ private static final Map dialectMap = new EnumMap<>(DbType.class); - /** * 通过设置当前线程的数据库类型,以达到在代码执行时随时切换方言的功能 */ @@ -142,9 +141,9 @@ public class DialectFactory { case DB2_1005: return new DB2105Dialect(KeywordWrap.NONE, DB2105Dialect.DB2105LimitOffsetProcessor.DB2105); case SQLSERVER: - return new CommonsDialectImpl(KeywordWrap.NONE_CASE_SENSITIVE, LimitOffsetProcessor.SQLSERVER); + return new SqlserverDialectImpl(KeywordWrap.SQUARE_BRACKETS, LimitOffsetProcessor.SQLSERVER); case SQLSERVER_2005: - return new CommonsDialectImpl(KeywordWrap.NONE_CASE_SENSITIVE, LimitOffsetProcessor.SQLSERVER_2005); + return new Sqlserver2005DialectImpl(KeywordWrap.SQUARE_BRACKETS, LimitOffsetProcessor.SQLSERVER_2005); case INFORMIX: return new CommonsDialectImpl(KeywordWrap.NONE, LimitOffsetProcessor.INFORMIX); case SINODB: diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/KeywordWrap.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/KeywordWrap.java index 8096a6ac..68dc4d8d 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/KeywordWrap.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/KeywordWrap.java @@ -18,6 +18,7 @@ package com.mybatisflex.core.dialect; import com.mybatisflex.core.constant.SqlConsts; import com.mybatisflex.core.util.StringUtil; +import java.util.Arrays; import java.util.Collections; import java.util.Set; import java.util.stream.Collectors; @@ -88,6 +89,7 @@ public class KeywordWrap { this(false, Collections.emptySet(), prefix, suffix); } + public KeywordWrap(boolean caseSensitive, String prefix, String suffix) { this(caseSensitive, Collections.emptySet(), prefix, suffix); } @@ -103,7 +105,8 @@ public class KeywordWrap { this.suffix = suffix; } - public KeywordWrap(boolean caseSensitive, boolean keywordsToUpperCase, Set keywords, String prefix, String suffix) { + public KeywordWrap(boolean caseSensitive, boolean keywordsToUpperCase, Set keywords, String prefix, + String suffix) { this.caseSensitive = caseSensitive; this.keywordsToUpperCase = keywordsToUpperCase; this.keywords = keywords.stream().map(String::toUpperCase).collect(Collectors.toSet()); @@ -128,6 +131,37 @@ public class KeywordWrap { } } + //数据scheme table 包装 根据 . 分割后分别包装 + public String wrapKeyword(String keyword) { + StringBuilder resultBuilder = new StringBuilder(); + String[] split = keyword.split("\\."); + if (split != null && split.length > 0) { + Arrays.asList(split) + .forEach(f -> resultBuilder.append(prefix).append(f).append(suffix).append(".")); + return resultBuilder.toString().substring(0, resultBuilder.length() - 1); + } else { + return prefix + keyword + suffix; + } + } + + //sqlserver 转义 scheme table colums 包装 根据 . 分割后分别包装 + public String wrap4Sqlserver(String keyword) { + if (StringUtil.isBlank(keyword) || SqlConsts.ASTERISK.equals(keyword.trim())) { + return keyword; + } + + if (caseSensitive || keywords.isEmpty()) { + return wrapKeyword(keyword); + } + + if (keywordsToUpperCase) { + keyword = keyword.toUpperCase(); + return keywords.contains(keyword) ? wrapKeyword(keyword) : keyword; + } else { + return keywords.contains(keyword.toUpperCase()) ? wrapKeyword(keyword) : keyword; + } + } + public boolean isCaseSensitive() { return caseSensitive; } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/impl/Sqlserver2005DialectImpl.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/impl/Sqlserver2005DialectImpl.java new file mode 100644 index 00000000..b6f60e25 --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/impl/Sqlserver2005DialectImpl.java @@ -0,0 +1,51 @@ +package com.mybatisflex.core.dialect.impl; + +import com.mybatisflex.core.dialect.KeywordWrap; +import com.mybatisflex.core.dialect.LimitOffsetProcessor; +import com.mybatisflex.core.util.CollectionUtil; + +import java.util.Set; + +import static com.mybatisflex.core.constant.SqlConsts.ASTERISK; + +public class Sqlserver2005DialectImpl extends CommonsDialectImpl { + + public static final Set keywords = CollectionUtil.newHashSet("ADD", "ALL", "ALTER", "AND", "ANY", "AS", + "ASC", "AUTHORIZATION", "BACKUP", "BEGIN", "BETWEEN", "BREAK", "BROWSE", "BULK", "BY", "CASCADE", "CASE", + "CHECK", "CHECKPOINT", "CLOSE", "CLUSTERED", "COALESCE", "COLLATE", "COLUMN", "COMMIT", "COMPUTE", "CONSTRAINT", + "CONTAINS", "CONTAINSTABLE", "CONTINUE", "CONVERT", "CREATE", "CROSS", "CURRENT", "CURRENT_DATE", + "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "DATABASE", "DBCC", "DEALLOCATE", "DECLARE", + "DEFAULT", "DELETE", "DENY", "DESC", "DISTINCT", "DISTRIBUTED", "DOUBLE", "DROP", "DUMP", "ELSE", "END", + "ERRLVL", "ESCAPE", "EXCEPT", "EXEC", "EXECUTE", "EXISTS", "EXIT", "EXTERNAL", "FETCH", "FILE", "FILLFACTOR", + "FOR", "FOREIGN", "FREETEXT", "FREETEXTTABLE", "FROM", "FULL", "FUNCTION", "GOTO", "GRANT", "GROUP", "HAVING", + "HOLDLOCK", "IDENTITY", "IDENTITY_INSERT", "IDENTITYCOL", "IF", "IN", "INDEX", "INNER", "INSERT", "INTERSECT", + "INTO", "IS", "JOIN", "KEY", "KILL", "LEFT", "LIKE", "LINENO", "LOAD", "MAX", "MIN", "NATIONAL", "NOCHECK", + "NONCLUSTERED", "NOT", "NULL", "NULLIF", "OF", "OFF", "OFFSETS", "ON", "OPEN", "OPENDATASOURCE", "OPENQUERY", + "OPENROWSET", "OPTION", "OR", "ORDER", "OUTER", "OVER", "PERCENT", "PLAN", "PRECISION", "PREPARE", "PRIMARY", + "PRINT", "PROC", "PROCEDURE", "PUBLIC", "RAISERROR", "READ", "READTEXT", "RECONFIGURE", "REFERENCES", + "REPEATABLE", "RESTORE", "RESTRICT", "RETURN", "REVOKE", "RIGHT", "ROLLBACK", "ROWCOUNT", "ROWGUIDCOL", "RULE", + "SAVE", "SCHEMA", "SECURITYAUDIT", "SELECT", "SESSION_USER", "SET", "SETUSER", "SHUTDOWN", "SOME", "STATISTICS", + "SYSTEM_USER", "TABLE", "TABLESAMPLE", "TEXTSIZE", "THEN", "TO", "TOP", "TRAN", "TRANSACTION", "TRIGGER", + "TRUNCATE", "TSEQUAL", "UNION", "UNIQUE", "UNPIVOT", "UPDATE", "UPDATETEXT", "USE", "USER", "USING", "VALUES", + "VARYING", "VIEW", "WAITFOR", "WHEN", "WHERE", "WHILE", "WITH", "WITHIN GROUP", "WRITETEXT", "XACT_ABORT", + "XLOCK"); + + public Sqlserver2005DialectImpl() { + this(LimitOffsetProcessor.SQLSERVER_2005); + } + + public Sqlserver2005DialectImpl(LimitOffsetProcessor limitOffsetProcessor) { + this(new KeywordWrap(true, false, keywords, "[", "]"), limitOffsetProcessor); + } + + public Sqlserver2005DialectImpl(KeywordWrap keywordWrap, LimitOffsetProcessor limitOffsetProcessor) { + super(keywordWrap, limitOffsetProcessor); + } + + @Override + public String wrap(String keyword) { + return ASTERISK.equals(keyword) ? keyword : keywordWrap.wrap4Sqlserver(keyword); + } + +} + diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/impl/SqlserverDialectImpl.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/impl/SqlserverDialectImpl.java new file mode 100644 index 00000000..4a3852a5 --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/dialect/impl/SqlserverDialectImpl.java @@ -0,0 +1,51 @@ +package com.mybatisflex.core.dialect.impl; + +import com.mybatisflex.core.dialect.KeywordWrap; +import com.mybatisflex.core.dialect.LimitOffsetProcessor; +import com.mybatisflex.core.util.CollectionUtil; + +import java.util.Set; + +import static com.mybatisflex.core.constant.SqlConsts.ASTERISK; + +public class SqlserverDialectImpl extends CommonsDialectImpl { + + public static final Set keywords = CollectionUtil.newHashSet("ADD", "ALL", "ALTER", "AND", "ANY", "AS", + "ASC", "BACKUP", "BEGIN", "BETWEEN", "BREAK", "BROWSE", "BULK", "BY", "CASCADE", "CASE", "CHECK", "CHECKPOINT", + "CLOSE", "CLUSTERED", "COALESCE", "COLUMN", "COMMIT", "COMMITTED", "COMPUTE", "CONFIRM", "CONNECT", + "CONSTRAINT", "CONTAINS", "CONTAINSTABLE", "CONTINUE", "CONVERT", "CREATE", "CROSS", "CURRENT", "CURRENT_DATE", + "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "DATABASE", "DBCC", "DEALLOCATE", "DECLARE", + "DEFAULT", "DELETE", "DENY", "DESC", "DISTINCT", "DISTRIBUTED", "DOUBLE", "DROP", "DUMP", "ELSE", "END", + "ERRLVL", "ESCAPE", "EXCEPT", "EXEC", "EXECUTE", "EXISTS", "EXIT", "EXTERNAL", "FETCH", "FILE", "FILLFACTOR", + "FOR", "FOREIGN", "FREETEXT", "FREETEXTTABLE", "FROM", "FULL", "FUNCTION", "GOTO", "GRANT", "GROUP", "HAVING", + "HOLDLOCK", "IDENTITY", "IDENTITY_INSERT", "IDENTITYCOL", "IF", "IN", "INDEX", "INNER", "INSERT", "INTERSECT", + "INTO", "IS", "JOIN", "KEY", "KILL", "LEFT", "LIKE", "LINENO", "LOAD", "MAX", "MIN", "NATIONAL", "NOCHECK", + "NONCLUSTERED", "NOT", "NULL", "NULLIF", "OF", "OFF", "OFFSETS", "ON", "OPEN", "OPENDATASOURCE", "OPENQUERY", + "OPENROWSET", "OPTION", "OR", "ORDER", "OUTER", "OVER", "PERCENT", "PIPE", "PLAN", "PRECISION", "PREPARE", + "PRIMARY", "PRINT", "PRIVILEGES", "PROC", "PROCEDURE", "PUBLIC", "RAISERROR", "READ", "READTEXT", "RECONFIGURE", + "REFERENCES", "REPEATABLE", "RESTORE", "RESTRICT", "RETURN", "REVOKE", "RIGHT", "ROLLBACK", "ROWCOUNT", + "ROWGUIDCOL", "RULE", "SAVE", "SCHEMA", "SECURITYAUDIT", "SELECT", "SEMANTICKEYPHRASETABLE", + "SEMANTICSIMILARITYDETAILSTABLE", "SEMANTICSIMILARITYTABLE", "SESSION_USER", "SET", "SETUSER", "SHUTDOWN", + "SOME", "STATISTICS", "SYSTEM_USER", "TABLE", "TABLESAMPLE", "TEXTSIZE", "THEN", "TO", "TOP", "TRAN", + "TRANSACTION", "TRIGGER", "TRUNCATE", "TSEQUAL", "UNION", "UNIQUE", "UNPIVOT", "UPDATE", "UPDATETEXT", "USE", + "USER", "USING", "VALUES", "VARYING", "VIEW", "WAITFOR", "WHEN", "WHERE", "WHILE", "WITH", "WITHIN GROUP", + "WRITETEXT", "XACT_ABORT", "XLOCK"); + + public SqlserverDialectImpl() { + this(LimitOffsetProcessor.SQLSERVER); + } + + public SqlserverDialectImpl(LimitOffsetProcessor limitOffsetProcessor) { + this(new KeywordWrap(true, false, keywords, "[", "]"), limitOffsetProcessor); + } + + public SqlserverDialectImpl(KeywordWrap keywordWrap, LimitOffsetProcessor limitOffsetProcessor) { + super(keywordWrap, limitOffsetProcessor); + } + + @Override + public String wrap(String keyword) { + return ASTERISK.equals(keyword) ? keyword : keywordWrap.wrap4Sqlserver(keyword); + } +} + diff --git a/mybatis-flex-core/src/test/java/com/mybatisflex/coretest/SqlServer2005DialectTester.java b/mybatis-flex-core/src/test/java/com/mybatisflex/coretest/SqlServer2005DialectTester.java index a7a33a1b..9919abca 100644 --- a/mybatis-flex-core/src/test/java/com/mybatisflex/coretest/SqlServer2005DialectTester.java +++ b/mybatis-flex-core/src/test/java/com/mybatisflex/coretest/SqlServer2005DialectTester.java @@ -20,6 +20,8 @@ import com.mybatisflex.core.dialect.IDialect; import com.mybatisflex.core.dialect.KeywordWrap; import com.mybatisflex.core.dialect.LimitOffsetProcessor; import com.mybatisflex.core.dialect.impl.CommonsDialectImpl; +import com.mybatisflex.core.dialect.impl.Sqlserver2005DialectImpl; +import com.mybatisflex.core.dialect.impl.SqlserverDialectImpl; import com.mybatisflex.core.query.QueryWrapper; import org.junit.Assert; import org.junit.Test; @@ -42,11 +44,65 @@ public class SqlServer2005DialectTester { IDialect dialect = new CommonsDialectImpl(KeywordWrap.SQUARE_BRACKETS, LimitOffsetProcessor.SQLSERVER_2005); String sql = dialect.forSelectByQuery(query); System.out.println(sql); - Assert.assertEquals("WITH temp_datas AS(" + - "SELECT ROW_NUMBER() OVER ( ORDER BY [id] DESC) as __rn, * FROM [tb_account] WHERE [id] IN (?, ?) AND [sex] = ?" + + Assert.assertEquals("WITH temp_datas AS(" + + "SELECT ROW_NUMBER() OVER ( ORDER BY [id] DESC) as __rn, * FROM [tb_account] WHERE [id] IN (?, ?) AND [sex] = ?" + + ") " + "SELECT * FROM temp_datas WHERE __rn BETWEEN 11 AND 20 ORDER BY __rn", sql); } + @Test + public void testSelectSqlSqlserver2005() { + QueryWrapper query = new QueryWrapper().select() + .from("TEST.dbo.tb_account") + .where(ACCOUNT.ID.in("100", "200")) + .and(ACCOUNT.SEX.eq(1)) + .orderBy(ACCOUNT.ID.desc()) + .limit(0, 10); + + IDialect dialect = new Sqlserver2005DialectImpl(KeywordWrap.SQUARE_BRACKETS, + LimitOffsetProcessor.SQLSERVER_2005); + String sql = dialect.forSelectByQuery(query); + System.out.println(sql); + Assert.assertEquals( + "WITH temp_datas AS(SELECT ROW_NUMBER() OVER ( ORDER BY [tb_account].[id] DESC) as __rn, * FROM [TEST].[dbo].[tb_account] WHERE [tb_account].[id] IN (?, ?) AND [tb_account].[sex] = ?) SELECT * FROM temp_datas WHERE __rn BETWEEN 1 AND 10 ORDER BY __rn", + sql); + } + + @Test + public void testSelectSqlSqlserver() { + QueryWrapper query = new QueryWrapper().select() + .from("TEST.dbo.tb_account") + .where(ACCOUNT.ID.in("100", "200")) + .and(ACCOUNT.SEX.eq(1)) + .orderBy(ACCOUNT.ID.desc()) + .limit(0, 10); + + IDialect dialect = new SqlserverDialectImpl(KeywordWrap.SQUARE_BRACKETS, + LimitOffsetProcessor.SQLSERVER); + String sql = dialect.forSelectByQuery(query); + System.out.println(sql); + Assert.assertEquals( + "SELECT * FROM [TEST].[dbo].[tb_account] WHERE [tb_account].[id] IN (?, ?) AND [tb_account].[sex] = ? ORDER BY [tb_account].[id] DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY", + sql); + } + + @Test + public void testSelectSqlSqlserver1() { + QueryWrapper query = new QueryWrapper().select() + .from("tb_account") + .where(ACCOUNT.ID.in("100", "200")) + .and(ACCOUNT.SEX.eq(1)) + .orderBy(ACCOUNT.ID.desc()) + .limit(0, 10); + + IDialect dialect = new SqlserverDialectImpl(KeywordWrap.SQUARE_BRACKETS, + LimitOffsetProcessor.SQLSERVER); + String sql = dialect.forSelectByQuery(query); + System.out.println(sql); + Assert.assertEquals( + "SELECT * FROM [tb_account] WHERE [id] IN (?, ?) AND [sex] = ? ORDER BY [id] DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY", + sql); + } }