update docs

This commit is contained in:
开源海哥 2023-03-25 13:58:29 +08:00
parent 2f54af9122
commit f06fa5b6df
23 changed files with 10979 additions and 0 deletions

View File

@ -0,0 +1,41 @@
// node_modules/vitepress/dist/client/theme-default/index.js
import "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/styles/fonts.css";
// node_modules/vitepress/dist/client/theme-default/without-fonts.js
import "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/styles/vars.css";
import "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/styles/base.css";
import "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/styles/utils.css";
import "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/styles/components/custom-block.css";
import "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/styles/components/vp-code.css";
import "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/styles/components/vp-code-group.css";
import "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/styles/components/vp-doc.css";
import "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/styles/components/vp-sponsor.css";
import VPBadge from "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/components/VPBadge.vue";
import Layout from "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/Layout.vue";
import { default as default2 } from "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/components/VPHomeHero.vue";
import { default as default3 } from "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/components/VPHomeFeatures.vue";
import { default as default4 } from "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/components/VPHomeSponsors.vue";
import { default as default5 } from "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/components/VPDocAsideSponsors.vue";
import { default as default6 } from "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/components/VPTeamPage.vue";
import { default as default7 } from "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/components/VPTeamPageTitle.vue";
import { default as default8 } from "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/components/VPTeamPageSection.vue";
import { default as default9 } from "/Users/michael/work/git/mybatis-flex/docs/node_modules/vitepress/dist/client/theme-default/components/VPTeamMembers.vue";
var theme = {
Layout,
enhanceApp: ({ app }) => {
app.component("Badge", VPBadge);
}
};
var without_fonts_default = theme;
export {
default5 as VPDocAsideSponsors,
default3 as VPHomeFeatures,
default2 as VPHomeHero,
default4 as VPHomeSponsors,
default9 as VPTeamMembers,
default6 as VPTeamPage,
default8 as VPTeamPageSection,
default7 as VPTeamPageTitle,
without_fonts_default as default
};
//# sourceMappingURL=@theme_index.js.map

View File

@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../../node_modules/vitepress/dist/client/theme-default/index.js", "../../../node_modules/vitepress/dist/client/theme-default/without-fonts.js"],
"sourcesContent": ["import './styles/fonts.css';\nexport * from './without-fonts';\nexport { default as default } from './without-fonts';\n", "import './styles/vars.css';\nimport './styles/base.css';\nimport './styles/utils.css';\nimport './styles/components/custom-block.css';\nimport './styles/components/vp-code.css';\nimport './styles/components/vp-code-group.css';\nimport './styles/components/vp-doc.css';\nimport './styles/components/vp-sponsor.css';\nimport VPBadge from './components/VPBadge.vue';\nimport Layout from './Layout.vue';\n// Note: if we add more optional components here, i.e. components that are not\n// used in the theme by default unless the user imports them, make sure to update\n// the `lazyDefaultThemeComponentsRE` regex in src/node/build/bundle.ts.\nexport { default as VPHomeHero } from './components/VPHomeHero.vue';\nexport { default as VPHomeFeatures } from './components/VPHomeFeatures.vue';\nexport { default as VPHomeSponsors } from './components/VPHomeSponsors.vue';\nexport { default as VPDocAsideSponsors } from './components/VPDocAsideSponsors.vue';\nexport { default as VPTeamPage } from './components/VPTeamPage.vue';\nexport { default as VPTeamPageTitle } from './components/VPTeamPageTitle.vue';\nexport { default as VPTeamPageSection } from './components/VPTeamPageSection.vue';\nexport { default as VPTeamMembers } from './components/VPTeamMembers.vue';\nconst theme = {\n Layout,\n enhanceApp: ({ app }) => {\n app.component('Badge', VPBadge);\n }\n};\nexport default theme;\n"],
"mappings": ";AAAA,OAAO;;;ACAP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO,aAAa;AACpB,OAAO,YAAY;AAInB,SAAoB,WAAXA,gBAA6B;AACtC,SAAoB,WAAXA,gBAAiC;AAC1C,SAAoB,WAAXA,gBAAiC;AAC1C,SAAoB,WAAXA,gBAAqC;AAC9C,SAAoB,WAAXA,gBAA6B;AACtC,SAAoB,WAAXA,gBAAkC;AAC3C,SAAoB,WAAXA,gBAAoC;AAC7C,SAAoB,WAAXA,gBAAgC;AACzC,IAAM,QAAQ;AAAA,EACV;AAAA,EACA,YAAY,CAAC,EAAE,IAAI,MAAM;AACrB,QAAI,UAAU,SAAS,OAAO;AAAA,EAClC;AACJ;AACA,IAAO,wBAAQ;",
"names": ["default"]
}

View File

