mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-07 09:08:24 +08:00
Merge remote-tracking branch 'origin/main' into main
This commit is contained in:
commit
6c04a13cd8
@ -1,4 +1,4 @@
|
||||
- **`update(entity)`**:根据主键来更新数据,若实体类属性数据为 `null`,该属性不会新到数据库。
|
||||
- **`update(entity)`**:根据主键来更新数据,若实体类属性数据为 `null`,该属性不会更新到数据库。
|
||||
- **`update(entity, ignoreNulls)`**:根据主键来更新数据到数据库。
|
||||
- **`updateByMap(entity, whereConditions)`**:根据 Map 构建的条件来更新数据。
|
||||
- **`updateByMap(entity, ignoreNulls, whereConditions)`**:根据 Map 构建的条件来更新数据。
|
||||
|
||||
@ -17,10 +17,10 @@ public class AccountController {
|
||||
|
||||
@Autowired
|
||||
AccountMapper accountMapper;
|
||||
|
||||
|
||||
@GetMapping("/accounts")
|
||||
List<Account> selectList() {
|
||||
|
||||
|
||||
//构造 QueryWrapper,也支持使用 QueryWrapper.create() 构造,效果相同
|
||||
QueryWrapper query = new QueryWrapper();
|
||||
query.where(ACCOUNT.ID.ge(100));
|
||||
@ -148,7 +148,7 @@ select(column("abc")) --> SELECT abc
|
||||
,然后在自己的项目里进行自定义扩展。
|
||||
|
||||
|
||||
| 支持的函数 | 函数说明 |
|
||||
| 支持的函数 | 函数说明 |
|
||||
| -------- | -------- |
|
||||
| count | 查询数据总量 |
|
||||
| distinct | 对指定列进行去重 |
|
||||
@ -339,11 +339,11 @@ QueryWrapper wrapper = QueryWrapper.create()
|
||||
其查询生成的 Sql 如下:
|
||||
|
||||
```sql
|
||||
SELECT `id`,
|
||||
(CASE WHEN `id` >= 2 THEN 'x2'
|
||||
WHEN `id` >= 1 THEN 'x1'
|
||||
ELSE 'x100'
|
||||
END) AS `xName`
|
||||
SELECT `id`,
|
||||
(CASE WHEN `id` >= 2 THEN 'x2'
|
||||
WHEN `id` >= 1 THEN 'x1'
|
||||
ELSE 'x100'
|
||||
END) AS `xName`
|
||||
FROM `tb_account`
|
||||
```
|
||||
|
||||
@ -374,12 +374,12 @@ QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
其查询生成的 Sql 如下:
|
||||
|
||||
```sql
|
||||
SELECT *,
|
||||
(CASE `id`
|
||||
WHEN 100 THEN 100
|
||||
WHEN 200 THEN 200
|
||||
ELSE 300 END) AS `result`
|
||||
FROM `tb_account`
|
||||
SELECT *,
|
||||
(CASE `id`
|
||||
WHEN 100 THEN 100
|
||||
WHEN 200 THEN 200
|
||||
ELSE 300 END) AS `result`
|
||||
FROM `tb_account`
|
||||
WHERE `user_name` LIKE ?
|
||||
```
|
||||
|
||||
@ -408,7 +408,7 @@ System.out.println(query.toSQL());
|
||||
生成的 SQL 如下:
|
||||
|
||||
```sql
|
||||
WITH CTE AS (SELECT * FROM `tb_article` WHERE `id` >= 100)
|
||||
WITH CTE AS (SELECT * FROM `tb_article` WHERE `id` >= 100)
|
||||
SELECT * FROM `tb_account` WHERE `sex` = 1
|
||||
```
|
||||
|
||||
@ -432,8 +432,8 @@ System.out.println(query.toSQL());
|
||||
生成的 SQL 如下:
|
||||
|
||||
```sql
|
||||
WITH xxx(id, name)
|
||||
AS (VALUES (a, b) UNION (SELECT * FROM `tb_article` WHERE `id` >= 200))
|
||||
WITH xxx(id, name)
|
||||
AS (VALUES (a, b) UNION (SELECT * FROM `tb_article` WHERE `id` >= 200))
|
||||
SELECT * FROM `tb_account` WHERE `sex` = 1
|
||||
```
|
||||
|
||||
@ -458,7 +458,7 @@ System.out.println(query.toSQL());
|
||||
生成的 SQL 如下:
|
||||
|
||||
```sql
|
||||
WITH RECURSIVE CTE AS (SELECT * FROM `tb_article` WHERE `id` >= 100)
|
||||
WITH RECURSIVE CTE AS (SELECT * FROM `tb_article` WHERE `id` >= 100)
|
||||
SELECT * FROM `tb_account` WHERE `sex` = 1
|
||||
```
|
||||
|
||||
@ -485,9 +485,9 @@ System.out.println(query.toSQL());
|
||||
生成的 SQL 如下:
|
||||
|
||||
```sql
|
||||
WITH RECURSIVE
|
||||
CTE AS (SELECT * FROM `tb_article` WHERE `id` >= 100),
|
||||
xxx(id, name) AS (VALUES (a, b) UNION (SELECT * FROM `tb_article` WHERE `id` >= 200))
|
||||
WITH RECURSIVE
|
||||
CTE AS (SELECT * FROM `tb_article` WHERE `id` >= 100),
|
||||
xxx(id, name) AS (VALUES (a, b) UNION (SELECT * FROM `tb_article` WHERE `id` >= 200))
|
||||
SELECT * FROM `tb_account` WHERE `sex` = 1
|
||||
```
|
||||
|
||||
@ -509,7 +509,7 @@ QueryWrapper queryWrapper=QueryWrapper.create()
|
||||
```sql
|
||||
SELECT * FROM tb_account
|
||||
WHERE id >= ?
|
||||
AND user_name LIKE ?
|
||||
AND user_name LIKE ?
|
||||
```
|
||||
|
||||
## where 动态条件 1
|
||||
@ -526,7 +526,7 @@ QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
|
||||
```sql
|
||||
SELECT * FROM tb_account
|
||||
WHERE user_name LIKE ?
|
||||
WHERE user_name LIKE ?
|
||||
```
|
||||
|
||||
## where 动态条件 2
|
||||
@ -543,7 +543,7 @@ QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
|
||||
```sql
|
||||
SELECT * FROM tb_account
|
||||
WHERE user_name LIKE ?
|
||||
WHERE user_name LIKE ?
|
||||
```
|
||||
|
||||
## where 动态条件 3
|
||||
@ -560,7 +560,7 @@ QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
|
||||
```sql
|
||||
SELECT * FROM tb_account
|
||||
WHERE id >= ?
|
||||
WHERE id >= ?
|
||||
```
|
||||
|
||||
## where 动态条件 4
|
||||
@ -569,7 +569,7 @@ WHERE id >= ?
|
||||
String name = null;
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.select().from(ACCOUNT)
|
||||
.where(ACCOUNT.ID.ge(100))
|
||||
.where(ACCOUNT.ID.ge(100))
|
||||
.and(ACCOUNT.USER_NAME.like(name, If::hasText));
|
||||
```
|
||||
|
||||
@ -577,7 +577,7 @@ QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
|
||||
```sql
|
||||
SELECT * FROM tb_account
|
||||
WHERE id >= ?
|
||||
WHERE id >= ?
|
||||
```
|
||||
|
||||
## where select
|
||||
@ -749,9 +749,9 @@ QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
其查询生成的 Sql 如下:
|
||||
|
||||
```sql
|
||||
SELECT * FROM tb_account LEFT JOIN tb_article
|
||||
ON tb_account.id = tb_article.account_id AND tb_account.age = ?
|
||||
WHERE tb_account.age >= ?
|
||||
SELECT * FROM tb_account LEFT JOIN tb_article
|
||||
ON tb_account.id = tb_article.account_id AND tb_account.age = ?
|
||||
WHERE tb_account.age >= ?
|
||||
```
|
||||
|
||||
## join select
|
||||
@ -772,12 +772,47 @@ QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
其查询生成的 Sql 如下:
|
||||
|
||||
```sql
|
||||
SELECT * FROM tb_account
|
||||
SELECT * FROM tb_account
|
||||
LEFT JOIN (SELECT * FROM tb_article WHERE id >= ? ) AS a
|
||||
ON tb_account.id = a.id
|
||||
WHERE tb_account.age >= ?
|
||||
ON tb_account.id = a.id
|
||||
WHERE tb_account.age >= ?
|
||||
```
|
||||
|
||||
## join 自己
|
||||
|
||||
```java
|
||||
QueryWrapper queryWrapper = QueryWrapper.create();
|
||||
queryWrapper.from(ACCOUNT)
|
||||
.leftJoin(ACCOUNT).as("a1").on(ACCOUNT.ID.eq(ACCOUNT.PARENT_ID))
|
||||
.leftJoin(ACCOUNT).as("a2").on(ACCOUNT.ID.eq(ACCOUNT.PARENT_ID))
|
||||
.where(ACCOUNT.ID.ge(1));
|
||||
```
|
||||
|
||||
其查询生成的 Sql 如下:
|
||||
|
||||
```sql
|
||||
SELECT * FROM `tb_account`
|
||||
LEFT JOIN `tb_account` AS `a1`
|
||||
ON `a1`.`id` = `tb_account`.`parent_id`
|
||||
LEFT JOIN `tb_account` AS `a2`
|
||||
ON `a2`.`id` = `tb_account`.`parent_id`
|
||||
WHERE `tb_account`.`id` >= ?
|
||||
```
|
||||
|
||||
若 `tb_account` 表带有逻辑删除,那么其生成的 SQL 如下:
|
||||
|
||||
```sql
|
||||
SELECT * FROM `tb_account`
|
||||
LEFT JOIN `tb_account` AS `a1`
|
||||
ON `a1`.`id` = `tb_account`.`parent_id` AND `a1`.`is_delete` = ?
|
||||
LEFT JOIN `tb_account` AS `a2`
|
||||
ON `a2`.`id` = `tb_account`.`parent_id` AND `a2`.`is_delete` = ?
|
||||
WHERE `tb_account`.`id` >= ?
|
||||
AND `tb_account`.`is_delete` = ?
|
||||
```
|
||||
|
||||
> 关于逻辑删除更多文档请参考 [这里](../core/logic-delete.html)。
|
||||
|
||||
## union, union all
|
||||
|
||||
```java
|
||||
@ -792,8 +827,8 @@ QueryWrapper query = new QueryWrapper()
|
||||
其查询生成的 Sql 如下:
|
||||
|
||||
```sql
|
||||
(SELECT id FROM tb_account ORDER BY id DESC)
|
||||
UNION (SELECT id FROM tb_article)
|
||||
(SELECT id FROM tb_account ORDER BY id DESC)
|
||||
UNION (SELECT id FROM tb_article)
|
||||
UNION ALL (SELECT id FROM tb_article)
|
||||
```
|
||||
|
||||
@ -894,12 +929,12 @@ System.out.println(query.toSQL());
|
||||
SQL 输入内容如下:
|
||||
|
||||
```sql
|
||||
SELECT * FROM `tb_article`
|
||||
LEFT JOIN `tb_account` AS `a` ON `a`.`id` = `tb_article`.`account_id`
|
||||
WHERE `a`.`id` >= 100 AND
|
||||
(`a`.`id` >= 100
|
||||
OR `a`.`age` > 200
|
||||
AND `tb_article`.`account_id` = 200
|
||||
SELECT * FROM `tb_article`
|
||||
LEFT JOIN `tb_account` AS `a` ON `a`.`id` = `tb_article`.`account_id`
|
||||
WHERE `a`.`id` >= 100 AND
|
||||
(`a`.`id` >= 100
|
||||
OR `a`.`age` > 200
|
||||
AND `tb_article`.`account_id` = 200
|
||||
OR (`a`.`id` LIKE '%a%' )
|
||||
)
|
||||
```
|
||||
|
||||
@ -7,7 +7,19 @@
|
||||
- 熟悉 Spring Boot 及相关框架
|
||||
- 熟悉 Java 构建工具,比如 Maven
|
||||
|
||||
## Hello World
|
||||
> 当前章节涉及到的源码已经全部上传到:https://gitee.com/Suomm/mybatis-flex-test ,在开始之前,
|
||||
> 您也可以先下载到本地,导入到 idea 开发工具后,在继续看文档。
|
||||
|
||||
|
||||
|
||||
## Hello World 视频教程
|
||||
|
||||
<iframe width="100%" height="400px" src="//player.bilibili.com/player.html?aid=955526987&bvid=BV1yW4y1Z74j&cid=1187300793&page=1&autoplay=no" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
|
||||
|
||||
MyBatis-Flex 视频系列详情:https://www.bilibili.com/video/BV1yW4y1Z74j
|
||||
|
||||
## Hello World 文档
|
||||
|
||||
|
||||
**第 1 步:创建数据库表**
|
||||
|
||||
|
||||
@ -271,7 +271,7 @@ public interface BaseMapper<T> {
|
||||
// === 改(update) ===
|
||||
|
||||
/**
|
||||
* 根据主键来更新数据,若实体类属性数据为 {@code null},该属性不会新到数据库。
|
||||
* 根据主键来更新数据,若实体类属性数据为 {@code null},该属性不会更新到数据库。
|
||||
*
|
||||
* @param entity 数据内容,必须包含有主键
|
||||
* @return 受影响的行数
|
||||
|
||||
@ -18,6 +18,7 @@ package com.mybatisflex.core.dialect;
|
||||
|
||||
import com.mybatisflex.core.FlexGlobalConfig;
|
||||
import com.mybatisflex.core.dialect.impl.CommonsDialectImpl;
|
||||
import com.mybatisflex.core.dialect.impl.DmDialect;
|
||||
import com.mybatisflex.core.dialect.impl.OracleDialect;
|
||||
import com.mybatisflex.core.util.ObjectUtil;
|
||||
import org.apache.ibatis.util.MapUtil;
|
||||
@ -109,6 +110,7 @@ public class DialectFactory {
|
||||
case CSIIDB:
|
||||
return new CommonsDialectImpl(KeywordWrap.BACK_QUOTE, LimitOffsetProcessor.MYSQL);
|
||||
case DM:
|
||||
return new DmDialect();
|
||||
case ORACLE:
|
||||
return new OracleDialect();
|
||||
case GAUSS:
|
||||
|
||||
@ -19,7 +19,6 @@ import com.mybatisflex.core.constant.SqlConsts;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -110,14 +109,35 @@ public class KeywordWrap {
|
||||
return prefix + keyword + suffix;
|
||||
}
|
||||
|
||||
if (keywords.contains(keyword.toUpperCase(Locale.ENGLISH))) {
|
||||
if (keywordsToUpperCase) {
|
||||
return prefix + keyword.toUpperCase() + suffix;
|
||||
} else {
|
||||
return prefix + keyword + suffix;
|
||||
}
|
||||
}
|
||||
return keyword;
|
||||
keyword = keywordsToUpperCase ? keyword.toUpperCase() : keyword;
|
||||
return keywords.contains(keyword) ? (prefix + keyword + suffix) : keyword;
|
||||
}
|
||||
|
||||
public boolean isCaseSensitive() {
|
||||
return caseSensitive;
|
||||
}
|
||||
|
||||
public void setCaseSensitive(boolean caseSensitive) {
|
||||
this.caseSensitive = caseSensitive;
|
||||
}
|
||||
|
||||
public boolean isKeywordsToUpperCase() {
|
||||
return keywordsToUpperCase;
|
||||
}
|
||||
|
||||
public void setKeywordsToUpperCase(boolean keywordsToUpperCase) {
|
||||
this.keywordsToUpperCase = keywordsToUpperCase;
|
||||
}
|
||||
|
||||
public Set<String> getKeywords() {
|
||||
return keywords;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public String getSuffix() {
|
||||
return suffix;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
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;
|
||||
|
||||
public class DmDialect extends CommonsDialectImpl {
|
||||
|
||||
//https://docs.oracle.com/cd/A97630_01/appdev.920/a42525/apb.htm
|
||||
public static final Set<String> keywords = CollectionUtil.newHashSet(
|
||||
"ACCESS", "ELSE", "MODIFY", "START", "ADD", "EXCLUSIVE", "NOAUDIT", "SELECT",
|
||||
"ALL", "EXISTS", "NOCOMPRESS", "SESSION", "ALTER", "FILE", "NOT", "SET", "AND", "FLOAT",
|
||||
"NOTFOUND", "SHARE", "ANY", "FOR", "NOWAIT", "SIZE", "ARRAYLEN", "FROM", "NULL", "SMALLINT",
|
||||
"AS", "GRANT", "NUMBER", "SQLBUF", "ASC", "GROUP", "OF", "SUCCESSFUL", "AUDIT", "HAVING",
|
||||
"OFFLINE", "SYNONYM", "BETWEEN", "IDENTIFIED", "ON", "SYSDATE", "BY", "IMMEDIATE", "ONLINE",
|
||||
"TABLE", "CHAR", "IN", "OPTION", "THEN", "CHECK", "INCREMENT", "OR", "TO", "CLUSTER", "INDEX",
|
||||
"ORDER", "TRIGGER", "COLUMN", "INITIAL", "PCTFREE", "UID", "COMMENT", "INSERT", "PRIOR",
|
||||
"UNION", "COMPRESS", "INTEGER", "PRIVILEGES", "UNIQUE", "CONNECT", "INTERSECT", "PUBLIC",
|
||||
"UPDATE", "CREATE", "INTO", "RAW", "USER", "CURRENT", "IS", "RENAME", "VALIDATE", "DATE", "LEVEL",
|
||||
"RESOURCE", "VALUES", "DECIMAL", "LIKE", "REVOKE", "VARCHAR", "DEFAULT", "LOCK", "ROW", "VARCHAR2",
|
||||
"DELETE", "LONG", "ROWID", "VIEW", "DESC", "MAXEXTENTS", "ROWLABEL", "WHENEVER", "DISTINCT", "MINUS",
|
||||
"ROWNUM", "WHERE", "DROP", "MODE", "ROWS", "WITH", "ADMIN", "CURSOR", "FOUND", "MOUNT", "AFTER", "CYCLE",
|
||||
"FUNCTION", "NEXT", "ALLOCATE", "DATABASE", "GO", "NEW", "ANALYZE", "DATAFILE", "GOTO", "NOARCHIVELOG",
|
||||
"ARCHIVE", "DBA", "GROUPS", "NOCACHE", "ARCHIVELOG", "DEC", "INCLUDING", "NOCYCLE", "AUTHORIZATION",
|
||||
"DECLARE", "INDICATOR", "NOMAXVALUE", "AVG", "DISABLE", "INITRANS", "NOMINVALUE", "BACKUP", "DISMOUNT",
|
||||
"INSTANCE", "NONE", "BEGIN", "DOUBLE", "INT", "NOORDER", "BECOME", "DUMP", "KEY", "NORESETLOGS", "BEFORE",
|
||||
"EACH", "LANGUAGE", "NORMAL", "BLOCK", "ENABLE", "LAYER", "NOSORT", "BODY", "END", "LINK", "NUMERIC", "CACHE",
|
||||
"ESCAPE", "LISTS", "OFF", "CANCEL", "EVENTS", "LOGFILE", "OLD", "CASCADE", "EXCEPT", "MANAGE", "ONLY", "CHANGE",
|
||||
"EXCEPTIONS", "MANUAL", "OPEN", "CHARACTER", "EXEC", "MAX", "OPTIMAL", "CHECKPOINT", "EXPLAIN", "MAXDATAFILES",
|
||||
"OWN", "CLOSE", "EXECUTE", "MAXINSTANCES", "PACKAGE", "COBOL", "EXTENT", "MAXLOGFILES", "PARALLEL", "COMMIT",
|
||||
"EXTERNALLY", "MAXLOGHISTORY", "PCTINCREASE", "COMPILE", "FETCH", "MAXLOGMEMBERS", "PCTUSED", "CONSTRAINT",
|
||||
"FLUSH", "MAXTRANS", "PLAN", "CONSTRAINTS", "FREELIST", "MAXVALUE", "PLI", "CONTENTS", "FREELISTS", "MIN",
|
||||
"PRECISION", "CONTINUE", "FORCE", "MINEXTENTS", "PRIMARY", "CONTROLFILE", "FOREIGN", "MINVALUE", "PRIVATE",
|
||||
"COUNT", "FORTRAN", "MODULE", "PROCEDURE", "PROFILE", "SAVEPOINT", "SQLSTATE", "TRACING", "QUOTA", "SCHEMA",
|
||||
"STATEMENT_ID", "TRANSACTION", "READ", "SCN", "STATISTICS", "TRIGGERS", "REAL", "SECTION", "STOP", "TRUNCATE",
|
||||
"RECOVER", "SEGMENT", "STORAGE", "UNDER", "REFERENCES", "SEQUENCE", "SUM", "UNLIMITED", "REFERENCING", "SHARED",
|
||||
"SWITCH", "UNTIL", "RESETLOGS", "SNAPSHOT", "SYSTEM", "USE", "RESTRICTED", "SOME", "TABLES", "USING", "REUSE",
|
||||
"SORT", "TABLESPACE", "WHEN", "ROLE", "SQL", "TEMPORARY", "WRITE", "ROLES", "SQLCODE", "THREAD", "WORK", "ROLLBACK",
|
||||
"SQLERROR", "TIME", "ABORT", "BETWEEN", "CRASH", "DIGITS", "ACCEPT", "BINARY_INTEGER", "CREATE", "DISPOSE", "ACCESS",
|
||||
"BODY", "CURRENT", "DISTINCT", "ADD", "BOOLEAN", "CURRVAL", "DO", "ALL", "BY", "CURSOR", "DROP", "ALTER", "CASE", "DATABASE",
|
||||
"ELSE", "AND", "CHAR", "DATA_BASE", "ELSIF", "ANY", "CHAR_BASE", "DATE", "END", "ARRAY", "CHECK", "DBA", "ENTRY", "ARRAYLEN",
|
||||
"CLOSE", "DEBUGOFF", "EXCEPTION", "AS", "CLUSTER", "DEBUGON", "EXCEPTION_INIT", "ASC", "CLUSTERS", "DECLARE", "EXISTS",
|
||||
"ASSERT", "COLAUTH", "DECIMAL", "EXIT", "ASSIGN", "COLUMNS", "DEFAULT", "FALSE", "AT", "COMMIT", "DEFINITION", "FETCH",
|
||||
"AUTHORIZATION", "COMPRESS", "DELAY", "FLOAT", "AVG", "CONNECT", "DELETE", "FOR", "BASE_TABLE", "CONSTANT", "DELTA", "FORM",
|
||||
"BEGIN", "COUNT", "DESC", "FROM", "FUNCTION", "NEW", "RELEASE", "SUM", "GENERIC", "NEXTVAL", "REMR", "TABAUTH",
|
||||
"GOTO", "NOCOMPRESS", "RENAME", "TABLE", "GRANT", "NOT", "RESOURCE", "TABLES", "GROUP", "NULL", "RETURN", "TASK", "HAVING",
|
||||
"NUMBER", "REVERSE", "TERMINATE", "IDENTIFIED", "NUMBER_BASE", "REVOKE", "THEN", "IF", "OF", "ROLLBACK", "TO", "IN", "ON",
|
||||
"ROWID", "TRUE", "INDEX", "OPEN", "ROWLABEL", "TYPE", "INDEXES", "OPTION", "ROWNUM", "UNION", "INDICATOR", "OR", "ROWTYPE",
|
||||
"UNIQUE", "INSERT", "ORDER", "RUN", "UPDATE", "INTEGER", "OTHERS", "SAVEPOINT", "USE", "INTERSECT", "OUT", "SCHEMA", "VALUES",
|
||||
"INTO", "PACKAGE", "SELECT", "VARCHAR", "IS", "PARTITION", "SEPARATE", "VARCHAR2", "LEVEL", "PCTFREE", "SET", "VARIANCE",
|
||||
"LIKE", "POSITIVE", "SIZE", "VIEW", "LIMITED", "PRAGMA", "SMALLINT", "VIEWS", "LOOP", "PRIOR", "SPACE", "WHEN", "MAX", "PRIVATE",
|
||||
"SQL", "WHERE", "MIN", "PROCEDURE", "SQLCODE", "WHILE", "MINUS", "PUBLIC", "SQLERRM", "WITH", "MLSLABEL", "RAISE", "START",
|
||||
"WORK", "MOD", "RANGE", "STATEMENT", "XOR", "MODE", "REAL", "STDDEV", "NATURAL", "RECORD", "SUBTYPE", "GEN", "KP", "L",
|
||||
"NA", "NC", "ND", "NL", "NM", "NR", "NS", "NT", "NZ", "TTC", "UPI", "O", "S", "XA"
|
||||
|
||||
);
|
||||
|
||||
public DmDialect() {
|
||||
//达梦 默认情况下,是支持 MySQL 的分页语法的
|
||||
this(LimitOffsetProcessor.MYSQL);
|
||||
}
|
||||
|
||||
public DmDialect(LimitOffsetProcessor limitOffsetProcessor) {
|
||||
//只有以上的关键字时,会添加 "" 包裹
|
||||
this(new KeywordWrap(false, false, keywords, "\"", "\""), limitOffsetProcessor);
|
||||
}
|
||||
|
||||
public DmDialect(KeywordWrap keywordWrap, LimitOffsetProcessor limitOffsetProcessor) {
|
||||
super(keywordWrap, limitOffsetProcessor);
|
||||
}
|
||||
|
||||
}
|
||||
@ -50,6 +50,8 @@ public enum LocalizedFormats implements Localizable {
|
||||
|
||||
|
||||
ENTITY_VERSION_NULL("The version value of entity \"{0}\" must not be null."),
|
||||
|
||||
KEY_GENERATOR_BLANK("The name of key generator must not be null or blank."),
|
||||
;
|
||||
|
||||
private final String sourceFormat;
|
||||
|
||||
@ -111,6 +111,8 @@ public class FieldQueryManager {
|
||||
} else if (fieldType == Set.class) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
// avoid NPE
|
||||
return ClassUtil.newInstance(fieldType);
|
||||
}
|
||||
|
||||
if (ClassUtil.canInstance(fieldType.getModifiers())) {
|
||||
|
||||
@ -15,9 +15,12 @@
|
||||
*/
|
||||
package com.mybatisflex.core.keygen;
|
||||
|
||||
import com.mybatisflex.core.exception.FlexExceptions;
|
||||
import com.mybatisflex.core.exception.locale.LocalizedFormats;
|
||||
import com.mybatisflex.core.keygen.impl.FlexIDKeyGenerator;
|
||||
import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator;
|
||||
import com.mybatisflex.core.keygen.impl.UUIDKeyGenerator;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@ -43,9 +46,12 @@ public class KeyGeneratorFactory {
|
||||
* 获取 主键生成器
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
* @return 主键生成器
|
||||
*/
|
||||
public static IKeyGenerator getKeyGenerator(String name) {
|
||||
if (StringUtil.isBlank(name)){
|
||||
throw FlexExceptions.wrap(LocalizedFormats.KEY_GENERATOR_BLANK);
|
||||
}
|
||||
return KEY_GENERATOR_MAP.get(name.trim());
|
||||
}
|
||||
|
||||
|
||||
@ -16,7 +16,8 @@
|
||||
package com.mybatisflex.core.logicdelete;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.query.QueryCondition;
|
||||
import com.mybatisflex.core.query.QueryColumn;
|
||||
import com.mybatisflex.core.query.QueryTable;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
|
||||
@ -41,10 +42,10 @@ public abstract class AbstractLogicDeleteProcessor implements LogicDeleteProcess
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildQueryCondition(QueryWrapper queryWrapper, TableInfo tableInfo) {
|
||||
queryWrapper.and(QueryCondition.create(tableInfo.getSchema(), tableInfo.getTableName(), tableInfo.getLogicDeleteColumn()
|
||||
, EQUALS
|
||||
, getLogicNormalValue()));
|
||||
public void buildQueryCondition(QueryWrapper queryWrapper, TableInfo tableInfo, String joinTableAlias) {
|
||||
QueryTable queryTable = new QueryTable(tableInfo.getSchema(), tableInfo.getTableName()).as(joinTableAlias);
|
||||
QueryColumn queryColumn = new QueryColumn(queryTable, tableInfo.getLogicDeleteColumn());
|
||||
queryWrapper.and(queryColumn.eq(getLogicNormalValue()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -44,11 +44,11 @@ public interface LogicDeleteProcessor {
|
||||
|
||||
/**
|
||||
* 用于构建通过 {@link QueryWrapper} 查询数据时的内容。
|
||||
*
|
||||
* @param queryWrapper 条件构造器
|
||||
* @param queryWrapper 条件构造器
|
||||
* @param tableInfo 表信息
|
||||
* @param joinTableAlias join table 的别名
|
||||
*/
|
||||
void buildQueryCondition(QueryWrapper queryWrapper, TableInfo tableInfo);
|
||||
void buildQueryCondition(QueryWrapper queryWrapper, TableInfo tableInfo, String joinTableAlias);
|
||||
|
||||
/**
|
||||
* 获取逻辑删除列未删除标记值。
|
||||
|
||||
@ -18,6 +18,7 @@ package com.mybatisflex.core.logicdelete;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.query.QueryColumn;
|
||||
import com.mybatisflex.core.query.QueryTable;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
|
||||
@ -35,8 +36,9 @@ public abstract class NullableColumnLogicDeleteProcessor extends AbstractLogicDe
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildQueryCondition(QueryWrapper queryWrapper, TableInfo tableInfo) {
|
||||
QueryColumn queryColumn = new QueryColumn(tableInfo.getSchema(), tableInfo.getTableName(), tableInfo.getLogicDeleteColumn());
|
||||
public void buildQueryCondition(QueryWrapper queryWrapper, TableInfo tableInfo, String joinTableAlias) {
|
||||
QueryTable queryTable = new QueryTable(tableInfo.getSchema(), tableInfo.getTableName()).as(joinTableAlias);
|
||||
QueryColumn queryColumn = new QueryColumn(queryTable, tableInfo.getLogicDeleteColumn());
|
||||
queryWrapper.and(queryColumn.isNull());
|
||||
}
|
||||
|
||||
|
||||
@ -18,8 +18,6 @@ package com.mybatisflex.core.logicdelete.impl;
|
||||
import com.mybatisflex.core.FlexGlobalConfig;
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.logicdelete.AbstractLogicDeleteProcessor;
|
||||
import com.mybatisflex.core.query.QueryCondition;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
|
||||
import static com.mybatisflex.core.constant.SqlConsts.EQUALS;
|
||||
@ -27,6 +25,7 @@ import static com.mybatisflex.core.constant.SqlConsts.SINGLE_QUOTE;
|
||||
|
||||
/**
|
||||
* 默认逻辑删除处理器。
|
||||
* @author michael
|
||||
*/
|
||||
public class DefaultLogicDeleteProcessor extends AbstractLogicDeleteProcessor {
|
||||
|
||||
@ -40,13 +39,6 @@ public class DefaultLogicDeleteProcessor extends AbstractLogicDeleteProcessor {
|
||||
return dialect.wrap(logicColumn) + EQUALS + prepareValue(getLogicDeletedValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildQueryCondition(QueryWrapper queryWrapper, TableInfo tableInfo) {
|
||||
queryWrapper.where(QueryCondition.create(tableInfo.getSchema(), tableInfo.getTableName(), tableInfo.getLogicDeleteColumn()
|
||||
, EQUALS
|
||||
, getLogicNormalValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getLogicNormalValue() {
|
||||
return FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete();
|
||||
|
||||
@ -317,14 +317,14 @@ public class QueryConditionBuilder<Wrapper extends QueryWrapper> {
|
||||
* @param queryWrapper
|
||||
* @return
|
||||
*/
|
||||
public Wrapper in(Wrapper queryWrapper) {
|
||||
public Wrapper in(QueryWrapper queryWrapper) {
|
||||
if (queryWrapper != null) {
|
||||
this.queryWrapper.addWhereQueryCondition(queryColumn.in(queryWrapper), connector);
|
||||
}
|
||||
return this.queryWrapper;
|
||||
}
|
||||
|
||||
public <T> Wrapper in(Wrapper queryWrapper, Predicate<T> when) {
|
||||
public <T> Wrapper in(QueryWrapper queryWrapper, Predicate<T> when) {
|
||||
if (queryWrapper != null) {
|
||||
this.queryWrapper.addWhereQueryCondition(queryColumn.in(queryWrapper, when), connector);
|
||||
}
|
||||
@ -398,14 +398,14 @@ public class QueryConditionBuilder<Wrapper extends QueryWrapper> {
|
||||
*
|
||||
* @param queryWrapper
|
||||
*/
|
||||
public Wrapper notIn(Wrapper queryWrapper) {
|
||||
public Wrapper notIn(QueryWrapper queryWrapper) {
|
||||
if (queryWrapper != null) {
|
||||
this.queryWrapper.addWhereQueryCondition(queryColumn.notIn(queryWrapper), connector);
|
||||
}
|
||||
return this.queryWrapper;
|
||||
}
|
||||
|
||||
public <T> Wrapper notIn(Wrapper queryWrapper, Predicate<T> when) {
|
||||
public <T> Wrapper notIn(QueryWrapper queryWrapper, Predicate<T> when) {
|
||||
if (queryWrapper != null) {
|
||||
this.queryWrapper.addWhereQueryCondition(queryColumn.notIn(queryWrapper, when), connector);
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ import java.util.Objects;
|
||||
*/
|
||||
public class QueryTable implements CloneSupport<QueryTable> {
|
||||
|
||||
protected int tableDefHashCode = 0;
|
||||
protected String schema;
|
||||
protected String name;
|
||||
protected String alias;
|
||||
@ -36,6 +37,8 @@ public class QueryTable implements CloneSupport<QueryTable> {
|
||||
}
|
||||
|
||||
public QueryTable(TableDef tableDef) {
|
||||
// TableDef的标识符号,0:不确定标识
|
||||
this.tableDefHashCode = tableDef.hashCode();
|
||||
this.schema = tableDef.getSchema();
|
||||
this.name = tableDef.getTableName();
|
||||
}
|
||||
@ -77,6 +80,13 @@ public class QueryTable implements CloneSupport<QueryTable> {
|
||||
return StringUtil.isNotBlank(schema) ? schema + "." + name : name;
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public QueryTable as(String alias) {
|
||||
this.alias = alias;
|
||||
@ -87,15 +97,16 @@ public class QueryTable implements CloneSupport<QueryTable> {
|
||||
if (table == null) {
|
||||
return false;
|
||||
}
|
||||
if (StringUtil.isNotBlank(alias)
|
||||
&& StringUtil.isNotBlank(table.alias)
|
||||
&& (Objects.equals(alias, table.alias))) {
|
||||
if (StringUtil.isNotBlank(alias) && StringUtil.isNotBlank(table.alias) && (Objects.equals(alias, table.alias))) {
|
||||
return false;
|
||||
}
|
||||
//比较对象都有tableDef标记,就用标记比对, 否则就用名称比对
|
||||
if (tableDefHashCode != 0 && table.tableDefHashCode != 0) {
|
||||
return tableDefHashCode == table.tableDefHashCode;
|
||||
}
|
||||
return Objects.equals(name, table.name);
|
||||
}
|
||||
|
||||
|
||||
Object[] getValueArray() {
|
||||
return FlexConsts.EMPTY_ARRAY;
|
||||
}
|
||||
@ -110,14 +121,9 @@ public class QueryTable implements CloneSupport<QueryTable> {
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "QueryTable{" +
|
||||
"schema='" + schema + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", alias='" + alias + '\'' +
|
||||
'}';
|
||||
return "QueryTable{" + "schema='" + schema + '\'' + ", name='" + name + '\'' + ", alias='" + alias + '\'' + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -39,7 +39,7 @@ public class StringQueryColumn extends QueryColumn {
|
||||
|
||||
@Override
|
||||
String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
return content;
|
||||
return content + WrapperUtil.buildAlias(alias, dialect);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -779,7 +779,8 @@ public class TableInfo {
|
||||
|
||||
//逻辑删除
|
||||
if (StringUtil.isNotBlank(getLogicDeleteColumnOrSkip())) {
|
||||
LogicDeleteManager.getProcessor().buildQueryCondition(queryWrapper, this);
|
||||
String joinTableAlias = CPI.getContext(queryWrapper, "joinTableAlias");
|
||||
LogicDeleteManager.getProcessor().buildQueryCondition(queryWrapper, this, joinTableAlias);
|
||||
}
|
||||
|
||||
//多租户
|
||||
@ -800,11 +801,14 @@ public class TableInfo {
|
||||
if (CollectionUtil.isNotEmpty(joins)) {
|
||||
for (Join join : joins) {
|
||||
QueryTable joinQueryTable = CPI.getJoinQueryTable(join);
|
||||
|
||||
//join select
|
||||
if (joinQueryTable instanceof SelectQueryTable) {
|
||||
QueryWrapper childQuery = ((SelectQueryTable) joinQueryTable).getQueryWrapper();
|
||||
doAppendConditions(entity, childQuery);
|
||||
} else {
|
||||
|
||||
}
|
||||
//join table
|
||||
else {
|
||||
String nameWithSchema = joinQueryTable.getNameWithSchema();
|
||||
if (StringUtil.isNotBlank(nameWithSchema)) {
|
||||
TableInfo tableInfo = TableInfoFactory.ofTableName(nameWithSchema);
|
||||
@ -812,8 +816,10 @@ public class TableInfo {
|
||||
QueryCondition joinQueryCondition = CPI.getJoinQueryCondition(join);
|
||||
QueryWrapper newWrapper = QueryWrapper.create()
|
||||
.where(joinQueryCondition);
|
||||
CPI.putContext(newWrapper, "joinTableAlias", joinQueryTable.getAlias());
|
||||
tableInfo.appendConditions(entity, newWrapper);
|
||||
CPI.setJoinQueryCondition(join, CPI.getWhereQueryCondition(newWrapper));
|
||||
QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(newWrapper);
|
||||
CPI.setJoinQueryCondition(join, whereQueryCondition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ public class DateUtil {
|
||||
public static final String dateMillisecondPattern = "yyyy-MM-dd HH:mm:ss SSS";
|
||||
public static final String dateCSTPattern = "EEE MMM dd HH:mm:ss zzz yyyy";
|
||||
|
||||
private static final ThreadLocal<HashMap<String, SimpleDateFormat>> TL = ThreadLocal.withInitial(() -> new HashMap<>());
|
||||
private static final ThreadLocal<HashMap<String, SimpleDateFormat>> TL = ThreadLocal.withInitial(HashMap::new);
|
||||
|
||||
private static final Map<String, DateTimeFormatter> dateTimeFormatters = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
@ -12,3 +12,5 @@ UPDATE_ONLY_SUPPORT_1_TABLE=\"UpdateByQuery\" \u4ec5\u652f\u6301\u4f20\u5165 1 \
|
||||
UPDATE_OR_DELETE_NOT_ALLOW=\u6267\u884c "update" \u6216\u8005 "delete" \u7684 SQL \u65f6\uff0c\u4e0d\u5141\u8bb8\u5168\u8868\u64cd\u4f5c\uff0c\u5fc5\u987b\u8981\u6709 where \u6761\u4ef6\u3002
|
||||
|
||||
ENTITY_VERSION_NULL=\u4e50\u89c2\u9501\u5b9e\u4f53\u7c7b\u5fc5\u987b\u8bbe\u7f6e version \u7684\u503c\uff1a{0}\u3002
|
||||
|
||||
KEY_GENERATOR_BLANK=\u4e3b\u952e\u751f\u6210\u5668\u7684\u540d\u79f0\u4e0d\u80fd\u4e3a null \u6216\u8005\u7a7a\u5b57\u7b26\u4e32\u3002
|
||||
|
||||
@ -116,6 +116,20 @@ public class AccountTester {
|
||||
System.out.println(accounts);
|
||||
}
|
||||
|
||||
/**
|
||||
* issues https://gitee.com/mybatis-flex/mybatis-flex/issues/I7QD29
|
||||
*/
|
||||
@Test
|
||||
public void testLeftJoinSelfForLogicDelete() {
|
||||
QueryWrapper queryWrapper = QueryWrapper.create();
|
||||
queryWrapper.from(ACCOUNT)
|
||||
.leftJoin(ACCOUNT).as("a1").on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
|
||||
.leftJoin(ACCOUNT).as("a2").on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
|
||||
.where(ACCOUNT.ID.ge(1));
|
||||
List<Article> accounts = articleMapper.selectListByQuery(queryWrapper);
|
||||
System.out.println(accounts);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSelectAsToDTO() {
|
||||
|
||||
29
readme.md
29
readme.md
@ -2,6 +2,35 @@
|
||||
|
||||
# MyBatis-Flex is an elegant Mybatis Enhancement Framework.
|
||||
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://search.maven.org/search?q=mybatis-flex%20mybatis-flex">
|
||||
<img src="https://img.shields.io/maven-central/v/com.mybatis-flex/parent?label=Maven%20Central" alt="Maven" />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.apache.org/licenses/LICENSE-2.0.txt">
|
||||
<img src="https://img.shields.io/:license-Apache2-blue.svg" alt="Apache 2" />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html">
|
||||
<img src="https://img.shields.io/badge/JDK-8-green.svg" alt="jdk-8" />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html">
|
||||
<img src="https://img.shields.io/badge/JDK-11-green.svg" alt="jdk-11" />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html">
|
||||
<img src="https://img.shields.io/badge/JDK-17-green.svg" alt="jdk-17" />
|
||||
</a>
|
||||
<br />
|
||||
<img src="https://img.shields.io/badge/SpringBoot-v2.x-blue">
|
||||
<img src="https://img.shields.io/badge/SpringBoot-v3.x-blue">
|
||||
<a target="_blank" href='https://github.com/noear/solon'><img src="https://img.shields.io/badge/Solon-v2.x-blue"></a>
|
||||
<br />
|
||||
<a target="_blank" href='https://gitee.com/mybatis-flex/mybatis-flex'>
|
||||
<img src='https://gitee.com/mybatis-flex/mybatis-flex/badge/star.svg' alt='Gitee star'/>
|
||||
</a>
|
||||
<a target="_blank" href='https://github.com/mybatis-flex/mybatis-flex'>
|
||||
<img src="https://img.shields.io/github/stars/mybatis-flex/mybatis-flex.svg?logo=github" alt="Github star"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Features
|
||||
|
||||
- 1、MyBatis-Flex is very lightweight, and it only depends on Mybatis and no other third-party dependencies
|
||||
|
||||
28
readme_zh.md
28
readme_zh.md
@ -3,6 +3,34 @@
|
||||
|
||||
# MyBatis-Flex: 一个优雅的 MyBatis 增强框架
|
||||
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://search.maven.org/search?q=mybatis-flex%20mybatis-flex">
|
||||
<img src="https://img.shields.io/maven-central/v/com.mybatis-flex/parent?label=Maven%20Central" alt="Maven" />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.apache.org/licenses/LICENSE-2.0.txt">
|
||||
<img src="https://img.shields.io/:license-Apache2-blue.svg" alt="Apache 2" />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html">
|
||||
<img src="https://img.shields.io/badge/JDK-8-green.svg" alt="jdk-8" />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html">
|
||||
<img src="https://img.shields.io/badge/JDK-11-green.svg" alt="jdk-11" />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html">
|
||||
<img src="https://img.shields.io/badge/JDK-17-green.svg" alt="jdk-17" />
|
||||
</a>
|
||||
<br />
|
||||
<img src="https://img.shields.io/badge/SpringBoot-v2.x-blue">
|
||||
<img src="https://img.shields.io/badge/SpringBoot-v3.x-blue">
|
||||
<a target="_blank" href='https://gitee.com/noear/solon'><img src="https://img.shields.io/badge/Solon-v2.x-blue"></a>
|
||||
<br />
|
||||
<a target="_blank" href='https://gitee.com/mybatis-flex/mybatis-flex'>
|
||||
<img src='https://gitee.com/mybatis-flex/mybatis-flex/badge/star.svg' alt='Gitee star'/>
|
||||
</a>
|
||||
<a target="_blank" href='https://github.com/mybatis-flex/mybatis-flex'>
|
||||
<img src="https://img.shields.io/github/stars/mybatis-flex/mybatis-flex.svg?logo=github" alt="Github star"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 特征
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user