@ -0,0 +1,162 @@
// node_modules/@vue/devtools-api/lib/esm/env.js
function getDevtoolsGlobalHook() {
return getTarget().__VUE_DEVTOOLS_GLOBAL_HOOK__;
}
function getTarget() {
return typeof navigator !== "undefined" && typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {};
}
var isProxyAvailable = typeof Proxy === "function";
// node_modules/@vue/devtools-api/lib/esm/const.js
var HOOK_SETUP = "devtools-plugin:setup";
var HOOK_PLUGIN_SETTINGS_SET = "plugin:settings:set";
// node_modules/@vue/devtools-api/lib/esm/time.js
var supported;
var perf;
function isPerformanceSupported() {
var _a;
if (supported !== void 0) {
return supported;
}
if (typeof window !== "undefined" && window.performance) {
supported = true;
perf = window.performance;
} else if (typeof global !== "undefined" && ((_a = global.perf_hooks) === null || _a === void 0 ? void 0 : _a.performance)) {
supported = true;
perf = global.perf_hooks.performance;
} else {
supported = false;
}
return supported;
}
function now() {
return isPerformanceSupported() ? perf.now() : Date.now();
}
// node_modules/@vue/devtools-api/lib/esm/proxy.js
var ApiProxy = class {
constructor(plugin, hook) {
this.target = null;
this.targetQueue = [];
this.onQueue = [];
this.plugin = plugin;
this.hook = hook;
const defaultSettings = {};
if (plugin.settings) {
for (const id in plugin.settings) {
const item = plugin.settings[id];
defaultSettings[id] = item.defaultValue;
}
}
const localSettingsSaveId = `__vue-devtools-plugin-settings__${plugin.id}`;
let currentSettings = Object.assign({}, defaultSettings);
try {
const raw = localStorage.getItem(localSettingsSaveId);
const data = JSON.parse(raw);
Object.assign(currentSettings, data);
} catch (e) {
}
this.fallbacks = {
getSettings() {
return currentSettings;
},
setSettings(value) {
try {
localStorage.setItem(localSettingsSaveId, JSON.stringify(value));
} catch (e) {
}
currentSettings = value;
},
now() {
return now();
}
};
if (hook) {
hook.on(HOOK_PLUGIN_SETTINGS_SET, (pluginId, value) => {
if (pluginId === this.plugin.id) {
this.fallbacks.setSettings(value);
}
});
}
this.proxiedOn = new Proxy({}, {
get: (_target, prop) => {
if (this.target) {
return this.target.on[prop];
} else {
return (...args) => {
this.onQueue.push({
method: prop,
args
});
};
}
}
});
this.proxiedTarget = new Proxy({}, {
get: (_target, prop) => {
if (this.target) {
return this.target[prop];
} else if (prop === "on") {
return this.proxiedOn;
} else if (Object.keys(this.fallbacks).includes(prop)) {
return (...args) => {
this.targetQueue.push({
method: prop,
args,
resolve: () => {
}
});
return this.fallbacks[prop](...args);
};
} else {
return (...args) => {
return new Promise((resolve) => {
this.targetQueue.push({
method: prop,
args,
resolve
});
});
};
}
}
});
}
async setRealTarget(target) {
this.target = target;
for (const item of this.onQueue) {
this.target.on[item.method](...item.args);
}
for (const item of this.targetQueue) {
item.resolve(await this.target[item.method](...item.args));
}
}
};
// node_modules/@vue/devtools-api/lib/esm/index.js
function setupDevtoolsPlugin(pluginDescriptor, setupFn) {
const descriptor = pluginDescriptor;
const target = getTarget();
const hook = getDevtoolsGlobalHook();
const enableProxy = isProxyAvailable && descriptor.enableEarlyProxy;
if (hook && (target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ || !enableProxy)) {
hook.emit(HOOK_SETUP, pluginDescriptor, setupFn);
} else {
const proxy = enableProxy ? new ApiProxy(descriptor, hook) : null;
const list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || [];
list.push({
pluginDescriptor: descriptor,
setupFn,
proxy
});
if (proxy)
setupFn(proxy.proxiedTarget);
}
}
export {
isPerformanceSupported,
now,
setupDevtoolsPlugin
};
//# sourceMappingURL=@vue_devtools-api.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,25 @@
{
"hash": "14110bcc",
"browserHash": "8dc88e09",
"optimized": {
"vue": {
"src": "../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "a62e771b",
"needsInterop": false
},
"@vue/devtools-api": {
"src": "../../../node_modules/@vue/devtools-api/lib/esm/index.js",
"file": "@vue_devtools-api.js",
"fileHash": "d5c641bb",
"needsInterop": false
},
"@theme/index": {
"src": "../../../node_modules/vitepress/dist/client/theme-default/index.js",
"file": "@theme_index.js",
"fileHash": "0ec9b2ee",
"needsInterop": false
}
},
"chunks": {}
}

View File

@ -0,0 +1 @@
{"type":"module"}

9516
docs/.vitepress/cache/deps/vue.js vendored Normal file

File diff suppressed because it is too large Load Diff

7
docs/.vitepress/cache/deps/vue.js.map vendored Normal file

File diff suppressed because one or more lines are too long

86
docs/.vitepress/config.ts Normal file
View File

@ -0,0 +1,86 @@
import { defineConfig } from 'vitepress'
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "Mybatis-Flex",
description: "MyBatis-Flex Office website",
// logo: '/assets/images/logo02.png',
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav: [
{ text: '首页', link: '/' },
{ text: '帮助文档', link: '/zh/maven' },
{ text: '更新日志', link: 'https://gitee.com/mybatis-flex/mybatis-flex/blob/main/changes.txt' },
{ text: '源码', items: [
{ text: 'Gitee', link: 'https://gitee.com/mybatis-flex/mybatis-flex' },
{ text: 'Github', link: 'https://github.com/mybatis-flex/mybatis-flex' }
] },
],
sidebar: [
{
text: '简介',
items: [
{ text: 'Mybatis-Flex 是什么', link: '/zh/what-is-mybatisflex' },
{ text: '快速开始', link: '/zh/getting-started' },
{ text: 'Maven 依赖', link: '/zh/maven' },
]
},
{
text: '基础功能',
items: [
{ text: '增删改', link: '/zh/add-delete-update' },
{ text: '查询和分页', link: '/zh/query' },
{ text: 'QueryWrapper', link: '/zh/querywrapper' },
]
},
{
text: '核心功能',
items: [
{ text: '实体类配置', link: '/zh/table' },
{ text: '主键配置', link: '/zh/id' },
{ text: '列配置', link: '/zh/column' },
{ text: 'Db + Row', link: '/zh/db_row' },
{ text: '逻辑删除', link: '/zh/logic_delete' },
{ text: '乐观锁', link: '/zh/version' },
{ text: '数据填充', link: '/zh/fill' },
{ text: '数据脱敏', link: '/zh/mask' },
{ text: '数据审计', link: '/zh/audit' },
]
},
{
text: '其他',
items: [
{ text: '代码生成器', link: '/zh/codegen' },
{ text: 'APT 设置', link: '/zh/apt' },
]
}
],
footer: {
message: 'Released under the Apache License.',
copyright: 'Copyright © 2022-present Mybatis-Flex '
}
},
head: [
[
'link',{ rel: 'icon', href: '/assets/images/logo02.png' }
],
[
// 添加百度统计
"script",
{},
`
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?3f50d5fbe3bf955411748b5616b24a24";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
`
]
],
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

28
docs/index.md Normal file
View File

@ -0,0 +1,28 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home
hero:
name: "Mybatis-Flex"
text: "一个优雅的 Mybatis 增强框架"
tagline: 更轻量、更灵活、更简单
image:
src: /assets/images/logo01.png
alt: Mybatis-Flex
actions:
- theme: brand
text: 快速开始
link: /zh/getting-started
- theme: alt
text: 在 Gitee 上查看
link: https://gitee.com/mybatis-flex/mybatis-flex
features:
- title: 更轻量
details: Mybatis-Flex 框架基于 Mybatis 的 SqlProvider 进行实现,没有任何连拦截器,没有任何第三方依赖,没有任何 Sql Parse因此也会有更高的性能。
- title: 更灵活
details: Mybatis-Flex 提供了非常灵活的 QueryWrapper支持关联查询、多表查询、多主键、逻辑删除、乐观锁更新、数据填充、数据脱敏....
- title: 更简单
details: 完善的增删改查和分页查询内置功能强劲的代码生成器SQL 查询 IDE 自动提示...
---

10
docs/package.json Normal file
View File

@ -0,0 +1,10 @@
{
"scripts": {
"docs:dev": "vitepress dev",
"docs:build": "vitepress build",
"docs:preview": "vitepress preview"
},
"devDependencies": {
"vitepress": "^1.0.0-alpha.61"
}
}

View File

@ -0,0 +1,77 @@
# Mybatis-Flex 的增删改功能
Mybatis-Flex 内置了一个名为 `BaseMapper` 的接口,它实现了基本的增删改查功能以及分页查询功能。
> Mybatis-Flex 的 **代码生成器** 生成的所有 Mapper 辅助类,都是继承 BaseMapper。
## 新增数据
`BaseMapper` 的接口提供了 insert 和 insertBatch 方法,用于新增数据;
- **insert** 新增 1 条数据
- **insertBatch** 新增多条数据
## 删除数据
`BaseMapper` 的接口提供了 deleteById、deleteBatchByIds、deleteByMap、deleteByQuery 方法,用于删除数据;
- **deleteById(id)** :根据主键 id 删除数据,复合主键需要传入一个数组,例如 [1,100]
- **deleteBatchByIds(idList)** :根据主键的 集合,批量删除多条数据
- **deleteByMap(map)** :根据 `map<字段名,值>` 组成的条件删除数据,字段名和值的关系为相等的关系,同时,防止 "不小心" 全表
删除数据map 的值不允许为 null 或者 空数据。
- **deleteByQuery(queryWrapper)**:根据 queryWrapper 组成的条件删除数据。
**deleteByQuery(queryWrapper)** 方法示例:
```java
QueryWrapper queryWrapper = QueryWrapper.create();
queryWrapper.where(ACCOUNT.ID.ge(100));
//通过 queryWrapper 删除
accountMapper.deleteByQuery(queryWrapper);
```
以上的代码,会删除所有 id >= 100 的数据,其执行的 Sql 如下:
```sql
delete from tb_account where id >= 100;
```
>tip: QueryWrapper 非常灵活,也是 Mybatis-Flex 的特色之一,更多关于 QueryWrapper 的
> 请看 [QueryWrapper 章节](./querywrapper)。
## 更新数据
`BaseMapper` 的接口提供了 update、updateByMap、updateByQuery 方法,用于更新数据;
- **update(entity)**:根据主键更新到 entity 到数据库,要求主键值不能为空,否则会抛出异常。同时,数据为 null 的字段 **不会** 更新到数据库。
- **update(entity, ignoreNulls)**:根据主键更新到 entity 到数据库要求主键值不能为空。ignoreNulls 为是否忽略 null 字段,如果为 true所有 null 字段都会更新到数据库。
- **updateByMap(entity, map)**:根据 `map<字段名,值>` 组成的条件更新到 entity 到数据库entity 可以没有主键(如果有也会被忽略), entity 的 null 属性,会自动被忽略。
- **updateByQuery(entity, queryWrapper)**:根据 queryWrapper 组成的条件更新到 entity 到数据库entity 可以没有主键(如果有也会被忽略), entity 的 null 属性,会自动被忽略。
- **updateByQuery(entity, ignoreNulls, queryWrapper)**:据 queryWrapper 组成的条件更新到 entity 到数据库entity 可以没有主键(如果有也会被忽略)。 ignoreNulls 用于是否忽略 entity 的 null 属性
若 ignoreNulls 为 trueentity 的所有 null 属性都会被更新到数据库。
## 部分字段更新
在很多场景下,我们希望只更新**部分字段**,而更新的字段中,一些为 null一些非 null。此时需要用到 `UpdateEntity` 工具类,以下是示例代码:
```java
Account account = UpdateEntity.of(Account.class);
account.setId(100);
account.setUserName(null);
account.setAge(10);
accountMapper.update(account,false);
```
以上的示例中,会把 id (主键)为 100 这条数据中的 user_name 字段更新为 nullage 字段更新为 10其他字段不会被更新。也就是说通过 UpdateEntity 创建的对象,只会更新调用了 setter 方法的字段,若不调用 setter 方法,不管这个对象里的属性的值是什么,都不会更新到数据库。
其执行的 sql 内容如下:
```sql
update tb_account
set user_name = ?, age = ? where id = ?
#参数: null,10,100
```

0
docs/zh/basemapper.md Normal file
View File

169
docs/zh/fill.md Normal file
View File

@ -0,0 +1,169 @@
# Entity 的主键配置
在 Entity 类中Mybatis-Flex 是使用 `@Id` 注解来标识主键的,如下代码所示:
```java
@Table("tb_account")
public class Account {
// id 为自增主键
@Id(keyType = KeyType.Auto)
private Long id;
//getter setter
}
```
`@Id` 注解的内容如下:
```java
public @interface Id {
/**
* ID 生成策略,默认为 none
*
* @return 生成策略
*/
KeyType keyType() default KeyType.None;
/**
* 若 keyType 类型是 sequence value 则代表的是
* sequence 序列的 sql 内容
* 例如select SEQ_USER_ID.nextval as id from dual
*
* 若 keyType 是 Generatorvalue 则代表的是使用的那个 keyGenerator 的名称
*
*/
String value() default "";
/**
* sequence 序列执行顺序
* 是在 entity 数据插入之前执行,还是之后执行,之后执行的一般是数据主动生成的 id
*
* @return 执行之前还是之后
*/
boolean before() default true;
}
```
keyType 为主键的生成方式KeyType 有 4 种类型:
```java
public enum KeyType {
/**
* 自增的方式
*/
Auto,
/**
* 通过执行数据库 sql 生成
* 例如select SEQ_USER_ID.nextval as id from dual
*/
Sequence,
/**
* 通过 IKeyGenerator 生成器生成
*/
Generator,
/**
* 其他方式,比如说在代码层用户手动设置
*/
None,
}
```
## 多主键、复合主键
Mybatis-Flex 多主键就是在 Entity 类里有多个 `@Id` 注解标识而已,比如:
```java
@Table("tb_account")
public class Account {
@Id(keyType=KeyType.Auto)
private Long id;
@Id(keyType=KeyType.Generator, value="uuid")
private String otherId;
//getter setter
}
```
当我们保存数据的时候Account 的 id 主键为自增,而 otherId 主键则通过 uuid 生成。
## 主键生成器
第 1 步:编写一个类,实现 `IKeyGenerator` 接口,例如:
```java
public class UUIDKeyGenerator implements IKeyGenerator {
@Override
public Object generate(Object entity, String keyColumn) {
return UUID.randomUUID().toString().replace("-", "");
}
}
```
第 2 步:注册 UUIDKeyGenerator
```java
KeyGeneratorFactory.register("myUUID", new UUIDKeyGenerator());
```
第 3 步:在 Entity 里使用 "myUUID" 生成器:
```java
@Table("tb_account")
public class Account {
@Id(keyType=KeyType.Generator, value="myUUID")
private String otherId;
//getter setter
}
```
## 使用序列 Sequence 生成
```java
@Table("tb_account")
public class Account {
@Id(keyType=KeyType.Sequence, value="select SEQ_USER_ID.nextval as id from dual")
private Long id;
}
```
## 全局配置
一般的项目中,通常是许多的 Entity 使用同一个数据库,同时使用一种主键生成方式,比如说都使用 自增,
或者都使用通过序列Sequence生成此时我们是没有必要为每个 Entity 单独配置一样内容的。
Mybatis-Flex 提供了一种全局配置的方式,代码如下:
```java
FlexGlobalConfig.KeyConfig keyConfig = new FlexGlobalConfig.KeyConfig();
keyConfig.setKeyType(KeyType.Sequence);
keyConfig.setValue("select SEQ_USER_ID.nextval as id from dual")
keyConfig.setBefore(true);
FlexGlobalConfig.getDefaultConfig().setKeyConfig(keyConfig);
```
此时Entity 类 Account.java 只需要如下配置即可。
```java
@Table("tb_account")
public class Account {
@Id()
private Long id;
}
```

View File

@ -0,0 +1,85 @@
# 快速开始
## Hello World
**第 1 步:创建数据库表**
```sql
CREATE TABLE IF NOT EXISTS `tb_account`
(
`id` INTEGER PRIMARY KEY auto_increment,
`user_name` VARCHAR(100),
`age` Integer,
`birthday` DATETIME
);
```
**第 2 步:创建 java 项目,并添加 Maven 依赖**
```xml
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-core</artifactId>
<version>1.0.3</version>
</dependency>
```
**第 3 步:编写实体类**
```java
@Table("tb_account")
public class Account {
@Id(keyType = KeyType.Auto)
private Long id;
private String userName;
private Integer age;
private Date birthday;
//getter setter
}
```
- `@Table("tb_account")` 设置实体类与表名的映射关系
- `@Id(keyType = KeyType.Auto)` 标识主键为自增
**第 4 步:编写一个 main 方法开始使用**
```java
public class HelloWorld {
public static void main(String... args) {
//创建数据源
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/mybatis-flex");
dataSource.setUsername("username");
dataSource.setPassword("password");
//配置数据源
MybatisFlexBootstrap.getInstance()
.setDatasource(dataSource)
.addMapper(AccountMapper.class)
.start();
//示例1查询 id=1 条数据
Account account = MybatisFlexBootstrap.getInstance()
.execute(AccountMapper.class, mapper ->
mapper.selectOneById(1)
);
//示例2或者使用 Db + Row 查询
String sql = "select * from tb_account where age > ?";
List<Row> rows = Db.selectListBySql(sql, 18);
}
}
```
> 以上的示例中, `AccountMapper.class` 为 Mybatis-Flex 通过 APT 自动生成,无需手动编码。
> 也可以关闭自动生成功能,手动编写 AccountMapper更多查看 [APT 文档](/zh/apt)。
## 更多示例
- 示例 1[Mybatis-Flex 原生(非 Spring](https://gitee.com/mybatis-flex/mybatis-flex/tree/main/mybatis-flex-test/mybatis-flex-native-test)
- 示例 2[Mybatis-Flex with Spring]()
- 示例 3[Mybatis-Flex with Spring boot]()
- 示例 4[Db + Row]()

169
docs/zh/mask.md Normal file
View File

@ -0,0 +1,169 @@
# Entity 的主键配置
在 Entity 类中Mybatis-Flex 是使用 `@Id` 注解来标识主键的,如下代码所示:
```java
@Table("tb_account")
public class Account {
// id 为自增主键
@Id(keyType = KeyType.Auto)
private Long id;
//getter setter
}
```
`@Id` 注解的内容如下:
```java
public @interface Id {
/**
* ID 生成策略,默认为 none
*
* @return 生成策略
*/
KeyType keyType() default KeyType.None;
/**
* 若 keyType 类型是 sequence value 则代表的是
* sequence 序列的 sql 内容
* 例如select SEQ_USER_ID.nextval as id from dual
*
* 若 keyType 是 Generatorvalue 则代表的是使用的那个 keyGenerator 的名称
*
*/
String value() default "";
/**
* sequence 序列执行顺序
* 是在 entity 数据插入之前执行,还是之后执行,之后执行的一般是数据主动生成的 id
*
* @return 执行之前还是之后
*/
boolean before() default true;
}
```
keyType 为主键的生成方式KeyType 有 4 种类型:
```java
public enum KeyType {
/**
* 自增的方式
*/
Auto,
/**
* 通过执行数据库 sql 生成
* 例如select SEQ_USER_ID.nextval as id from dual
*/
Sequence,
/**
* 通过 IKeyGenerator 生成器生成
*/
Generator,
/**
* 其他方式,比如说在代码层用户手动设置
*/
None,
}
```
## 多主键、复合主键
Mybatis-Flex 多主键就是在 Entity 类里有多个 `@Id` 注解标识而已,比如:
```java
@Table("tb_account")
public class Account {
@Id(keyType=KeyType.Auto)
private Long id;
@Id(keyType=KeyType.Generator, value="uuid")
private String otherId;
//getter setter
}
```
当我们保存数据的时候Account 的 id 主键为自增,而 otherId 主键则通过 uuid 生成。
## 主键生成器
第 1 步:编写一个类,实现 `IKeyGenerator` 接口,例如:
```java
public class UUIDKeyGenerator implements IKeyGenerator {
@Override
public Object generate(Object entity, String keyColumn) {
return UUID.randomUUID().toString().replace("-", "");
}
}
```
第 2 步:注册 UUIDKeyGenerator
```java
KeyGeneratorFactory.register("myUUID", new UUIDKeyGenerator());
```
第 3 步:在 Entity 里使用 "myUUID" 生成器:
```java
@Table("tb_account")
public class Account {
@Id(keyType=KeyType.Generator, value="myUUID")
private String otherId;
//getter setter
}
```
## 使用序列 Sequence 生成
```java
@Table("tb_account")
public class Account {
@Id(keyType=KeyType.Sequence, value="select SEQ_USER_ID.nextval as id from dual")
private Long id;
}
```
## 全局配置
一般的项目中,通常是许多的 Entity 使用同一个数据库,同时使用一种主键生成方式,比如说都使用 自增,
或者都使用通过序列Sequence生成此时我们是没有必要为每个 Entity 单独配置一样内容的。
Mybatis-Flex 提供了一种全局配置的方式,代码如下:
```java
FlexGlobalConfig.KeyConfig keyConfig = new FlexGlobalConfig.KeyConfig();
keyConfig.setKeyType(KeyType.Sequence);
keyConfig.setValue("select SEQ_USER_ID.nextval as id from dual")
keyConfig.setBefore(true);
FlexGlobalConfig.getDefaultConfig().setKeyConfig(keyConfig);
```
此时Entity 类 Account.java 只需要如下配置即可。
```java
@Table("tb_account")
public class Account {
@Id()
private Long id;
}
```

59
docs/zh/query.md Normal file
View File

@ -0,0 +1,59 @@
# Mybatis-Flex 的查询和分页
## 基础查询
在 Mybatis-Flex 的 BaseMapper 中,提供了如下的功能用于查询数据库的数据:
- **selectOneById(id)**:根据主键 id 查询数据
- **selectOneByMap(map)**:根据 `map<字段名,值>` 组成的条件查询 1 条数据,若命中多条数据,则只返回第一条数据。
- **selectOneByQuery(query)**:根据 QueryWrapper 组成的条件查询 1 条数据,若命中多条数据,则只返回第一条数据。
- **selectListByIds(idList)**:根据多个 id 查询,返回多条数据
- **selectListByMap(map)**:根据 `map<字段名,值>` 组成的条件查询数据。
- **selectListByMap(map, count)**:根据 `map<字段名,值>` 组成的条件查询数据,只取前 count 条。
- **selectListByQuery(query)** 根据 QueryWrapper 组成的条件查询数据。
- **selectAll**:查询所有数据。
- **selectCountByQuery**:根据 QueryWrapper 查询数据量。
## 分页查询
在 Mybatis-Flex 的 BaseMapper 中,提供了如下的分页查询功能:
```java
Page<T> paginate(int pageNumber, int pageSize, QueryWrapper queryWrapper);
```
- pageNumber 当前页码,从 1 开始
- pageSize 每 1 页的数据量
- queryWrapper 查询条件
paginate 的返回值为 Page 对象Page 类的定义如下:
```java
public class Page<T> implements Serializable {
private List<T> list; // list result of this page
private int pageNumber; // page number
private int pageSize; // result amount of this page
private long totalPage; // total page
private long totalRow; // total row
}
```
在 Page 的定义中,我们知道:通过 `paginate` 方法去查询数据的时候,除了数据列表以外,还查询的数据的总量,才能构造出 `Page` 对象。
在一般的分页场景中只有第一页的时候有必要去查询数据总量第二页以后是没必要的因为第一页已经拿到总量了因此Mybatis-Flex 的分页查询还提供了另一个方法:
```java
Page<T> paginate(Page<T> page, QueryWrapper queryWrapper);
```
这个方法的使用示例如下:
```java
// 多一个 totalPage 参数
Page<T> page = new Page<>(pageNumber, pageSize, totalPage);
Page<T> resultPage = paginate(page, queryWrapper);
```
当构造的 `page` 对象已经有 totalPage 后,再通过 `paginate(page, queryWrapper)` 方法去查询,则不会再查询数据总量,从提高了性能。
> 只有 totalRow 小于 0 的时候才会去查询总量。

360
docs/zh/querywrapper.md Normal file
View File

@ -0,0 +1,360 @@
# 灵活的 QueryWrapper
在 [增删改](./add-delete-update) 和 [查询和分页](./query) 章节中,我们随时能看到 QueryWrapper 的身影QueryWrapper 是用于构造 Sql 的
强有力工具,也是 Mybatis-Flex 的亮点和特色。
::: tip 提示
QueryWrapper 可以被序列化通过 RPC 进行传输因此在微服务项目中我们可以在客户端网关、Controller 层等)构造出 QueryWrapper传给
Provider 层进行查询返回数据。
:::
## QueryWrapper 的使用
以下代码是一个完整 Spring Controller 的示例:
```java
@RestController
public class AccountController {
@Autowired
AccountMapper accountMapper;
@GetMapping("/accounts")
List<Account> selectList() {
//构造 QueryWrapper
QueryWrapper query = new QueryWrapper();
query.where(ACCOUNT.ID.ge(100));
//通过 query 查询数据列表返回
return accountMapper.selectListByQuery(query);
}
}
```
在以上的示例中,其核心代码为:构造 QueryWrapper通过 Mapper 查询,如下所示:
```java
//构造 QueryWrapper
QueryWrapper query = new QueryWrapper();
query.where(ACCOUNT.ID.ge(100));
//通过 query 查询数据列表
accountMapper.selectListByQuery(query);
```
以上代码执行的 Sql 如下:
```sql
select * from tb_account
where id >= 100
```
## select *
```java
QueryWrapper query=new QueryWrapper();
query.select(ACCOUNT.ID, ACCOUNT.USER_NAME)
.from(ACCOUNT)
```
其查询生成的 Sql 如下:
```sql
SELECT id, user_name FROM tb_account
```
## select ... as
```java
QueryWrapper query = new QueryWrapper()
.select(
ACCOUNT.ID.as("accountId")
, ACCOUNT.USER_NAME
.from(ACCOUNT.as("a"));
```
其查询生成的 Sql 如下:
```sql
SELECT a.id as accountId, a.user_name
FROM tb_account AS a
```
## select 多张表
```java
QueryWrapper query = new QueryWrapper()
.select(
ACCOUNT.ID
, ACCOUNT.USER_NAME
, ARTICLE.ID.as("articleId")
, ARTICLE.TITLE)
.from(ACCOUNT.as("a"), ARTICLE.as("b"))
.where(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID));
```
其查询生成的 Sql 如下:
```sql
SELECT a.id, a.user_name, b.id AS articleId, b.title
FROM tb_account AS a, tb_article AS b
WHERE a.id = b.account_id
```
## select function
```java
QueryWrapper query=new QueryWrapper()
.select(
ACCOUNT.ID,
ACCOUNT.USER_NAME,
max(ACCOUNT.BIRTHDAY),
avg(ACCOUNT.SEX).as("sex_avg")
).from(ACCOUNT);
```
其查询生成的 Sql 如下:
```sql
SELECT id, user_name, MAX(birthday), AVG(sex) AS sex_avg
FROM tb_account
```
## where
```java
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(100))
.and(ACCOUNT.USER_NAME.like("michael"));
```
其查询生成的 Sql 如下:
```sql
SELECT * FROM tb_account
WHERE id >= ?
AND user_name LIKE ?
```
## where 动态条件 1
```java 1,4
boolean flag = false;
QueryWrapper queryWrapper = QueryWrapper.create()
.select().from(ACCOUNT)
.where(flag ? ACCOUNT.ID.ge(100) : noCondition())
.and(ACCOUNT.USER_NAME.like("michael"));
```
其查询生成的 Sql 如下:
```sql
SELECT * FROM tb_account
WHERE user_name LIKE ?
```
## where 动态条件 2
```java 1,4
boolean flag = false;
QueryWrapper queryWrapper = QueryWrapper.create()
.select().from(ACCOUNT)
.where(ACCOUNT.ID.ge(100).when(flag)) // when....
.and(ACCOUNT.USER_NAME.like("michael"));
```
其查询生成的 Sql 如下:
```sql
SELECT * FROM tb_account
WHERE user_name LIKE ?
```
## where select
```java
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(
select(ARTICLE.ACCOUNT_ID).from(ARTICLE).where(ARTICLE.ID.ge(100))
));
```
其查询生成的 Sql 如下:
```sql
SELECT * FROM tb_account
WHERE id >=
(SELECT account_id FROM tb_article WHERE id >= ? )
```
## where exists, not exists
```java
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(100))
.and(
exist( // or notExist(...)
selectOne().from(ARTICLE).where(ARTICLE.ID.ge(100))
)
);
```
其查询生成的 Sql 如下:
```sql
SELECT * FROM tb_account
WHERE id >= ?
AND EXIST (
SELECT 1 FROM tb_article WHERE id >= ?
)
```
## and (...) or (...)
```java
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(100))
.and(ACCOUNT.SEX.eq(1).or(ACCOUNT.SEX.eq(2)))
.or(ACCOUNT.AGE.in(18,19,20).and(ACCOUNT.USER_NAME.like("michael")));
```
其查询生成的 Sql 如下:
```sql
SELECT * FROM tb_account
WHERE id >= ?
AND (sex = ? OR sex = ? )
OR (age IN (?,?,?) AND user_name LIKE ? )
```
## group by
```java
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.groupBy(ACCOUNT.USER_NAME);
```
其查询生成的 Sql 如下:
```sql
SELECT * FROM tb_account
GROUP BY user_name
```
## having
```java
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.groupBy(ACCOUNT.USER_NAME)
.having(ACCOUNT.AGE.between(18,25));
```
其查询生成的 Sql 如下:
```sql
SELECT * FROM tb_account
GROUP BY user_name
HAVING age BETWEEN ? AND ?
```
## orderBy
```java
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.orderBy(ACCOUNT.AGE.asc(), ACCOUNT.USER_NAME.desc().nullsLast());
```
其查询生成的 Sql 如下:
```sql
SELECT * FROM tb_account
ORDER BY age ASC, user_name DESC NULLS LAST
```
## joinleft joininner join...
```java
QueryWrapper queryWrapper=QueryWrapper.create()
.select()
.from(ACCOUNT)
.leftJoin(ARTICLE).on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
.innerJoin(ARTICLE).on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
.where(ACCOUNT.AGE.ge(10));
```
其查询生成的 Sql 如下:
```sql
SELECT * FROM tb_account
LEFT JOIN tb_article ON tb_account.id = tb_article.account_id
INNER JOIN tb_article ON tb_account.id = tb_article.account_id
WHERE tb_account.age >= ?
```
## limit... offset
::: tip 提示
在 "limit... offset" 的示例中Mybatis-Flex 能够自动识别当前数据库,并根据数据库的类型生成不同的 SQL用户也可以很轻易的通过 DialectFactory 注册(新增或改写)自己的实现方言。
:::
```java
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(ACCOUNT)
.orderBy(ACCOUNT.ID.desc())
.limit(10)
.offset(20);
```
MySql 下执行的代码如下:
```sql
SELECT * FROM `tb_account` ORDER BY `id` DESC LIMIT 20, 10
```
PostgreSQL 下执行的代码如下:
```sql
SELECT * FROM "tb_account" ORDER BY "id" DESC LIMIT 20 OFFSET 10
```
Informix 下执行的代码如下:
```sql
SELECT SKIP 20 FIRST 10 * FROM "tb_account" ORDER BY "id" DESC
```
Oracle 下执行的代码如下:
```sql
SELECT * FROM (SELECT TEMP_DATAS.*,
ROWNUM RN FROM (
SELECT * FROM "tb_account" ORDER BY "id" DESC)
TEMP_DATAS WHERE ROWNUM <=30)
WHERE RN >20
```
Db2 下执行的代码如下:
```sql
SELECT * FROM "tb_account" ORDER BY "id" DESC
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
```
Sybase 下执行的代码如下:
```sql
SELECT TOP 10 START AT 21 * FROM "tb_account" ORDER BY "id" DESC
```
Firebird 下执行的代码如下:
```sql
SELECT * FROM "tb_account" ORDER BY "id" DESC ROWS 20 TO 30
```

169
docs/zh/table.md Normal file
View File

@ -0,0 +1,169 @@
# Entity 的主键配置
在 Entity 类中Mybatis-Flex 是使用 `@Id` 注解来标识主键的,如下代码所示:
```java
@Table("tb_account")
public class Account {
// id 为自增主键
@Id(keyType = KeyType.Auto)
private Long id;
//getter setter
}
```
`@Id` 注解的内容如下:
```java
public @interface Id {
/**
* ID 生成策略,默认为 none
*
* @return 生成策略
*/
KeyType keyType() default KeyType.None;
/**
* 若 keyType 类型是 sequence value 则代表的是
* sequence 序列的 sql 内容
* 例如select SEQ_USER_ID.nextval as id from dual
*
* 若 keyType 是 Generatorvalue 则代表的是使用的那个 keyGenerator 的名称
*
*/
String value() default "";
/**
* sequence 序列执行顺序
* 是在 entity 数据插入之前执行,还是之后执行,之后执行的一般是数据主动生成的 id
*
* @return 执行之前还是之后
*/
boolean before() default true;
}
```
keyType 为主键的生成方式KeyType 有 4 种类型:
```java
public enum KeyType {
/**
* 自增的方式
*/
Auto,
/**
* 通过执行数据库 sql 生成
* 例如select SEQ_USER_ID.nextval as id from dual
*/
Sequence,
/**
* 通过 IKeyGenerator 生成器生成
*/
Generator,
/**
* 其他方式,比如说在代码层用户手动设置
*/
None,
}
```
## 多主键、复合主键
Mybatis-Flex 多主键就是在 Entity 类里有多个 `@Id` 注解标识而已,比如:
```java
@Table("tb_account")
public class Account {
@Id(keyType=KeyType.Auto)
private Long id;
@Id(keyType=KeyType.Generator, value="uuid")
private String otherId;
//getter setter
}
```
当我们保存数据的时候Account 的 id 主键为自增,而 otherId 主键则通过 uuid 生成。
## 主键生成器
第 1 步:编写一个类,实现 `IKeyGenerator` 接口,例如:
```java
public class UUIDKeyGenerator implements IKeyGenerator {
@Override
public Object generate(Object entity, String keyColumn) {
return UUID.randomUUID().toString().replace("-", "");
}
}
```
第 2 步:注册 UUIDKeyGenerator
```java
KeyGeneratorFactory.register("myUUID", new UUIDKeyGenerator());
```
第 3 步:在 Entity 里使用 "myUUID" 生成器:
```java
@Table("tb_account")
public class Account {
@Id(keyType=KeyType.Generator, value="myUUID")
private String otherId;
//getter setter
}
```
## 使用序列 Sequence 生成
```java
@Table("tb_account")
public class Account {
@Id(keyType=KeyType.Sequence, value="select SEQ_USER_ID.nextval as id from dual")
private Long id;
}
```
## 全局配置
一般的项目中,通常是许多的 Entity 使用同一个数据库,同时使用一种主键生成方式,比如说都使用 自增,
或者都使用通过序列Sequence生成此时我们是没有必要为每个 Entity 单独配置一样内容的。
Mybatis-Flex 提供了一种全局配置的方式,代码如下:
```java
FlexGlobalConfig.KeyConfig keyConfig = new FlexGlobalConfig.KeyConfig();
keyConfig.setKeyType(KeyType.Sequence);
keyConfig.setValue("select SEQ_USER_ID.nextval as id from dual")
keyConfig.setBefore(true);
FlexGlobalConfig.getDefaultConfig().setKeyConfig(keyConfig);
```
此时Entity 类 Account.java 只需要如下配置即可。
```java
@Table("tb_account")
public class Account {
@Id()
private Long id;
}
```

View File

@ -0,0 +1 @@
# Mybatis-Flex 是什么