mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-08 17:48:25 +08:00
Merge branch 'main' of gitee.com:mybatis-flex/mybatis-flex into main
Signed-off-by: 王帅 <1474983351@qq.com>
This commit is contained in:
commit
bad11ec060
101
changes.txt
101
changes.txt
@ -1,3 +1,104 @@
|
|||||||
|
mybatis-flex v1.3.0 20230525:
|
||||||
|
新增:新增 一对多、多对一 查询功能
|
||||||
|
新增:为 SqlServer 添加独立的 LimitOffset 处理器
|
||||||
|
新增:QueryWrapper 新增 "for update" 的 SQL 构建支持
|
||||||
|
新增:QueryWrapper 新增 select convert(...) 的 SQL 构建支持
|
||||||
|
新增:QueryWrapper 新增 select case when ... then 的 SQL 构建支持
|
||||||
|
优化:APT 默认生成独立文件,之前所有 APT 生成在同一个文件修改为可配置
|
||||||
|
修复:Table.camelToUnderline 注解在 APT 上配置不生效的问题
|
||||||
|
修复:多租户设置 TenantId 时,在某些极端情况下出现异常的问题
|
||||||
|
文档:新增 一对多、多对一 的相关文档
|
||||||
|
文档:新增 select case when ... then 的 QueryWrapper 示例
|
||||||
|
文档:添加关于 hint 的相关描述
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mybatis-flex v1.2.9 20230523:
|
||||||
|
新增:字段添加对 Byte.class 类型的支持 #I76GNW
|
||||||
|
修复:"select * ,(select ...) from ..." 第二个 select 有参数时出错的问题
|
||||||
|
修复:QueryCondition 在 left join 查询是出现 NPE 的问题
|
||||||
|
修复:修改 QueryWrapper 添加 limit(1) 时,SQLServer 方言构建 SQL 出错的问题
|
||||||
|
修复:修改 TDENGINE 方言出错的问题
|
||||||
|
修复:baseMapper.selectOneByQueryAs() 查询出错的问题
|
||||||
|
修复:当自定义 TypeHandler 时,通过 UpdateEntity 更新时,TypeHandler 无效的问题
|
||||||
|
优化:重构 TableInfo.appendConditions() 代码
|
||||||
|
优化:修改错别字 "那些" 为 "哪些"
|
||||||
|
文档:更新 "Mybatis" 为 "MyBatis" #I72DWO
|
||||||
|
文档:添加使用 druid-spring-boot-starter 出错到常见问题里
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mybatis-flex v1.2.8 20230522:
|
||||||
|
新增:新增 select id,(select...) from 的支持
|
||||||
|
优化:Solon 插件增加 RowMapperInvoker 注入和 FlexGlobalConfig 可事件扩展的支持,感谢 @西东
|
||||||
|
优化:分页的 count 查询默认去掉 left join 和 order by 等
|
||||||
|
优化:APT 的 ALL_COLUMNS 修改 table.*
|
||||||
|
文档:添加 hint 的相关文档
|
||||||
|
文档:优化 mybatis-flex-solon-plugin 的使用文档
|
||||||
|
文档:优化 queryWrapper 的相关文档
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mybatis-flex v1.2.7 20230520:
|
||||||
|
新增:添加 solon 关于 ServiceImpl 的实现
|
||||||
|
新增:left join 等 join 查询添加 as(lambda) 的支持
|
||||||
|
优化:优化 EnumWrapper.java 使之具有更高的性能
|
||||||
|
优化:迁移 IService 到 core 目录
|
||||||
|
优化:重命名 Db.updateBatchEntity 为 Db.updateEntitiesBatch
|
||||||
|
修复:逻辑删除设置 bool 类型在 postgresql 下出错的问题
|
||||||
|
文档:添加批量操作的相关文档说明
|
||||||
|
文档:添加关联查询的相关文档
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mybatis-flex v1.2.6 20230518:
|
||||||
|
新增:IService 添加 updateBatch 方法,感谢 @Saoforest
|
||||||
|
优化:findById 默认返回 isLarge 的字段 #I73SJY
|
||||||
|
优化:WrapperUtil.getValues() 并直接读取枚举内容
|
||||||
|
修复:ClassUtil 修复无法正确读取 JDK 动态代理超类问题,感谢 @Saoforest
|
||||||
|
修复:批量执行每一个批次会少 1 条数据的问题,感谢 @笨小孩
|
||||||
|
文档:添加数据权限的相关文档
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mybatis-flex v1.2.5 20230517:
|
||||||
|
新增:Db.executeBatch 方法,用于批量操作
|
||||||
|
新增:Db 工具类添加基于 Entity 的 updateBatch 方法,感谢 @黄沐鸿
|
||||||
|
新增:KeyGenerators.java 方便进行主键生成策略配置
|
||||||
|
新增:APT 的 mybatis-flex.properties 文件添加使用 ClassLoader 读取,方便读取 jar 的内容,感谢 @XiaoLin
|
||||||
|
新增:QueryWrapper 新增 hash join 的支持
|
||||||
|
新增:QueryWrapper 新增 sql hint 的支持
|
||||||
|
优化:添加 configuration-processor,实现 yaml 配置自动提示,感谢 @tan90
|
||||||
|
修复:v1.2.4 新增的 paginateAs 异常问题 #I73BP6
|
||||||
|
修复:v1.2.4 版本造成的 Db.paginate 出错的问题
|
||||||
|
文档:优化 id 主键生成器的相关文档
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mybatis-flex v1.2.4 20230515:
|
||||||
|
新增:Db.updateBatch() 方法,用于批量修改或者插入等场景
|
||||||
|
新增:通过雪花算法生成数据库主键,内置雪花算法,感谢 @王帅
|
||||||
|
新增:QueryCondition 可以直接传入枚举变量,自动读取 @EnumValue
|
||||||
|
新增:代码生成器添加 Service、ServiceImpl、Controller 的生成功能,感谢 @王帅
|
||||||
|
新增:Db 和 BaseMapper 添加 selectObject 和 selectObjectList 等方法
|
||||||
|
新增:BaseMapper 添加 selectOneByQueryAs 等方法,方便在 left join 等场景直接转换为 dto vo 等;
|
||||||
|
优化:不变属性添加 final 关键字,感谢 @庄佳彬
|
||||||
|
优化:修改拼写错误,processer->processor, 感谢 @庄佳彬
|
||||||
|
优化:Page.of() 方法
|
||||||
|
优化:为 IService.java 添加常用的方法
|
||||||
|
优化:修改 SqlUtil.retBool 为 SqlUtil.toBool
|
||||||
|
修复:orderBy 参数里传入 null 或者 空值,就会抛出异常的问题 #23
|
||||||
|
文档:优化 mybatis-flex.com 的目录结构
|
||||||
|
文档:新增内置主键生成器说明文档,感谢 @王帅
|
||||||
|
文档:修正数据脱敏中的一些描述错误
|
||||||
|
文档:IService 文档添加代码示例
|
||||||
|
文档:优化 "部分字段更新" 的相关文档
|
||||||
|
文档:更新代码生成器的相关文档,感谢 @王帅
|
||||||
|
文档:添加关于 BaseMapper 进行关联查询的相关文档
|
||||||
|
文档:添加在 Spring 的场景下使用 @Transactional 注解的注意事项
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mybatis-flex v1.2.3 20230511:
|
mybatis-flex v1.2.3 20230511:
|
||||||
新增:MaskManager 添加 withoutMask 模板方法,感谢 @pengpeng
|
新增:MaskManager 添加 withoutMask 模板方法,感谢 @pengpeng
|
||||||
新增:TenantManager 添加 withoutTenantCondition 模板方法,感谢 @pengpeng
|
新增:TenantManager 添加 withoutTenantCondition 模板方法,感谢 @pengpeng
|
||||||
|
|||||||
41
docs/.vitepress/cache/deps/@theme_index.js
vendored
41
docs/.vitepress/cache/deps/@theme_index.js
vendored
@ -1,41 +0,0 @@
|
|||||||
// 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
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"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"]
|
|
||||||
}
|
|
||||||
12
docs/.vitepress/cache/deps/_metadata.json
vendored
12
docs/.vitepress/cache/deps/_metadata.json
vendored
@ -1,17 +1,11 @@
|
|||||||
{
|
{
|
||||||
"hash": "02c07141",
|
"hash": "6f7e7a0c",
|
||||||
"browserHash": "e1f90fb9",
|
"browserHash": "e8cc6d21",
|
||||||
"optimized": {
|
"optimized": {
|
||||||
"vue": {
|
"vue": {
|
||||||
"src": "../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
|
"src": "../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
|
||||||
"file": "vue.js",
|
"file": "vue.js",
|
||||||
"fileHash": "129bee0c",
|
"fileHash": "98aba298",
|
||||||
"needsInterop": false
|
|
||||||
},
|
|
||||||
"@theme/index": {
|
|
||||||
"src": "../../../node_modules/vitepress/dist/client/theme-default/index.js",
|
|
||||||
"file": "@theme_index.js",
|
|
||||||
"fileHash": "7f1d5583",
|
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -58,10 +58,13 @@ export default defineConfig({
|
|||||||
{
|
{
|
||||||
text: '基础功能',
|
text: '基础功能',
|
||||||
items: [
|
items: [
|
||||||
{text: '增删改', link: '/zh/base/add-delete-update'},
|
{text: '增、删、改', link: '/zh/base/add-delete-update'},
|
||||||
{text: '查询和分页', link: '/zh/base/query'},
|
{text: '查询和分页', link: '/zh/base/query'},
|
||||||
{text: 'IService', link: '/zh/base/service'},
|
{text: '批量操作', link: '/zh/base/batch'},
|
||||||
|
{text: '一对多、多对一', link: '/zh/base/field-query'},
|
||||||
{text: 'QueryWrapper', link: '/zh/base/querywrapper'},
|
{text: 'QueryWrapper', link: '/zh/base/querywrapper'},
|
||||||
|
{text: 'Db + Row', link: '/zh/base/db-row'},
|
||||||
|
{text: 'IService', link: '/zh/base/service'},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -70,7 +73,6 @@ export default defineConfig({
|
|||||||
{text: '@Table 注解', link: '/zh/core/table'},
|
{text: '@Table 注解', link: '/zh/core/table'},
|
||||||
{text: '@Id 注解', link: '/zh/core/id'},
|
{text: '@Id 注解', link: '/zh/core/id'},
|
||||||
{text: '@Column 注解', link: '/zh/core/column'},
|
{text: '@Column 注解', link: '/zh/core/column'},
|
||||||
{text: 'Db + Row', link: '/zh/core/db-row'},
|
|
||||||
{text: '逻辑删除', link: '/zh/core/logic-delete'},
|
{text: '逻辑删除', link: '/zh/core/logic-delete'},
|
||||||
{text: '乐观锁', link: '/zh/core/version'},
|
{text: '乐观锁', link: '/zh/core/version'},
|
||||||
{text: '数据填充', link: '/zh/core/fill'},
|
{text: '数据填充', link: '/zh/core/fill'},
|
||||||
@ -79,6 +81,7 @@ export default defineConfig({
|
|||||||
{text: 'SQL 打印', link: '/zh/core/sql-print'},
|
{text: 'SQL 打印', link: '/zh/core/sql-print'},
|
||||||
{text: '多数据源', link: '/zh/core/multi-datasource'},
|
{text: '多数据源', link: '/zh/core/multi-datasource'},
|
||||||
{text: '事务管理', link: '/zh/core/tx'},
|
{text: '事务管理', link: '/zh/core/tx'},
|
||||||
|
{text: '数据权限', link: '/zh/core/data-permission'},
|
||||||
{text: '字段权限', link: '/zh/core/columns-permission'},
|
{text: '字段权限', link: '/zh/core/columns-permission'},
|
||||||
{text: '字段加密', link: '/zh/core/columns-encrypt'},
|
{text: '字段加密', link: '/zh/core/columns-encrypt'},
|
||||||
{text: '字典回写', link: '/zh/core/columns-dict'},
|
{text: '字典回写', link: '/zh/core/columns-dict'},
|
||||||
|
|||||||
16
docs/.vitepress/theme/MyLayout.vue
Normal file
16
docs/.vitepress/theme/MyLayout.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!--.vitepress/theme/MyLayout.vue-->
|
||||||
|
<script setup>
|
||||||
|
import DefaultTheme from 'vitepress/theme'
|
||||||
|
const { Layout } = DefaultTheme
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Layout>
|
||||||
|
<!-- <template #doc-footer-before>-->
|
||||||
|
<!-- <div class="tip custom-block" style="margin-bottom: 24px">-->
|
||||||
|
<!-- <p class="custom-block-title">广告</p>-->
|
||||||
|
<!-- <p>此次广告位招租。</p>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
</Layout>
|
||||||
|
</template>
|
||||||
10
docs/.vitepress/theme/index.js
Normal file
10
docs/.vitepress/theme/index.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// .vitepress/theme/index.js
|
||||||
|
import DefaultTheme from 'vitepress/theme'
|
||||||
|
import MyLayout from './MyLayout.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...DefaultTheme,
|
||||||
|
// override the Layout with a wrapper component that
|
||||||
|
// injects the slots
|
||||||
|
Layout: MyLayout
|
||||||
|
}
|
||||||
@ -22,6 +22,6 @@ features:
|
|||||||
- title: 更灵活
|
- title: 更灵活
|
||||||
details: MyBatis-Flex 提供了非常灵活的 QueryWrapper,支持关联查询、多表查询、多主键、逻辑删除、乐观锁更新、数据填充、数据脱敏、等等....
|
details: MyBatis-Flex 提供了非常灵活的 QueryWrapper,支持关联查询、多表查询、多主键、逻辑删除、乐观锁更新、数据填充、数据脱敏、等等....
|
||||||
- title: 更高的性能
|
- title: 更高的性能
|
||||||
details: MyBatis-Flex 通过独特的架构,没有任何 Mybatis 拦截器、在 SQL 执行的过程中,没有任何的 Sql Parse,因此会带来指数级的性能增长。
|
details: MyBatis-Flex 通过独特的架构,没有任何 MyBatis 拦截器、在 SQL 执行的过程中,没有任何的 SQL Parse,因此会带来指数级的性能增长。
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
# Mybatis-Flex 的增删改功能
|
# MyBatis-Flex 的增删改功能
|
||||||
|
|
||||||
Mybatis-Flex 内置了一个名为 `BaseMapper` 的接口,它实现了基本的增删改查功能以及分页查询功能。
|
MyBatis-Flex 内置了一个名为 `BaseMapper` 的接口,它实现了基本的增删改查功能以及分页查询功能。
|
||||||
|
|
||||||
> Mybatis-Flex 的 **代码生成器** 生成的所有 Mapper 辅助类,都是继承 BaseMapper。
|
> MyBatis-Flex 的 **代码生成器** 生成的所有 Mapper 辅助类,都是继承 BaseMapper。
|
||||||
|
|
||||||
## 新增数据
|
## 新增数据
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ accountMapper.deleteByCondition(ACCOUNT.ID.ge(100));
|
|||||||
delete from tb_account where id >= 100;
|
delete from tb_account where id >= 100;
|
||||||
```
|
```
|
||||||
|
|
||||||
>tip: QueryWrapper 非常灵活,也是 Mybatis-Flex 的特色之一,更多关于 QueryWrapper 的
|
>tip: QueryWrapper 非常灵活,也是 MyBatis-Flex 的特色之一,更多关于 QueryWrapper 的
|
||||||
> 请看 [QueryWrapper 章节](./querywrapper)。
|
> 请看 [QueryWrapper 章节](./querywrapper)。
|
||||||
|
|
||||||
## 更新数据
|
## 更新数据
|
||||||
@ -71,15 +71,22 @@ delete from tb_account where id >= 100;
|
|||||||
在很多场景下,我们希望只更新**部分字段**,而更新的字段中,一些为 null,一些非 null。此时需要用到 `UpdateEntity` 工具类,以下是示例代码:
|
在很多场景下,我们希望只更新**部分字段**,而更新的字段中,一些为 null,一些非 null。此时需要用到 `UpdateEntity` 工具类,以下是示例代码:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
Account account = UpdateEntity.of(Account.class);
|
Account account = UpdateEntity.of(Account.class, 100);
|
||||||
account.setId(100);
|
//Account account = UpdateEntity.of(Account.class);
|
||||||
|
//account.setId(100);
|
||||||
|
|
||||||
account.setUserName(null);
|
account.setUserName(null);
|
||||||
account.setAge(10);
|
account.setAge(10);
|
||||||
|
|
||||||
accountMapper.update(account);
|
accountMapper.update(account);
|
||||||
```
|
```
|
||||||
|
|
||||||
以上的示例中,会把 id (主键)为 100 这条数据中的 user_name 字段更新为 null,age 字段更新为 10,其他字段不会被更新。也就是说,通过 UpdateEntity 创建的对象,只会更新调用了 setter 方法的字段,若不调用 setter 方法,不管这个对象里的属性的值是什么,都不会更新到数据库。
|
|
||||||
|
|
||||||
|
|
||||||
|
以上的示例中,会把 id (主键)为 100 这条数据中的 user_name 字段更新为 null,age 字段更新为 10,其他字段不会被更新。
|
||||||
|
|
||||||
|
也就是说,通过 UpdateEntity 创建的对象,只会更新调用了 setter 方法的字段,若不调用 setter 方法,不管这个对象里的属性的值是什么,都不会更新到数据库。
|
||||||
|
|
||||||
其执行的 sql 内容如下:
|
其执行的 sql 内容如下:
|
||||||
|
|
||||||
@ -88,3 +95,16 @@ update tb_account
|
|||||||
set user_name = ?, age = ? where id = ?
|
set user_name = ?, age = ? where id = ?
|
||||||
#参数: null,10,100
|
#参数: null,10,100
|
||||||
```
|
```
|
||||||
|
|
||||||
|
注意:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Account account = UpdateEntity.of(Account.class, 100);
|
||||||
|
```
|
||||||
|
等同于:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Account account = UpdateEntity.of(Account.class);
|
||||||
|
account.setId(100);
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
83
docs/zh/base/batch.md
Normal file
83
docs/zh/base/batch.md
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# 批量操作
|
||||||
|
|
||||||
|
在 MyBatis-Flex 中,提供了许多批量操作(批量插入、批量更新等)的方法,当有多个方法的时候,会经常导致误用的情况。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `BaseMapper.insertBatch` 方法
|
||||||
|
|
||||||
|
这个方法的示例代码如下:
|
||||||
|
|
||||||
|
```java
|
||||||
|
List<Account> accounts = ....
|
||||||
|
mapper.insertBatch(accounts);
|
||||||
|
```
|
||||||
|
通过 `BaseMapper.insertBatch` 执行时,会通过 `accounts` 去组装一个如下的 SQL:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
insert into tb_account(id,nickname, .....) values
|
||||||
|
(100,"miachel100", ....),
|
||||||
|
(101,"miachel101", ....),
|
||||||
|
(102,"miachel102", ....),
|
||||||
|
(103,"miachel103", ....),
|
||||||
|
(104,"miachel104", ....),
|
||||||
|
(105,"miachel105", ....);
|
||||||
|
```
|
||||||
|
这种有一个特点:在小批量数据执行插入的时候,效率是非常高;但是当数据列表过多时,其生成的 SQL 可能会非常大, 这个大的 SQL
|
||||||
|
在传输和执行的时候就会变得很慢了。
|
||||||
|
|
||||||
|
因此,`BaseMapper.insertBatch` 方法只适用于在小批量数据插入的场景,比如 100 条数据以内。
|
||||||
|
|
||||||
|
## `Db.executeBatch` 方法
|
||||||
|
|
||||||
|
`Db.executeBatch` 可以用于进行批量的插入、修改和删除,以下是使用 `Db.executeBatch` 进行批量插入的示例:
|
||||||
|
|
||||||
|
```java
|
||||||
|
List<Account> accounts = ....
|
||||||
|
Db.executeBatch(accounts.size(), 1000, AccountMapper.class, (mapper, index) -> {
|
||||||
|
Account account = accounts.get(index);
|
||||||
|
mapper.insert(account);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
`Db.executeBatch` 是通过 JDBC 的 `Statement.executeBatch()` 进行批量执行;这个在大批量数据执行的时候,效率要比 `BaseMapper.insertBatch` 高出许多;
|
||||||
|
|
||||||
|
::: tip 提示
|
||||||
|
我看到有一些同学担心 `BaseMapper.insertBatch` 被误用,在 `IService` 中通过使用 `Db.executeBatch` 重写了 Service 的 `insertBatch` 方法,这也是没问题的。但还是需要明白,
|
||||||
|
`BaseMapper.insertBatch` 和 `Db.executeBatch` 的底层实现差异,以及不同的使用场景。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## `Db.updateBatch` 方法
|
||||||
|
|
||||||
|
这个方法的示例代码如下:
|
||||||
|
|
||||||
|
```java
|
||||||
|
List<Account> accounts = ....
|
||||||
|
String sql = "insert into tb_account(user_name,age,birthday) values (?,?,?)";
|
||||||
|
Db.updateBatch(sql, new BatchArgsSetter() {
|
||||||
|
@Override
|
||||||
|
public int getBatchSize() {
|
||||||
|
return accounts.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] getSqlArgs(int index) {
|
||||||
|
Account account = accounts = accounts.get(index);
|
||||||
|
Object[] args = new Object[3];
|
||||||
|
args[0] = account.getUserName;
|
||||||
|
args[1] = account.getAge();
|
||||||
|
args[2] = new Date();
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
虽然这个方法叫 `updateBatch`,但一样可以执行 `insert`、`delete`、`update` 等任何 SQL; 这个方法类似 Spring 的 `jdbcTemplate.batchUpdate()` 方法。
|
||||||
|
|
||||||
|
|
||||||
|
## `Db.updateEntitiesBatch` 方法
|
||||||
|
这个方法用于批量根据 id 更新 entity,其是对 `Db.executeBatch` 的封装,使用代码如下:
|
||||||
|
|
||||||
|
```java
|
||||||
|
List<Account> accounts = ....
|
||||||
|
Db.updateEntitiesBatch(accounts, 1000);
|
||||||
|
```
|
||||||
@ -35,7 +35,7 @@ Page<Row> rowPage = Db.paginate("tb_account",3,10,query);
|
|||||||
|
|
||||||
> Db 工具类还提供了更多 增、删、改、查和分页查询等方法。
|
> Db 工具类还提供了更多 增、删、改、查和分页查询等方法。
|
||||||
>
|
>
|
||||||
> 具体参考: [Db.java](./mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Db.java) 。
|
> 具体参考: [Db.java](https://gitee.com/mybatis-flex/mybatis-flex/blob/main/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Db.java) 。
|
||||||
|
|
||||||
## Row.toEntity()
|
## Row.toEntity()
|
||||||
|
|
||||||
88
docs/zh/base/field-query.md
Normal file
88
docs/zh/base/field-query.md
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# 一对多、多对一
|
||||||
|
|
||||||
|
在很多场景下,我们可能会用到 `一对多`、`一对一`、`多对一`、`多对多`等场景的关联查询,MyBatis-Flex 内置了相关的方法,用于支持此类场景。
|
||||||
|
|
||||||
|
## 代码示例(多对多)
|
||||||
|
|
||||||
|
以下是文章的示例,一篇文章可能归属于多个分类,一个类可以有多篇文章,需要用到中间表 `article_category_mapping`。
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class Article {
|
||||||
|
private Long id;
|
||||||
|
private String title;
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
//文章的归属分类,可能是 1 个或者多个
|
||||||
|
private List<Category> categories;
|
||||||
|
|
||||||
|
//getter setter
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
查询代码如下:
|
||||||
|
|
||||||
|
```java {10-14}
|
||||||
|
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||||
|
.select().form(ARTICLE)
|
||||||
|
.where(ARTICLE.id.ge(100));
|
||||||
|
|
||||||
|
List<Article> articles = mapper.selectListByQuery(queryWrapper
|
||||||
|
, fieldQueryBuilder -> fieldQueryBuilder
|
||||||
|
.field(Article::getCategories) // 或者 .field("categories")
|
||||||
|
.type(Category.class) //非集合,自动读取 type,可以不指定 type
|
||||||
|
.queryWrapper(article -> QueryWrapper.create()
|
||||||
|
.select().from(CATEGORY)
|
||||||
|
.where(CATEGORY.id.in(
|
||||||
|
select("category_id").from("article_category_mapping")
|
||||||
|
.where("article_id = ?", article.getId())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
通过以上代码可以看出,`Article.categories` 字段的结果,来源于 `queryWrapper()` 方法构建的 `QueryWrapper`。
|
||||||
|
|
||||||
|
其原理是:MyBatis-Flex 的内部逻辑是先查询出 `Article` 的数据,然后再根据 `Article` 构建出新的 SQL,查询分类,并赋值给 `Article.categories`,
|
||||||
|
假设 `Article` 有 10 条数据,那么最终会进行 11 次数据库查询。
|
||||||
|
|
||||||
|
查询的 SQL 大概如下:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
select * from tb_article where id >= 100;
|
||||||
|
|
||||||
|
-- 以上 SQL 得到结果后,再执行查询分类的 SQL,如下:
|
||||||
|
select * from tb_category where id in
|
||||||
|
(select category_id from article_category_mapping where article_id = 100);
|
||||||
|
|
||||||
|
select * from tb_category where id in
|
||||||
|
(select category_id from article_category_mapping where article_id = 101);
|
||||||
|
|
||||||
|
select * from tb_category where id in
|
||||||
|
(select category_id from article_category_mapping where article_id = 102);
|
||||||
|
|
||||||
|
select * from tb_category where id in
|
||||||
|
(select category_id from article_category_mapping where article_id = 103);
|
||||||
|
|
||||||
|
select * from tb_category where id in
|
||||||
|
(select category_id from article_category_mapping where article_id = 104);
|
||||||
|
|
||||||
|
select * from tb_category where id in
|
||||||
|
(select category_id from article_category_mapping where article_id = 105);
|
||||||
|
|
||||||
|
select * from tb_category where id in
|
||||||
|
(select category_id from article_category_mapping where article_id = 106);
|
||||||
|
|
||||||
|
select * from tb_category where id in
|
||||||
|
(select category_id from article_category_mapping where article_id = 107);
|
||||||
|
|
||||||
|
select * from tb_category where id in
|
||||||
|
(select category_id from article_category_mapping where article_id = 108);
|
||||||
|
|
||||||
|
select * from tb_category where id in
|
||||||
|
(select category_id from article_category_mapping where article_id = 109);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 其他场景
|
||||||
|
|
||||||
|
通过以上内容看出,`Article` 的任何属性,都是可以通过传入 `FieldQueryBuilder` 来构建 `QueryWrapper` 进行再次查询,
|
||||||
|
这些不仅仅只适用于 `一对多`、`一对一`、`多对一`、`多对多`等场景。任何 `Article` 对象里的属性,需要二次查询赋值的,都是可以通过这种方式进行。
|
||||||
@ -1,26 +1,148 @@
|
|||||||
# Mybatis-Flex 的查询和分页
|
# MyBatis-Flex 的查询和分页
|
||||||
|
|
||||||
## 基础查询
|
## 基础查询
|
||||||
|
|
||||||
在 Mybatis-Flex 的 BaseMapper 中,提供了如下的功能用于查询数据库的数据:
|
在 MyBatis-Flex 的 `BaseMapper` 中,提供了如下的功能用于查询数据库的数据:
|
||||||
|
|
||||||
- **selectOneById(id)**:根据主键 id 查询数据
|
- **selectOneById(id)**:根据主键 id 查询数据
|
||||||
- **selectOneByMap(map)**:根据 `map<字段名,值>` 组成的条件查询 1 条数据,若命中多条数据,则只返回第一条数据。
|
- **selectOneByMap(map)**:根据 `map<字段名,值>` 组成的条件查询 1 条数据,若命中多条数据,则只返回第一条数据。
|
||||||
- **selectOneByCondition(condition)**:根据 condition 组成的条件查询 1 条数据,若命中多条数据,则只返回第一条数据。
|
- **selectOneByCondition(condition)**:根据 condition 组成的条件查询 1 条数据,若命中多条数据,则只返回第一条数据。
|
||||||
- **selectOneByQuery(query)**:根据 QueryWrapper 组成的条件查询 1 条数据,若命中多条数据,则只返回第一条数据。
|
- **selectOneByQuery(query)**:根据 QueryWrapper 组成的条件查询 1 条数据,若命中多条数据,则只返回第一条数据。
|
||||||
|
- **selectOneByQueryAs(query, asType)**:和 `selectOneByQuery` 方法类似,但是在某些场景下,`query` 可能包含了 `left join` 等多表查询,返回的数据和 entity 字段不一致时,
|
||||||
|
可以通过 `asType` 参数来指定接收的数据类型(通常是 dto、vo 等)。
|
||||||
- **selectListByIds(idList)**:根据多个 id 查询,返回多条数据
|
- **selectListByIds(idList)**:根据多个 id 查询,返回多条数据
|
||||||
- **selectListByMap(map)**:根据 `map<字段名,值>` 组成的条件查询数据。
|
- **selectListByMap(map)**:根据 `map<字段名,值>` 组成的条件查询数据。
|
||||||
- **selectListByMap(map, count)**:根据 `map<字段名,值>` 组成的条件查询数据,只取前 count 条。
|
- **selectListByMap(map, count)**:根据 `map<字段名,值>` 组成的条件查询数据,只取前 count 条。
|
||||||
- **selectListByCondition(condition)**:根据 condition 组成的条件查询数据。
|
- **selectListByCondition(condition)**:根据 condition 组成的条件查询数据。
|
||||||
- **selectListByCondition(condition, count)**:根据 condition 组成的条件查询数据,只取前 count 条。
|
- **selectListByCondition(condition, count)**:根据 condition 组成的条件查询数据,只取前 count 条。
|
||||||
- **selectListByQuery(query)**: 根据 QueryWrapper 组成的条件查询数据。
|
- **selectListByQuery(query)**: 根据 QueryWrapper 组成的条件查询数据。
|
||||||
|
- **selectListByQueryAs(query, asType)**: 和 `selectListByQuery` 方法类似,但是在某些场景下,`query` 可能包含了 `left join` 等多表查询,返回的数据和 entity 字段不一致时,
|
||||||
|
可以通过 `asType` 参数来指定接收的数据类型(通常是 dto、vo 等)。
|
||||||
- **selectAll**:查询所有数据。
|
- **selectAll**:查询所有数据。
|
||||||
|
- **selectObjectByQuery(query)**:查询只返回 1 列,并只有 1 条数据的场景。
|
||||||
|
- **selectObjectListByQuery(query)**:查询只返回 1 列场景,比如 `QueryWrapper.create().select(ACCOINT.ID).from(...)`。
|
||||||
|
- **selectObjectListByQueryAs(query, asType)**:对 `selectObjectListByQuery` 进行封装,并转换为特定的类型。
|
||||||
- **selectCountByCondition**:根据 QueryWrapper 查询数据量。
|
- **selectCountByCondition**:根据 QueryWrapper 查询数据量。
|
||||||
- **selectCountByQuery**:根据 QueryWrapper 查询数据量。
|
- **selectCountByQuery**:根据 QueryWrapper 查询数据量。
|
||||||
|
|
||||||
|
## 关联查询(或多表查询)
|
||||||
|
|
||||||
|
在 `BaseMapper` 中,提供了 `selectOneByQueryAs`、`selectListByQueryAs` 、`paginateAs` 等方法,用于处理关联查询的场景。
|
||||||
|
|
||||||
|
假设有 `tb_account` 用户表和 `tb_article` 文章表,他们的字段分别如下:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE IF NOT EXISTS `tb_account`
|
||||||
|
(
|
||||||
|
`id` INTEGER PRIMARY KEY auto_increment,
|
||||||
|
`user_name` VARCHAR(100),
|
||||||
|
`age` Integer,
|
||||||
|
`birthday` DATETIME
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `tb_article`
|
||||||
|
(
|
||||||
|
`id` INTEGER PRIMARY KEY auto_increment,
|
||||||
|
`account_id` Integer,
|
||||||
|
`title` VARCHAR(100),
|
||||||
|
`content` text
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
当我们进行关联查询时,可以通过如下代码进行:
|
||||||
|
|
||||||
|
1、定义 `ArticleDTO` 类
|
||||||
|
```java
|
||||||
|
public class ArticleDTO {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long accountId;
|
||||||
|
private String title;
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
//以下用户相关字段
|
||||||
|
private String userName;
|
||||||
|
private int age;
|
||||||
|
private Date birthday;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2、使用 `QueryWrapper` 构建 `left join` 查询,查询结果通过 `ArticleDTO` 类型接收。
|
||||||
|
```java
|
||||||
|
QueryWrapper query = QueryWrapper.create()
|
||||||
|
.select(ARTICLE.ALL_COLUMNS)
|
||||||
|
.select(ACCOUNT.USER_NAME,ACCOUNT.AGE,ACCOUNT.BIRTHDAY)
|
||||||
|
.from(ARTICLE)
|
||||||
|
.leftJoin(ACCOUNT).on(ARTICLE.ACCOUNT_ID.eq(ACCOUNT.ID))
|
||||||
|
.where(ACCOUNT.ID.ge(0));
|
||||||
|
|
||||||
|
List<ArticleDTO> results = mapper.selectListByQueryAs(query, ArticleDTO.class);
|
||||||
|
System.out.println(results);
|
||||||
|
```
|
||||||
|
|
||||||
|
假设 `ArticleDTO` 定义的属性和 SQL 查询的字段不一致时,例如:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class ArticleDTO {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long accountId;
|
||||||
|
private String title;
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
//以下用户字段 和 用户表定义的列不一致,表定义的列为 user_name
|
||||||
|
private String authorName;
|
||||||
|
private int authorAge;
|
||||||
|
private Date birthday;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
那么, `QueryWrapper` 需要添加 `as`,修改如下:
|
||||||
|
|
||||||
|
```java 3,4
|
||||||
|
QueryWrapper query = QueryWrapper.create()
|
||||||
|
.select(ARTICLE.ALL_COLUMNS)
|
||||||
|
.select(ACCOUNT.USER_NAME.as(ArticleDTO::getAuthorName)
|
||||||
|
,ACCOUNT.AGE.as(ArticleDTO::getAuthorAge)
|
||||||
|
,ACCOUNT.BIRTHDAY
|
||||||
|
)
|
||||||
|
.from(ARTICLE)
|
||||||
|
.leftJoin(ACCOUNT).on(ARTICLE.ACCOUNT_ID.eq(ACCOUNT.ID))
|
||||||
|
.where(ACCOUNT.ID.ge(0));
|
||||||
|
|
||||||
|
List<ArticleDTO> results = mapper.selectListByQueryAs(query, ArticleDTO.class);
|
||||||
|
System.out.println(results);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
**注意事项:**
|
||||||
|
|
||||||
|
关联查询(`selectOneByQueryAs`、`selectListByQueryAs` 、`paginateAs` 等方法)中的 `asType` 参数类型(比如:`ArticleDTO`),
|
||||||
|
一样支持使用 `@Column`、`@ColumnMask` 注解以及 `@Table` 的 `onInsert`、`onUpdate`、`onSet` 配置。
|
||||||
|
|
||||||
|
同时,要求 `asType` 中的类型,必须定义属性来映射查询到的数据集,因此,以下的使用示例是 **错误** 的:
|
||||||
|
|
||||||
|
```java
|
||||||
|
QueryWrapper query = QueryWrapper.create()
|
||||||
|
.select(ACCOUNT.id)
|
||||||
|
.from(ACCOUNT);
|
||||||
|
|
||||||
|
List<Long> results = mapper.selectOneByQueryAs(query, Long.class);
|
||||||
|
```
|
||||||
|
在以上的示例中, **错误** 的原因是因为 `Long` 这个类型,并没有名称为 `id` 的属性,用来映射查询的结果集。
|
||||||
|
|
||||||
|
在以上的场景中,可以使用如下的方法(以下代码示例是正确的):
|
||||||
|
```java
|
||||||
|
QueryWrapper query = QueryWrapper.create()
|
||||||
|
.select(ACCOUNT.id)
|
||||||
|
.from(ACCOUNT);
|
||||||
|
|
||||||
|
List<Long> results = mapper.selectObjectListByQueryAs(query, Long.class);
|
||||||
|
```
|
||||||
|
|
||||||
## 分页查询
|
## 分页查询
|
||||||
|
|
||||||
在 Mybatis-Flex 的 BaseMapper 中,提供了如下的分页查询功能:
|
在 MyBatis-Flex 的 BaseMapper 中,提供了如下的分页查询功能:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
Page<T> paginate(int pageNumber, int pageSize, QueryWrapper queryWrapper);
|
Page<T> paginate(int pageNumber, int pageSize, QueryWrapper queryWrapper);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# 灵活的 QueryWrapper
|
# 灵活的 QueryWrapper
|
||||||
在 [增删改](./add-delete-update) 和 [查询和分页](./query) 章节中,我们随时能看到 QueryWrapper 的身影,QueryWrapper 是用于构造 Sql 的
|
在 [增删改](./add-delete-update) 和 [查询和分页](./query) 章节中,我们随时能看到 QueryWrapper 的身影,QueryWrapper 是用于构造 Sql 的
|
||||||
强有力工具,也是 Mybatis-Flex 的亮点和特色。
|
强有力工具,也是 MyBatis-Flex 的亮点和特色。
|
||||||
|
|
||||||
::: tip 提示
|
::: tip 提示
|
||||||
QueryWrapper 可以被序列化通过 RPC 进行传输,因此,在微服务项目中,我们可以在客户端(网关、Controller 层等)构造出 QueryWrapper,传给
|
QueryWrapper 可以被序列化通过 RPC 进行传输,因此,在微服务项目中,我们可以在客户端(网关、Controller 层等)构造出 QueryWrapper,传给
|
||||||
@ -21,7 +21,7 @@ public class AccountController {
|
|||||||
@GetMapping("/accounts")
|
@GetMapping("/accounts")
|
||||||
List<Account> selectList() {
|
List<Account> selectList() {
|
||||||
|
|
||||||
//构造 QueryWrapper
|
//构造 QueryWrapper,也支持使用 QueryWrapper.create() 构造,效果相同
|
||||||
QueryWrapper query = new QueryWrapper();
|
QueryWrapper query = new QueryWrapper();
|
||||||
query.where(ACCOUNT.ID.ge(100));
|
query.where(ACCOUNT.ID.ge(100));
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ where id >= 100
|
|||||||
## select *
|
## select *
|
||||||
|
|
||||||
```java
|
```java
|
||||||
QueryWrapper query=new QueryWrapper();
|
QueryWrapper query = new QueryWrapper();
|
||||||
query.select(ACCOUNT.ID, ACCOUNT.USER_NAME)
|
query.select(ACCOUNT.ID, ACCOUNT.USER_NAME)
|
||||||
.from(ACCOUNT)
|
.from(ACCOUNT)
|
||||||
```
|
```
|
||||||
@ -68,7 +68,7 @@ SELECT id, user_name FROM tb_account
|
|||||||
QueryWrapper query = new QueryWrapper()
|
QueryWrapper query = new QueryWrapper()
|
||||||
.select(
|
.select(
|
||||||
ACCOUNT.ID.as("accountId")
|
ACCOUNT.ID.as("accountId")
|
||||||
, ACCOUNT.USER_NAME
|
, ACCOUNT.USER_NAME)
|
||||||
.from(ACCOUNT.as("a"));
|
.from(ACCOUNT.as("a"));
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -119,6 +119,68 @@ SELECT id, user_name, MAX(birthday), AVG(sex) AS sex_avg
|
|||||||
FROM tb_account
|
FROM tb_account
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## select case...when
|
||||||
|
|
||||||
|
**示例 1:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
QueryWrapper wrapper = QueryWrapper.create()
|
||||||
|
.select(ACCOUNT.ID
|
||||||
|
,case_().when(ACCOUNT.ID.ge(2)).then("x2")
|
||||||
|
.when(ACCOUNT.ID.ge(1)).then("x1")
|
||||||
|
.else_("x100")
|
||||||
|
.end().as("xName")
|
||||||
|
```
|
||||||
|
|
||||||
|
其查询生成的 Sql 如下:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT `id`,
|
||||||
|
(CASE WHEN `id` >= 2 THEN 'x2'
|
||||||
|
WHEN `id` >= 1 THEN 'x1'
|
||||||
|
ELSE 'x100'
|
||||||
|
END) AS `xName`
|
||||||
|
FROM `tb_account`
|
||||||
|
```
|
||||||
|
|
||||||
|
SQL 执行的结果如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
|id |xName |
|
||||||
|
|1 |x1 |
|
||||||
|
|2 |x2 |
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
**示例 2:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||||
|
.select(ACCOUNT.ALL_COLUMNS,
|
||||||
|
case_(ACCOUNT.ID)
|
||||||
|
.when(100).then(100)
|
||||||
|
.when(200).then(200)
|
||||||
|
.else_(300).end().as("result"))
|
||||||
|
.from(ACCOUNT)
|
||||||
|
.where(ACCOUNT.USER_NAME.like("michael"));
|
||||||
|
```
|
||||||
|
|
||||||
|
其查询生成的 Sql 如下:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT *,
|
||||||
|
(CASE `id`
|
||||||
|
WHEN 100 THEN 100
|
||||||
|
WHEN 200 THEN 200
|
||||||
|
ELSE 300 END) AS `result`
|
||||||
|
FROM `tb_account` WHERE `user_name` LIKE ?
|
||||||
|
```
|
||||||
|
|
||||||
|
::: tip 提示
|
||||||
|
在以上示例中,由于 `case` 和 `else` 属于 Java 关键字,无法使用其进行方法命名,因此会添加一个下划线小尾巴 `"_"` 变成 `case_` 和 `else_`,这是无奈之举。
|
||||||
|
在以后的 QueryWrapper 构建中,遇到 java 关键字也会采用类型的解决方法。
|
||||||
|
:::
|
||||||
|
|
||||||
## where
|
## where
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@ -171,6 +233,23 @@ SELECT * FROM tb_account
|
|||||||
WHERE user_name LIKE ?
|
WHERE user_name LIKE ?
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## where 动态条件 3
|
||||||
|
|
||||||
|
```java 1,5
|
||||||
|
String name = null;
|
||||||
|
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||||
|
.select().from(ACCOUNT)
|
||||||
|
.where(ACCOUNT.ID.ge(100)) // when....
|
||||||
|
.and(ACCOUNT.USER_NAME.like(name).when(StringUtil::isNotBlank));
|
||||||
|
```
|
||||||
|
|
||||||
|
其查询生成的 Sql 如下:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM tb_account
|
||||||
|
WHERE id >= ?
|
||||||
|
```
|
||||||
|
|
||||||
## where select
|
## where select
|
||||||
```java
|
```java
|
||||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||||
@ -282,6 +361,28 @@ SELECT * FROM tb_account
|
|||||||
ORDER BY age ASC, user_name DESC NULLS LAST
|
ORDER BY age ASC, user_name DESC NULLS LAST
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## hint
|
||||||
|
|
||||||
|
Hint 是数据库厂商(比如 Oracle、MySQL、达梦等)提供的一种 SQL语法,它允许用户在 SQL 语句中插入相关的语法,从而影响 SQL 的执行方式。
|
||||||
|
它是一种【非常规】的直接影响优化器、指定执行计划的 SQL 优化手段。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
QueryWrapper queryWrapper=QueryWrapper.create()
|
||||||
|
.select().hint("INDEX_DESC")
|
||||||
|
.from(ACCOUNT)
|
||||||
|
.orderBy(ACCOUNT.AGE.asc(), ACCOUNT.USER_NAME.desc().nullsLast());
|
||||||
|
```
|
||||||
|
|
||||||
|
其查询生成的 Sql 如下:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT /*+ INDEX_DESC */ * FROM tb_account
|
||||||
|
ORDER BY age ASC, user_name DESC NULLS LAST
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## join(left join,inner join...)
|
## join(left join,inner join...)
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@ -370,7 +471,7 @@ UNION ALL (SELECT id FROM tb_article)
|
|||||||
## limit... offset
|
## limit... offset
|
||||||
|
|
||||||
::: tip 提示
|
::: tip 提示
|
||||||
在 "limit... offset" 的示例中,Mybatis-Flex 能够自动识别当前数据库👍,并根据数据库的类型生成不同的 SQL,用户也可以很轻易的通过 DialectFactory 注册(新增或改写)自己的实现方言。
|
在 "limit... offset" 的示例中,MyBatis-Flex 能够自动识别当前数据库👍,并根据数据库的类型生成不同的 SQL,用户也可以很轻易的通过 DialectFactory 注册(新增或改写)自己的实现方言。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
@ -383,7 +484,7 @@ QueryWrapper queryWrapper = QueryWrapper.create()
|
|||||||
.offset(20);
|
.offset(20);
|
||||||
```
|
```
|
||||||
|
|
||||||
MySql 下执行的代码如下:
|
MySQL 下执行的代码如下:
|
||||||
```sql
|
```sql
|
||||||
SELECT * FROM `tb_account` ORDER BY `id` DESC LIMIT 20, 10
|
SELECT * FROM `tb_account` ORDER BY `id` DESC LIMIT 20, 10
|
||||||
```
|
```
|
||||||
@ -426,7 +527,7 @@ SELECT * FROM "tb_account" ORDER BY "id" DESC ROWS 20 TO 30
|
|||||||
|
|
||||||
**疑问1:示例代码中的 QueryWrapper 所需要的 "ACCOUNT" 从哪里来的?**
|
**疑问1:示例代码中的 QueryWrapper 所需要的 "ACCOUNT" 从哪里来的?**
|
||||||
|
|
||||||
答:Mybatis-Flex 使用了 APT(Annotation Processing Tool)在项目编译的时候,
|
答:MyBatis-Flex 使用了 APT(Annotation Processing Tool)在项目编译的时候,
|
||||||
会自动根据 Entity 类定义的字段生成 "ACCOUNT" 类以及 Entity 对应的 Mapper 类, 通过开发工具构建项目(如下图),
|
会自动根据 Entity 类定义的字段生成 "ACCOUNT" 类以及 Entity 对应的 Mapper 类, 通过开发工具构建项目(如下图),
|
||||||
或者执行 maven 编译命令: `mvn clean package` 都可以自动生成。这个原理和 lombok 一致。
|
或者执行 maven 编译命令: `mvn clean package` 都可以自动生成。这个原理和 lombok 一致。
|
||||||
|
|
||||||
@ -451,4 +552,4 @@ QueryWrapper query1 = QueryWrapper.create()
|
|||||||
QueryWrapper query2 = QueryWrapper.create()
|
QueryWrapper query2 = QueryWrapper.create()
|
||||||
.where(ACCOUNT.AGE.ge(18));
|
.where(ACCOUNT.AGE.ge(18));
|
||||||
```
|
```
|
||||||
在以上的 `query1` 和 `query2` 中,它们构建出来的 SQL 条件是完全一致的,因为 Mybatis-Flex 会自动忽略 null 值的条件。
|
在以上的 `query1` 和 `query2` 中,它们构建出来的 SQL 条件是完全一致的,因为 MyBatis-Flex 会自动忽略 null 值的条件。
|
||||||
@ -2,7 +2,34 @@
|
|||||||
|
|
||||||
MyBatis-Flex 提供了一个名为 `IService` 的接口,及其默认实现类 `ServiceImpl` ,用于简化在 「Service」 层重复定义 「Mapper」 层的方法。
|
MyBatis-Flex 提供了一个名为 `IService` 的接口,及其默认实现类 `ServiceImpl` ,用于简化在 「Service」 层重复定义 「Mapper」 层的方法。
|
||||||
|
|
||||||
> `IService` 接口只是提供了简单且常用的 “增删改查” 方法,更多细节以及复杂的业务,还是需要使用 `BaseMapper` 进行处理。
|
> `IService` 接口只是提供了简单且常用的 “增删改查” 方法,更多细节以及复杂的业务,还是需要使用 `Mapper` 进行处理。
|
||||||
|
|
||||||
|
|
||||||
|
## 示例代码
|
||||||
|
|
||||||
|
接口:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public interface IAccountService extends IService{
|
||||||
|
//你的自定义方法
|
||||||
|
List<Account> customMethod();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
实现类:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Component
|
||||||
|
public class AccountServiceImpl implements IAccountService
|
||||||
|
extends ServiceImpl<AccountMapper, Account>{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Account> customMethod(){
|
||||||
|
//返回 id >= 100 的数据
|
||||||
|
return list(ACCOUNT.ID.ge(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 保存数据
|
## 保存数据
|
||||||
|
|
||||||
@ -70,4 +97,4 @@ MyBatis-Flex 提供了一个名为 `IService` 的接口,及其默认实现类
|
|||||||
|
|
||||||
## 其他方法
|
## 其他方法
|
||||||
|
|
||||||
- **getBaseMapper()**:获取对应的 `BaseMapper` 接口。
|
- **getMapper()**:获取对应的 `BaseMapper` 接口。
|
||||||
@ -36,13 +36,13 @@ Mybaits-Flex 消息包含了如下内容:
|
|||||||
|
|
||||||
::: tip 提示
|
::: tip 提示
|
||||||
通过以上的消息内容可知:每个 SQL 的执行,都包含了:哪个访问用户、哪个 IP 地址访问,访问的是哪个 URL 地址,这个 SQL 的参数是什么,执行的时间是什么,执行
|
通过以上的消息内容可知:每个 SQL 的执行,都包含了:哪个访问用户、哪个 IP 地址访问,访问的是哪个 URL 地址,这个 SQL 的参数是什么,执行的时间是什么,执行
|
||||||
花费了多少时间等等。这样,通过 Mybatis-flex 的 SQL 审计功能,我们能全盘了解到每个 SQL 的执行情况。
|
花费了多少时间等等。这样,通过 MyBatis-flex 的 SQL 审计功能,我们能全盘了解到每个 SQL 的执行情况。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
## 自定义 SQL 审计内容
|
## 自定义 SQL 审计内容
|
||||||
|
|
||||||
Mybatis-Flex 内置了一个名为 `MessageFactory` 的接口,我们只需实现该接口,并为 `AuditManager` 配置新的 `MessageFactory` 即可,如下所示:
|
MyBatis-Flex 内置了一个名为 `MessageFactory` 的接口,我们只需实现该接口,并为 `AuditManager` 配置新的 `MessageFactory` 即可,如下所示:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class MyMessageFactory implements MessageFactory {
|
public class MyMessageFactory implements MessageFactory {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# @Column 注解的使用
|
# @Column 注解的使用
|
||||||
|
|
||||||
Mybatis-Flex 提供了 `@Column` 用来对字段进行更多的配置,以下是 `@Column` 的代码定义:
|
MyBatis-Flex 提供了 `@Column` 用来对字段进行更多的配置,以下是 `@Column` 的代码定义:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public @interface Column {
|
public @interface Column {
|
||||||
|
|||||||
69
docs/zh/core/data-permission.md
Normal file
69
docs/zh/core/data-permission.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# 数据权限
|
||||||
|
|
||||||
|
数据权限指的是不同的用户,通过某一个方法去查询的时候,得到的是不同的数据结果集。常见的数据权限有:
|
||||||
|
|
||||||
|
- 获取全部数据
|
||||||
|
- 仅获取本人创建的数据
|
||||||
|
- 获取当前用户的部门数据
|
||||||
|
- 获取部门级以下部门的数据
|
||||||
|
- 获取某个地区的数据
|
||||||
|
- 等等
|
||||||
|
|
||||||
|
这一些,都是通过当前的用户的信息(部门、角色、权限等),查询时,添加特定的条件。在 MyBatis-Flex 中,我们可以通过 2 种方式来实现这一种需求。
|
||||||
|
|
||||||
|
## 方式1:使用自定义数据方言 `IDialect`
|
||||||
|
|
||||||
|
在自定义方言中,重写 `forSelectByQuery` 方法,这个方法是用于构建返回根据 `QueryWrapper` 查询的方法, 以下是示例代码:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class MyPermissionDialect extends CommonsDialectImpl{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String forSelectByQuery(QueryWrapper queryWrapper) {
|
||||||
|
|
||||||
|
//获取当前用户信息,为 queryWrapper 添加额外的条件
|
||||||
|
queryWrapper.and("...");
|
||||||
|
|
||||||
|
return supper.buildSelectSql(queryWrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
在项目启动时,通过 `DialectFactory` 注册 `MyPermissionDialect`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
DialectFactory.registerDialect(DbType.MYSQL, new MyPermissionDialect());
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
**常见问题1:通过重写 `IDialect` 后,所有的查询都添加了条件,但是有些表不需要条件如何做?**
|
||||||
|
|
||||||
|
>答:可以通过 CPI 获取 QueryWrapper 查询了哪些表,然后进行动态处理。例如 `List<QueryTable> tables = CPI.getQueryTables(queryWrapper)`,然后进一步对
|
||||||
|
> `tables` 进行验证是否需要添加数据权限。
|
||||||
|
|
||||||
|
## 方式2:重写 IService 的查询方法
|
||||||
|
|
||||||
|
在一般的应用中,查询是通过 Service 进行的,MyBatis-Flex 提供了 `IService` 接口及其默认的 `ServiceImpl` 实现类。
|
||||||
|
|
||||||
|
我们可以通过构建自己的 `IServiceImpl` 来实现这一种需求,例如:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class MyServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected M mapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseMapper<T> getMapper() {
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<T> list(QueryWrapper query) {
|
||||||
|
//获取当前用户信息,为 queryWrapper 添加额外的条件
|
||||||
|
return IService.super.list(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
当然,在 `IService` 中,除了 `list` 方法以外,还有其他的查询方法,可能也需要复写一下。
|
||||||
@ -12,13 +12,13 @@ public class Account{
|
|||||||
private TypeEnum typeEnum;
|
private TypeEnum typeEnum;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
在默认情况下,Mybatis 内置了一个名为:`EnumTypeHandler` 的处理器,用于处理这种场景。通过 `EnumTypeHandler` 处理后,数据库保存的是 `TypeEnum` 对应的属性名称,
|
在默认情况下,MyBatis 内置了一个名为:`EnumTypeHandler` 的处理器,用于处理这种场景。通过 `EnumTypeHandler` 处理后,数据库保存的是 `TypeEnum` 对应的属性名称,
|
||||||
是一个 String 类型。例如 `TypeEnum.TYPE1` 保存到数据库的内容为 `TYPE1` 这个字符串。
|
是一个 String 类型。例如 `TypeEnum.TYPE1` 保存到数据库的内容为 `TYPE1` 这个字符串。
|
||||||
|
|
||||||
## @EnumValue 注解
|
## @EnumValue 注解
|
||||||
|
|
||||||
|
|
||||||
但很多时候,我们希望保存到数据库的,是 `TypeEnum` 枚举的某个属性值,而非 `TYPE1` 字符串,那么,我就需要用到 Mybatis-Flex 提供的注解 `@EnumValue`,以下是示例代码:
|
但很多时候,我们希望保存到数据库的,是 `TypeEnum` 枚举的某个属性值,而非 `TYPE1` 字符串,那么,我就需要用到 MyBatis-Flex 提供的注解 `@EnumValue`,以下是示例代码:
|
||||||
|
|
||||||
```java 8,9
|
```java 8,9
|
||||||
public enum TypeEnum {
|
public enum TypeEnum {
|
||||||
@ -42,7 +42,7 @@ public enum TypeEnum {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
通过注解 `@EnumValue` 为 `code` 属性标注后,当我们保存 Account 内容到数据库时,Mybatis-Flex 会自动使用 `code` 属性值进行保存,同时在读取数据库内容的时候,Mybatis-Flex 自动把数据库的值转换为
|
通过注解 `@EnumValue` 为 `code` 属性标注后,当我们保存 Account 内容到数据库时,MyBatis-Flex 会自动使用 `code` 属性值进行保存,同时在读取数据库内容的时候,MyBatis-Flex 自动把数据库的值转换为
|
||||||
`TypeEnum` 枚举。
|
`TypeEnum` 枚举。
|
||||||
|
|
||||||
::: tip 注意事项
|
::: tip 注意事项
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
数据填充指的是,当 Entity 数据被插入 或者 更新的时候,会为字段进行一些默认的数据设置。这个非常有用,比如说当某个 entity 被插入时候
|
数据填充指的是,当 Entity 数据被插入 或者 更新的时候,会为字段进行一些默认的数据设置。这个非常有用,比如说当某个 entity 被插入时候
|
||||||
会设置一些数据插入的时间、数据插入的用户 id,多租户的场景下设置当前租户信息等等。
|
会设置一些数据插入的时间、数据插入的用户 id,多租户的场景下设置当前租户信息等等。
|
||||||
|
|
||||||
Mybatis-Flex 提供了两种方式,帮助开发者进行数据填充。
|
MyBatis-Flex 提供了两种方式,帮助开发者进行数据填充。
|
||||||
|
|
||||||
- 1、通过 `@Table` 注解的 `onInsert` 和 `onUpdate` 配置进行操作。这部分可以参考 [@Table 注解章节](./table) 。
|
- 1、通过 `@Table` 注解的 `onInsert` 和 `onUpdate` 配置进行操作。这部分可以参考 [@Table 注解章节](./table) 。
|
||||||
- 2、通过 `@Column` 注解的 `onInsertValue` 和 `onUpdateValue` 配置进行操作。这部分可以参考 [@Column 注解章节](./column)。
|
- 2、通过 `@Column` 注解的 `onInsertValue` 和 `onUpdateValue` 配置进行操作。这部分可以参考 [@Column 注解章节](./column)。
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# @Id 主键的使用
|
# @Id 主键的使用
|
||||||
|
|
||||||
在 Entity 类中,Mybatis-Flex 是使用 `@Id` 注解来标识主键的,如下代码所示:
|
在 Entity 类中,MyBatis-Flex 是使用 `@Id` 注解来标识主键的,如下代码所示:
|
||||||
|
|
||||||
```java 5
|
```java 5
|
||||||
@Table("tb_account")
|
@Table("tb_account")
|
||||||
@ -77,16 +77,16 @@ public enum KeyType {
|
|||||||
|
|
||||||
## 多主键、复合主键
|
## 多主键、复合主键
|
||||||
|
|
||||||
Mybatis-Flex 多主键就是在 Entity 类里有多个 `@Id` 注解标识而已,比如:
|
MyBatis-Flex 多主键就是在 Entity 类里有多个 `@Id` 注解标识而已,比如:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@Table("tb_account")
|
@Table("tb_account")
|
||||||
public class Account {
|
public class Account {
|
||||||
|
|
||||||
@Id(keyType=KeyType.Auto)
|
@Id(keyType = KeyType.Auto)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@Id(keyType=KeyType.Generator, value="uuid")
|
@Id(keyType = KeyType.Generator, value = KeyGenerators.uuid)
|
||||||
private String otherId;
|
private String otherId;
|
||||||
|
|
||||||
//getter setter
|
//getter setter
|
||||||
@ -96,19 +96,19 @@ public class Account {
|
|||||||
|
|
||||||
## 内置主键生成器
|
## 内置主键生成器
|
||||||
|
|
||||||
MyBatis-Flex 内置了三种主键生成器:
|
MyBatis-Flex 内置了三种主键生成器,他们的名称都定义在 `KeyGenerators` 类里:
|
||||||
|
|
||||||
- **UUIDKeyGenerator**:生成 UUID 作为数据库主键。
|
- **uuid**:通过 `UUIDKeyGenerator` 生成 UUID 作为数据库主键。
|
||||||
- **FlexIDKeyGenerator**:独创的 FlexID 算法生成数据库主键(了解更多信息请参阅[源码](https://gitee.com/mybatis-flex/mybatis-flex/blob/main/mybatis-flex-core/src/main/java/com/mybatisflex/core/keygen/impl/FlexIDKeyGenerator.java));
|
- **flexId**:独创的 FlexID 算法生成数据库主键(了解更多信息请参阅[源码](https://gitee.com/mybatis-flex/mybatis-flex/blob/main/mybatis-flex-core/src/main/java/com/mybatisflex/core/keygen/impl/FlexIDKeyGenerator.java))。
|
||||||
- **SnowFlakeIDKeyGenerator**:通过雪花算法生成数据库主键。
|
- **snowFlakeId**:通过雪花算法(`SnowFlakeIDKeyGenerator`)生成数据库主键。
|
||||||
|
|
||||||
这些主键生成器无需注册,直接使用即可:
|
这些主键生成器为 MyBatis-Flex 内置的,可直接使用:
|
||||||
|
|
||||||
```java
|
```java 4
|
||||||
@Table("tb_account")
|
@Table("tb_account")
|
||||||
public class Account {
|
public class Account {
|
||||||
|
|
||||||
@Id(keyType=KeyType.Generator, value="flexId")
|
@Id(keyType=KeyType.Generator, value=KeyGenerators.flexId)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
//getter setter
|
//getter setter
|
||||||
@ -166,7 +166,7 @@ public class Account {
|
|||||||
一般的项目中,通常是许多的 Entity 使用同一个数据库,同时使用一种主键生成方式,比如说都使用 自增,
|
一般的项目中,通常是许多的 Entity 使用同一个数据库,同时使用一种主键生成方式,比如说都使用 自增,
|
||||||
或者都使用通过序列(Sequence)生成,此时,我们是没有必要为每个 Entity 单独配置一样内容的。
|
或者都使用通过序列(Sequence)生成,此时,我们是没有必要为每个 Entity 单独配置一样内容的。
|
||||||
|
|
||||||
Mybatis-Flex 提供了一种全局配置的方式,代码如下:
|
MyBatis-Flex 提供了一种全局配置的方式,代码如下:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
FlexGlobalConfig.KeyConfig keyConfig = new FlexGlobalConfig.KeyConfig();
|
FlexGlobalConfig.KeyConfig keyConfig = new FlexGlobalConfig.KeyConfig();
|
||||||
@ -183,7 +183,7 @@ FlexGlobalConfig.getDefaultConfig().setKeyConfig(keyConfig);
|
|||||||
@Table("tb_account")
|
@Table("tb_account")
|
||||||
public class Account {
|
public class Account {
|
||||||
|
|
||||||
@Id()
|
@Id
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
我们可以进行表的字段设计时,用一个列标识该数据的 "删除状态",在 mybatis-flex 中,正常状态的值为 0, 已删除
|
我们可以进行表的字段设计时,用一个列标识该数据的 "删除状态",在 mybatis-flex 中,正常状态的值为 0, 已删除
|
||||||
的值为 1(可以通过设置 FlexGlobalConfig 来修改这个值)。
|
的值为 1(可以通过设置 FlexGlobalConfig 来修改这个值)。
|
||||||
|
|
||||||
## Mybatis-Flex 逻辑删除示例
|
## MyBatis-Flex 逻辑删除示例
|
||||||
|
|
||||||
假设在 tb_account 表中,存在一个为 is_deleted 的字段,用来标识该数据的逻辑删除,那么 tb_account 表
|
假设在 tb_account 表中,存在一个为 is_deleted 的字段,用来标识该数据的逻辑删除,那么 tb_account 表
|
||||||
对应的 "Account.java" 实体类应该配置如下:
|
对应的 "Account.java" 实体类应该配置如下:
|
||||||
@ -30,17 +30,17 @@ public class Account {
|
|||||||
```java
|
```java
|
||||||
accountMapper.deleteById(1);
|
accountMapper.deleteById(1);
|
||||||
```
|
```
|
||||||
Mybatis 执行的 SQL 如下:
|
MyBatis 执行的 SQL 如下:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
UPDATE `tb_account` SET `is_delete` = 1
|
UPDATE `tb_account` SET `is_delete` = 1
|
||||||
WHERE `id` = ? AND `is_delete` = 0
|
WHERE `id` = ? AND `is_delete` = 0
|
||||||
```
|
```
|
||||||
可以看出,当执行 deleteById 时,Mybatis 只是进行了 update 操作,而非 delete 操作。
|
可以看出,当执行 deleteById 时,MyBatis 只是进行了 update 操作,而非 delete 操作。
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
当 "tb_account" 的数据被删除时( is_delete = 1 时),我们通过 Mybatis-Flex 的 selectOneById 去查找数据时,会查询不到数据。
|
当 "tb_account" 的数据被删除时( is_delete = 1 时),我们通过 MyBatis-Flex 的 selectOneById 去查找数据时,会查询不到数据。
|
||||||
原因是 `selectOneById` 会自动添加上 `is_delete = 0` 条件,执行的 sql 如下:
|
原因是 `selectOneById` 会自动添加上 `is_delete = 0` 条件,执行的 sql 如下:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
## @ColumnMask
|
## @ColumnMask
|
||||||
|
|
||||||
Mybatis-Flex 提供了 `@ColumnMask()` 注解,以及内置的 9 种脱敏规则,帮助开发者方便的进行数据脱敏。例如:
|
MyBatis-Flex 提供了 `@ColumnMask()` 注解,以及内置的 9 种脱敏规则,帮助开发者方便的进行数据脱敏。例如:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@Table("tb_account")
|
@Table("tb_account")
|
||||||
@ -24,7 +24,7 @@ public class Account {
|
|||||||
|
|
||||||
以上的示例中,使用了 `CHINESE_NAME` 的脱敏规则,其主要用于处理 "中文名字" 的场景。当我们查询到 userName 为 `张三丰` 的时候,其内容自动被处理成 `张**`。
|
以上的示例中,使用了 `CHINESE_NAME` 的脱敏规则,其主要用于处理 "中文名字" 的场景。当我们查询到 userName 为 `张三丰` 的时候,其内容自动被处理成 `张**`。
|
||||||
|
|
||||||
除此之外,Mybatis-Flex 还提供了如下的 8 中脱敏规则(共9种),方便开发者直接使用:
|
除此之外,MyBatis-Flex 还提供了如下的 8 中脱敏规则(共9种),方便开发者直接使用:
|
||||||
|
|
||||||
- 手机号脱敏
|
- 手机号脱敏
|
||||||
- 固定电话脱敏
|
- 固定电话脱敏
|
||||||
|
|||||||
@ -30,7 +30,7 @@ dataSource2.setJdbcUrl(....)
|
|||||||
DataSource dataSource3 = new HikariDataSource();
|
DataSource dataSource3 = new HikariDataSource();
|
||||||
dataSource3.setJdbcUrl(....)
|
dataSource3.setJdbcUrl(....)
|
||||||
|
|
||||||
MybatisFlexBootstrap.getInstance()
|
MyBatisFlexBootstrap.getInstance()
|
||||||
.setDataSource("ds1", dataSource1)
|
.setDataSource("ds1", dataSource1)
|
||||||
.addDataSource("ds2", dataSource2)
|
.addDataSource("ds2", dataSource2)
|
||||||
.addDataSource("ds3", dataSource3)
|
.addDataSource("ds3", dataSource3)
|
||||||
|
|||||||
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
多租户简单来说是指一个单独的实例可以为多个用户(或组织)服务。多租户技术要求所有用户共用同一个数据中心,但能提供多个客户端相同甚至可定制化的服务,并且仍然可以保障客户的数据隔离。
|
多租户简单来说是指一个单独的实例可以为多个用户(或组织)服务。多租户技术要求所有用户共用同一个数据中心,但能提供多个客户端相同甚至可定制化的服务,并且仍然可以保障客户的数据隔离。
|
||||||
|
|
||||||
多租户的数据隔离有许多种方案,但最为常见的是以列进行隔离的方式。Mybatis-Flex 内置的正是通过指定的列(租户ID `tenant_id`)进行隔离的方案。
|
多租户的数据隔离有许多种方案,但最为常见的是以列进行隔离的方式。MyBatis-Flex 内置的正是通过指定的列(租户ID `tenant_id`)进行隔离的方案。
|
||||||
|
|
||||||
## 开始使用
|
## 开始使用
|
||||||
|
|
||||||
Mybatis-Flex 使用多租户需要 2 个步骤:
|
MyBatis-Flex 使用多租户需要 2 个步骤:
|
||||||
|
|
||||||
- step 1:通过 `@Column(tenantId = true)` 标识租户列。
|
- step 1:通过 `@Column(tenantId = true)` 标识租户列。
|
||||||
- step 2:为 `TenantManager` 配置 `TenantFactory`。
|
- step 2:为 `TenantManager` 配置 `TenantFactory`。
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# @Table 注解的使用
|
# @Table 注解的使用
|
||||||
|
|
||||||
在 Mybatis-Flex 中,`@Table` 主要是用于给 Entity 实体类添加标识,用于描述 实体类 和 数据库表 的关系,以及对实体类进行的一些
|
在 MyBatis-Flex 中,`@Table` 主要是用于给 Entity 实体类添加标识,用于描述 实体类 和 数据库表 的关系,以及对实体类进行的一些
|
||||||
功能辅助。
|
功能辅助。
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -73,10 +73,12 @@ Db.tx(() -> {
|
|||||||
|
|
||||||
## @Transactional
|
## @Transactional
|
||||||
|
|
||||||
Mybatis-Flex 已支持 Spring 框架的 `@Transactional`,在使用 Spring 的情况下,可以使用 `@Transactional` 进行事务管理。
|
MyBatis-Flex 已支持 Spring 框架的 `@Transactional`,在使用 SpringBoot 的情况下,可以直接使用 `@Transactional` 进行事务管理。
|
||||||
|
|
||||||
同理,使用 Spring 的 `TransactionTemplate` 进行事务管理也是没问题的。
|
同理,使用 Spring 的 `TransactionTemplate` 进行事务管理也是没问题的。
|
||||||
|
|
||||||
|
> 注意:若项目未使用 SpringBoot,只用到了 Spring,需要参考 MyBatis-Flex 的 [FlexTransactionAutoConfiguration](https://gitee.com/mybatis-flex/mybatis-flex/blob/main/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/FlexTransactionAutoConfiguration.java)
|
||||||
|
> 进行事务配置,才能正常使用 `@Transactional` 注解。
|
||||||
|
|
||||||
## 特征
|
## 特征
|
||||||
|
|
||||||
- 1、支持嵌套事务
|
- 1、支持嵌套事务
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# Mybatis-Flex 乐观锁
|
# MyBatis-Flex 乐观锁
|
||||||
|
|
||||||
## 使用场景
|
## 使用场景
|
||||||
|
|
||||||
@ -35,4 +35,4 @@ public class Account {
|
|||||||
需要注意的是:
|
需要注意的是:
|
||||||
|
|
||||||
- 1、在同一张表中,只能有一个被 `@Column(version = true)` 修饰的字段。
|
- 1、在同一张表中,只能有一个被 `@Column(version = true)` 修饰的字段。
|
||||||
- 2、Account 在插入数据时,若 version 未设置值,那么会自动被 Mybatis-Flex 设置为 0。
|
- 2、Account 在插入数据时,若 version 未设置值,那么会自动被 MyBatis-Flex 设置为 0。
|
||||||
@ -2,15 +2,14 @@
|
|||||||
|
|
||||||
## 示例中的 AccountMapper 和 "ACCOUNT" 在哪里,报错了。
|
## 示例中的 AccountMapper 和 "ACCOUNT" 在哪里,报错了。
|
||||||
|
|
||||||
Mybatis-Flex 使用了 APT 技术,这两个类是自动生成的。
|
MyBatis-Flex 使用了 APT 技术,这两个类是自动生成的。
|
||||||
参考:[Mybatis-Flex APT 配置 - Mybatis-Flex 官方网站](./others/apt.md)
|
参考:[MyBatis-Flex APT 配置 - MyBatis-Flex 官方网站](./others/apt.md)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## SpringBoot 项目,启动报错 Property 'sqlSessionFactory' or 'sqlSessionTempalte' are required
|
## SpringBoot 项目,启动报错 Property 'sqlSessionFactory' or 'sqlSessionTempalte' are required
|
||||||
|
|
||||||
如果当前依赖没有连接池相关依赖,则建议添加 HikariCP 依赖。
|
如果当前依赖没有连接池相关依赖,则建议添加 HikariCP 依赖。
|
||||||
如果已经添加了如 druid 依赖,则配置数据源类型 `spring.datasource.type=com.alibaba.druid.pool.DruidDataSource`。
|
|
||||||
|
|
||||||
SpringBoot v2.x 添加 hikariCP 的内容如下:
|
SpringBoot v2.x 添加 hikariCP 的内容如下:
|
||||||
|
|
||||||
@ -32,6 +31,9 @@ SpringBoot v3.x 添加 hikariCP 的内容如下:
|
|||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> 如果使用的是 druid 数据库连接池,则需要添加数据源类型的配置 `spring.datasource.type=com.alibaba.druid.pool.DruidDataSource`。
|
||||||
|
|
||||||
|
|
||||||
## 整合 Springboot 3 出现 ClassNotFoundException: NestedIOException 的错误
|
## 整合 Springboot 3 出现 ClassNotFoundException: NestedIOException 的错误
|
||||||
|
|
||||||
需要排除 flex 中的 mybatis-spring 的依赖,主动添加最新版本的 mybatis-spring 依赖。
|
需要排除 flex 中的 mybatis-spring 的依赖,主动添加最新版本的 mybatis-spring 依赖。
|
||||||
@ -64,7 +66,7 @@ SpringBoot v3.x 添加 hikariCP 的内容如下:
|
|||||||
|
|
||||||
原因是在数据源的配置中,未添加 `type` 字段的配置:
|
原因是在数据源的配置中,未添加 `type` 字段的配置:
|
||||||
|
|
||||||
```yaml 3
|
```yaml:line-numbers 3
|
||||||
spring:
|
spring:
|
||||||
datasource:
|
datasource:
|
||||||
type: com.alibaba.druid.pool.DruidDataSource
|
type: com.alibaba.druid.pool.DruidDataSource
|
||||||
@ -75,10 +77,35 @@ spring:
|
|||||||
第 3 行中的 `type` 字段不能为空,这个并非是 MyBaits-Flex 的问题,而是 Spring 没有内置对 Druid 数据源类型
|
第 3 行中的 `type` 字段不能为空,这个并非是 MyBaits-Flex 的问题,而是 Spring 没有内置对 Druid 数据源类型
|
||||||
的主动发现机制。若使用 `hikariCP` 数据源,则可以不配置 `type` 内容。
|
的主动发现机制。若使用 `hikariCP` 数据源,则可以不配置 `type` 内容。
|
||||||
|
|
||||||
> 若使用多数据源,或者把数据源配置到 `mybatis-flex.datasource` 下,使用 mybatis-flex 的数据源发现机制,
|
> 若把数据源配置到 `mybatis-flex.datasource` 下,使用 mybatis-flex 的数据源发现机制,
|
||||||
> 使用 druid 也可以不用配置 type,更多文档参考:[多数据源章节](./core/multi-datasource.md)。
|
> 使用 druid 则可以不用配置 type,更多文档参考:[多数据源章节](./core/multi-datasource.md)。
|
||||||
|
|
||||||
|
## 多数据源下,使用 Druid 无法启动,出现 "Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured." 错误。
|
||||||
|
|
||||||
|
在多数据源的场景下,不能使用 "druid-spring-boot-starter" 依赖,只能使用 "druid" 。
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>druid-spring-boot-starter</artifactId>
|
||||||
|
<version>${druid.version}</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
需要把以上的依赖,修改如下:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>druid</artifactId>
|
||||||
|
<version>${druid.version}</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
>错误原因是:druid-spring-boot-starter 内的 DruidDataSourceAutoConfigure 会去自动加载 spring.datasource 下的配置,当使用 MyBatis-Flex 的多数据源时,
|
||||||
|
> 这个配置已经不存在了。
|
||||||
|
|
||||||
## 与 PageHelper 集成出现错误
|
## 与 PageHelper 集成出现错误
|
||||||
|
|
||||||
在社区中,一些老的项目在使用到了开源项目 PageHelper,用于解决 xml 的分页问题,在和 Mybatis-flex 整合使用中,出现了一些错误,
|
在社区中,一些老的项目在使用到了开源项目 PageHelper,用于解决 xml 的分页问题,在和 MyBatis-flex 整合使用中,出现了一些错误,
|
||||||
这是许多热心的同学给出的解决方案:https://gitee.com/mybatis-flex/mybatis-flex/issues/I71AUE
|
这是许多热心的同学给出的解决方案:https://gitee.com/mybatis-flex/mybatis-flex/issues/I71AUE
|
||||||
@ -1,6 +1,6 @@
|
|||||||
# Mybatis-Flex 和同类框架「性能」对比
|
# MyBatis-Flex 和同类框架「性能」对比
|
||||||
|
|
||||||
本文主要是展示了 Mybatis-Flex 和 Mybaits-Plus 的「性能」对比。Mybaits-Plus 是一个非常优秀 Mybaits 增强框架,
|
本文主要是展示了 MyBatis-Flex 和 Mybaits-Plus 的「性能」对比。Mybaits-Plus 是一个非常优秀 Mybaits 增强框架,
|
||||||
其开源于 2016 年,有很多的成功案例。
|
其开源于 2016 年,有很多的成功案例。
|
||||||
|
|
||||||
本文只阐述了「性能」方面的对比,「功能」对比请参考 [这里](./comparison.md)。
|
本文只阐述了「性能」方面的对比,「功能」对比请参考 [这里](./comparison.md)。
|
||||||
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
## 测试单条数据查询
|
## 测试单条数据查询
|
||||||
|
|
||||||
Mybatis-Flex 的代码如下:
|
MyBatis-Flex 的代码如下:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
QueryWrapper queryWrapper = new QueryWrapper();
|
QueryWrapper queryWrapper = new QueryWrapper();
|
||||||
@ -28,7 +28,7 @@ queryWrapper.where(FLEX_ACCOUNT.ID.ge(100)
|
|||||||
mapper.selectOneByQuery(queryWrapper);
|
mapper.selectOneByQuery(queryWrapper);
|
||||||
```
|
```
|
||||||
|
|
||||||
Mybatis-Plus 的代码如下:
|
MyBatis-Plus 的代码如下:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
QueryWrapper queryWrapper = new QueryWrapper();
|
QueryWrapper queryWrapper = new QueryWrapper();
|
||||||
@ -85,14 +85,14 @@ mapper.selectOne(queryWrapper);
|
|||||||
```
|
```
|
||||||
|
|
||||||
::: tip 测试结论
|
::: tip 测试结论
|
||||||
> Mybatis-Flex 的查询单条数据的速度,大概是 Mybatis-Plus 的 5 ~ 10+ 倍。
|
> MyBatis-Flex 的查询单条数据的速度,大概是 MyBatis-Plus 的 5 ~ 10+ 倍。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## 测试列表(List)数据查询
|
## 测试列表(List)数据查询
|
||||||
|
|
||||||
要求返回的数据为 10 条数据。
|
要求返回的数据为 10 条数据。
|
||||||
|
|
||||||
Mybatis-Flex 的代码如下:
|
MyBatis-Flex 的代码如下:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
QueryWrapper queryWrapper = new QueryWrapper();
|
QueryWrapper queryWrapper = new QueryWrapper();
|
||||||
@ -102,7 +102,7 @@ queryWrapper.where(FLEX_ACCOUNT.ID.ge(100).or(FLEX_ACCOUNT.USER_NAME
|
|||||||
mapper.selectListByQuery(queryWrapper);
|
mapper.selectListByQuery(queryWrapper);
|
||||||
```
|
```
|
||||||
|
|
||||||
Mybatis-Plus 的代码如下:
|
MyBatis-Plus 的代码如下:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
QueryWrapper queryWrapper = new QueryWrapper();
|
QueryWrapper queryWrapper = new QueryWrapper();
|
||||||
@ -159,13 +159,13 @@ mapper.selectList(queryWrapper);
|
|||||||
```
|
```
|
||||||
|
|
||||||
::: tip 测试结论
|
::: tip 测试结论
|
||||||
> Mybatis-Flex 的查询 10 条数据的速度,大概是 Mybatis-Plus 的 5~10 倍左右。
|
> MyBatis-Flex 的查询 10 条数据的速度,大概是 MyBatis-Plus 的 5~10 倍左右。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## 分页查询
|
## 分页查询
|
||||||
|
|
||||||
|
|
||||||
Mybatis-Flex 的代码如下:
|
MyBatis-Flex 的代码如下:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
QueryWrapper queryWrapper = new QueryWrapper()
|
QueryWrapper queryWrapper = new QueryWrapper()
|
||||||
@ -173,7 +173,7 @@ QueryWrapper queryWrapper = new QueryWrapper()
|
|||||||
mapper.paginate(page, pageSize, 20000, queryWrapper);
|
mapper.paginate(page, pageSize, 20000, queryWrapper);
|
||||||
```
|
```
|
||||||
|
|
||||||
Mybatis-Plus 的代码如下:
|
MyBatis-Plus 的代码如下:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
LambdaQueryWrapper<PlusAccount> queryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<PlusAccount> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
# Mybatis-Flex 和同类框架「功能」对比
|
# MyBatis-Flex 和同类框架「功能」对比
|
||||||
|
|
||||||
MyBatis-Flex 主要是和 `MyBatis-Plus` 与 `Fluent-Mybatis` 对比,内容来源其官网、git 或者 网络文章,若有错误欢迎纠正。
|
MyBatis-Flex 主要是和 `MyBatis-Plus` 与 `Fluent-MyBatis` 对比,内容来源其官网、git 或者 网络文章,若有错误欢迎纠正。
|
||||||
|
|
||||||
- MyBatis-Plus:老牌的 MyBatis 增强框架,开源于 2016 年。
|
- MyBatis-Plus:老牌的 MyBatis 增强框架,开源于 2016 年。
|
||||||
- Fluent-Mybatis:阿里云开发的 Mybatis 增强框架(来至于阿里云·云效产品团队)
|
- Fluent-MyBatis:阿里云开发的 MyBatis 增强框架(来自于阿里云·云效产品团队)
|
||||||
|
|
||||||
> 本文只阐述了「功能」方面的对比,**「性能」** 对比请参考 [这里](./benchmark.md)。
|
> 本文只阐述了「功能」方面的对比,**「性能」** 对比请参考 [这里](./benchmark.md)。
|
||||||
> 若发现对比中有错误,请加入 Mybatis-Flex QQ 交流群:532992631,然后联系群主纠正。
|
> 若发现对比中有错误,请加入 MyBatis-Flex QQ 交流群:532992631,然后联系群主纠正。
|
||||||
|
|
||||||
## 功能对比
|
## 功能对比
|
||||||
|
|
||||||
|
|
||||||
| 功能或特点 | MyBatis-Flex | MyBatis-Plus | Fluent-Mybatis |
|
| 功能或特点 | MyBatis-Flex | MyBatis-Plus | Fluent-MyBatis |
|
||||||
| -------- | -------- | -------- | -------- |
|
| -------- | -------- | -------- | -------- |
|
||||||
| 对 entity 的基本增删改查 | ✅ | ✅ | ✅ |
|
| 对 entity 的基本增删改查 | ✅ | ✅ | ✅ |
|
||||||
| 分页查询 | ✅ | ✅ | ✅ |
|
| 分页查询 | ✅ | ✅ | ✅ |
|
||||||
@ -24,7 +24,7 @@ MyBatis-Flex 主要是和 `MyBatis-Plus` 与 `Fluent-Mybatis` 对比,内容来
|
|||||||
| 多种 id 生成策略 | ✅ | ✅ | ✅ |
|
| 多种 id 生成策略 | ✅ | ✅ | ✅ |
|
||||||
| 支持多主键、复合主键 | ✅ | ❌ | ❌ |
|
| 支持多主键、复合主键 | ✅ | ❌ | ❌ |
|
||||||
| 字段的 typeHandler 配置 | ✅ | ✅ | ✅ |
|
| 字段的 typeHandler 配置 | ✅ | ✅ | ✅ |
|
||||||
| 除了 Mybatis,无其他第三方依赖(更轻量) | ✅ | ❌ | ❌ |
|
| 除了 MyBatis,无其他第三方依赖(更轻量) | ✅ | ❌ | ❌ |
|
||||||
| QueryWrapper 是否支持在微服务项目下进行 RPC 传输 | ✅ | ❌ | 未知 |
|
| QueryWrapper 是否支持在微服务项目下进行 RPC 传输 | ✅ | ❌ | 未知 |
|
||||||
| 逻辑删除 | ✅ | ✅ | ✅ |
|
| 逻辑删除 | ✅ | ✅ | ✅ |
|
||||||
| 乐观锁 | ✅ | ✅ | ✅ |
|
| 乐观锁 | ✅ | ✅ | ✅ |
|
||||||
@ -50,30 +50,29 @@ MyBatis-Flex 主要是和 `MyBatis-Plus` 与 `Fluent-Mybatis` 对比,内容来
|
|||||||
**MyBatis-Flex:**
|
**MyBatis-Flex:**
|
||||||
|
|
||||||
````java
|
````java
|
||||||
QueryCondition condition = EMPLOYEE.LAST_NAME.like("B")
|
QueryWrapper query = QueryWrapper.create()
|
||||||
|
.where(EMPLOYEE.LAST_NAME.like(searchWord)) //条件为null时自动忽略
|
||||||
.and(EMPLOYEE.GENDER.eq(1))
|
.and(EMPLOYEE.GENDER.eq(1))
|
||||||
.and(EMPLOYEE.AGE.gt(24));
|
.and(EMPLOYEE.AGE.gt(24));
|
||||||
List<Employee> employees = employeeMapper.selectListByCondition(condition);
|
List<Employee> employees = employeeMapper.selectListByQuery(query);
|
||||||
````
|
````
|
||||||
|
|
||||||
**MyBatis-Plus:**
|
**MyBatis-Plus:**
|
||||||
|
|
||||||
````java
|
````java
|
||||||
QueryWrapper<Employee> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<Employee> queryWrapper = Wrappers.query()
|
||||||
queryWrapper
|
.like(searchWord != null, "last_name", searchWord)
|
||||||
.like("last_name","B")
|
.eq("gender", 1)
|
||||||
.eq("gender",1)
|
.gt("age", 24);
|
||||||
.gt("age",24);
|
|
||||||
List<Employee> employees = employeeMapper.selectList(queryWrapper);
|
List<Employee> employees = employeeMapper.selectList(queryWrapper);
|
||||||
````
|
````
|
||||||
或者 MyBatis-Plus 的另一种写法:
|
或者 MyBatis-Plus 的 lambda 写法:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<Employee> queryWrapper = Wrappers.<Employee>lambdaQuery()
|
||||||
queryWrapper
|
.like(StringUtils.isNotEmpty(searchWord), Employee::getUserName,"B")
|
||||||
.like(Employee::getUserName,"B")
|
.eq(Employee::getGender, 1)
|
||||||
.eq(Employee::getGender,1)
|
.gt(Employee::getAge, 24);
|
||||||
.gt(Employee::getAge,24);
|
|
||||||
List<Employee> employees = employeeMapper.selectList(queryWrapper);
|
List<Employee> employees = employeeMapper.selectList(queryWrapper);
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -82,7 +81,7 @@ List<Employee> employees = employeeMapper.selectList(queryWrapper);
|
|||||||
|
|
||||||
````java
|
````java
|
||||||
EmployeeQuery query = new EmployeeQuery()
|
EmployeeQuery query = new EmployeeQuery()
|
||||||
.where.lastName().like("B")
|
.where.lastName().like(searchWord, If::notNull)
|
||||||
.and.gender().eq(1)
|
.and.gender().eq(1)
|
||||||
.and.age().gt(24)
|
.and.age().gt(24)
|
||||||
.end();
|
.end();
|
||||||
@ -96,7 +95,7 @@ List<Employee> employees = employeeMapper.listEntity(query);
|
|||||||
**MyBatis-Flex:**
|
**MyBatis-Flex:**
|
||||||
|
|
||||||
````java
|
````java
|
||||||
QueryWrapper query = new QueryWrapper()
|
QueryWrapper query = QueryWrapper.create()
|
||||||
.select(
|
.select(
|
||||||
ACCOUNT.ID,
|
ACCOUNT.ID,
|
||||||
ACCOUNT.USER_NAME,
|
ACCOUNT.USER_NAME,
|
||||||
@ -108,17 +107,16 @@ List<Employee> employees = employeeMapper.selectListByQuery(query);
|
|||||||
**MyBatis-Plus:**
|
**MyBatis-Plus:**
|
||||||
|
|
||||||
````java
|
````java
|
||||||
QueryWrapper<Employee> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<Employee> queryWrapper = Wrappers.query()
|
||||||
queryWrapper
|
|
||||||
.select(
|
.select(
|
||||||
"id"
|
"id",
|
||||||
,"user_name"
|
"user_name",
|
||||||
,"max(birthday)"
|
"max(birthday)",
|
||||||
,"avg(birthday) as sex_avg"
|
"avg(birthday) as sex_avg"
|
||||||
);
|
);
|
||||||
List<Employee> employees = employeeMapper.selectList(queryWrapper);
|
List<Employee> employees = employeeMapper.selectList(queryWrapper);
|
||||||
````
|
````
|
||||||
> 缺点:字段硬编码,容易错处。无法使用 ide 的字段进行重构,无法使用 IDE 自动提示,发生错误不能及时发现。
|
> 缺点:字段硬编码,容易拼错。无法使用 ide 的字段进行重构,无法使用 IDE 自动提示,发生错误不能及时发现。
|
||||||
|
|
||||||
|
|
||||||
**Fluent-MyBatis:**
|
**Fluent-MyBatis:**
|
||||||
@ -153,19 +151,24 @@ OR (age IN (18,19,20) AND user_name LIKE "%michael%" )
|
|||||||
QueryWrapper query = QueryWrapper.create()
|
QueryWrapper query = QueryWrapper.create()
|
||||||
.where(ACCOUNT.ID.ge(100))
|
.where(ACCOUNT.ID.ge(100))
|
||||||
.and(ACCOUNT.SEX.eq(1).or(ACCOUNT.SEX.eq(2)))
|
.and(ACCOUNT.SEX.eq(1).or(ACCOUNT.SEX.eq(2)))
|
||||||
.or(ACCOUNT.AGE.in(18,19,20).and(ACCOUNT.USER_NAME.like("michael")));
|
.or(ACCOUNT.AGE.in(18, 19, 20).and(ACCOUNT.USER_NAME.like("michael")));
|
||||||
```
|
```
|
||||||
|
|
||||||
**MyBatis-Plus:**
|
**MyBatis-Plus:**
|
||||||
|
|
||||||
```java
|
```java
|
||||||
QueryWrapper<Employee> query = new QueryWrapper<>();
|
QueryWrapper<Employee> query = Wrappers.query()
|
||||||
queryWrapper.ge("id",100)
|
.ge("id", 100)
|
||||||
.and(i->i.eq("sex",1).or(x->x.eq("sex",2)))
|
.and(i -> i.eq("sex", 1).or(x -> x.eq("sex", 2)))
|
||||||
.or(i->i.in("age",{18,19,20}).like("user_name","michael"));
|
.or(i -> i.in("age", 18, 19, 20).like("user_name", "michael"));
|
||||||
|
// or lambda
|
||||||
|
LambdaQueryWrapper<Employee> query = Wrappers.<Employee>lambdaQuery()
|
||||||
|
.ge(Employee::getId, 100)
|
||||||
|
.and(i -> i.eq(Employee::getSex, 1).or(x -> x.eq(Employee::getSex, 2)))
|
||||||
|
.or(i -> i.in(Employee::getAge, 18, 19, 20).like(Employee::getUserName, "michael"));
|
||||||
```
|
```
|
||||||
|
|
||||||
**Fluent-Mybatis:**
|
**Fluent-MyBatis:**
|
||||||
|
|
||||||
```java
|
```java
|
||||||
AccountQuery query = new AccountQuery()
|
AccountQuery query = new AccountQuery()
|
||||||
@ -269,7 +272,7 @@ set user_name = "michael", age = 18, birthday = null
|
|||||||
where id = 100
|
where id = 100
|
||||||
```
|
```
|
||||||
|
|
||||||
**Mybatis-Flex** 代码如下:
|
**MyBatis-Flex** 代码如下:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
Account account = UpdateEntity.of(Account.class);
|
Account account = UpdateEntity.of(Account.class);
|
||||||
@ -281,7 +284,7 @@ account.setBirthday(null);
|
|||||||
accountMapper.update(account);
|
accountMapper.update(account);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Mybatis-Plus** 代码如下(或可使用 Mybatis-Plus 的 `LambdaUpdateWrapper`,但性能没有 `UpdateWrapper` 好):
|
**MyBatis-Plus** 代码如下(或可使用 MyBatis-Plus 的 `LambdaUpdateWrapper`,但性能没有 `UpdateWrapper` 好):
|
||||||
|
|
||||||
```java
|
```java
|
||||||
UpdateWrapper<Account> updateWrapper = new UpdateWrapper<>();
|
UpdateWrapper<Account> updateWrapper = new UpdateWrapper<>();
|
||||||
@ -293,7 +296,7 @@ updateWrapper.set("birthday", null);
|
|||||||
accountMapper.update(null, updateWrapper);
|
accountMapper.update(null, updateWrapper);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Fluent-Mybatis** 代码如下:
|
**Fluent-MyBatis** 代码如下:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
AccountUpdate update = new AccountUpdate()
|
AccountUpdate update = new AccountUpdate()
|
||||||
|
|||||||
@ -27,12 +27,12 @@ Maven示例:
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-core</artifactId>
|
<artifactId>mybatis-flex-core</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-processor</artifactId>
|
<artifactId>mybatis-flex-processor</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
@ -46,19 +46,18 @@ Gradle示例:
|
|||||||
```groovy
|
```groovy
|
||||||
// file: build.gradle
|
// file: build.gradle
|
||||||
ext {
|
ext {
|
||||||
mybatis_flex_version = '1.2.3'
|
mybatis_flex_version = '1.3.0'
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.mybatis-flex:mybatis-flex-core:${mybatis_flex_version}")
|
implementation("com.mybatis-flex:mybatis-flex-core:${mybatis_flex_version}")
|
||||||
|
|
||||||
// 启用APT
|
// 启用APT
|
||||||
annotationProcessor("com.mybatis-flex:mybatis-flex-annotation:${mybatis_flex_version}")
|
annotationProcessor("com.mybatis-flex:mybatis-flex-processor:${mybatis_flex_version}")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**第 3 步:编写实体类和 Mapper**
|
**第 3 步:编写实体类和 Mapper**
|
||||||
|
|
||||||
> 这部分可以使用 Mybatis-Flex 的代码生成器来生成实体类哟,功能非常强大的。详情进入:[代码生成器章节](../others/codegen.md) 了解。
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@Table("tb_account")
|
@Table("tb_account")
|
||||||
public class Account {
|
public class Account {
|
||||||
@ -82,20 +81,22 @@ public interface AccountMapper extends BaseMapper<Account> {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**第4步:编译项目自动生成辅助类**
|
> 这部分也可以使用 MyBatis-Flex 的代码生成器来生,功能非常强大的。详情进入:[代码生成器章节](../others/codegen.md) 了解。
|
||||||
|
|
||||||
Mybatis-Flex 使用了 APT(Annotation Processing Tool)技术,在项目编译的时,会自动生成辅助操作类。
|
|
||||||
|
**第4步:编译项目,自动生成查询辅助类**
|
||||||
|
|
||||||
|
MyBatis-Flex 使用了 APT(Annotation Processing Tool)技术,在项目编译的时,会自动生成辅助操作类。
|
||||||
|
|
||||||
- Maven 编译: `mvn clean package`
|
- Maven 编译: `mvn clean package`
|
||||||
- Gradle 编译: `gradlew classes`
|
- Gradle 编译: `gradlew classes`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
更多信息请参考:[APT 设置章节](../others/apt.md)
|
更多信息请参考:[APT 设置章节](../others/apt.md)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**第 5 步:通过 main 方法开始使用 Mybatis-Flex(无 Spring 的场景)**
|
**第 5 步:通过 main 方法开始使用 MyBatis-Flex(无 Spring 的场景)**
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class HelloWorld {
|
public class HelloWorld {
|
||||||
@ -109,7 +110,7 @@ public class HelloWorld {
|
|||||||
|
|
||||||
//配置数据源
|
//配置数据源
|
||||||
MybatisFlexBootstrap.getInstance()
|
MybatisFlexBootstrap.getInstance()
|
||||||
.setDatasource(dataSource)
|
.setDataSource(dataSource)
|
||||||
.addMapper(AccountMapper.class)
|
.addMapper(AccountMapper.class)
|
||||||
.start();
|
.start();
|
||||||
|
|
||||||
@ -134,12 +135,15 @@ public class HelloWorld {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> 以上的示例中, `ACCOUNT` 为 Mybatis-Flex 通过 APT 自动生成,无需手动编码。更多查看 [APT 文档](../others/apt.md)。
|
> 以上的示例中, `ACCOUNT` 为 MyBatis-Flex 通过 APT 自动生成,无需手动编码。更多查看 [APT 文档](../others/apt.md)。
|
||||||
|
>
|
||||||
|
>若觉得 APT 使用不习惯,
|
||||||
|
> 也可以使用代码生成器来生成。点击 [代码生成器文档](../others/codegen.md) 了解。
|
||||||
|
|
||||||
|
|
||||||
## 更多示例
|
## 更多示例
|
||||||
|
|
||||||
- 示例 1:[Mybatis-Flex 原生(非 Spring)](https://gitee.com/mybatis-flex/mybatis-flex/tree/main/mybatis-flex-test/mybatis-flex-native-test)
|
- 示例 1:[MyBatis-Flex 原生(非 Spring)](https://gitee.com/mybatis-flex/mybatis-flex-samples)
|
||||||
- 示例 2:[Mybatis-Flex with Spring](https://gitee.com/mybatis-flex/mybatis-flex/tree/main/mybatis-flex-test/mybatis-flex-spring-test)
|
- 示例 2:[MyBatis-Flex with Spring](https://gitee.com/mybatis-flex/mybatis-flex-samples)
|
||||||
- 示例 3:[Mybatis-Flex with Spring boot](https://gitee.com/mybatis-flex/mybatis-flex/tree/main/mybatis-flex-test/mybatis-flex-spring-boot-test)
|
- 示例 3:[MyBatis-Flex with Spring boot](https://gitee.com/mybatis-flex/mybatis-flex-samples)
|
||||||
- 示例 4:[Db + Row](https://gitee.com/mybatis-flex/mybatis-flex/blob/main/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/DbTestStarter.java)
|
- 示例 4:[Db + Row](https://gitee.com/mybatis-flex/mybatis-flex/blob/main/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/DbTestStarter.java)
|
||||||
@ -1,23 +1,23 @@
|
|||||||
# Maven 依赖
|
# Maven 依赖
|
||||||
|
|
||||||
> 以下的 xml maven 依赖示例中,可能并非最新的 Mybatis-Flex 版本,请自行查看最新版本,并修改版本号。
|
> 以下的 xml maven 依赖示例中,可能并非最新的 MyBatis-Flex 版本,请自行查看最新版本,并修改版本号。
|
||||||
>
|
>
|
||||||
> 建议配置 annotationProcessorPaths,那么可以省略mybatis-flex-processor的依赖
|
> 建议配置 annotationProcessorPaths,那么可以省略mybatis-flex-processor的依赖
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
1、只用到了 Mybatis,没用到 Spring 的场景:
|
1、只用到了 MyBatis,没用到 Spring 的场景:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-core</artifactId>
|
<artifactId>mybatis-flex-core</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-processor</artifactId>
|
<artifactId>mybatis-flex-processor</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
@ -28,12 +28,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-spring</artifactId>
|
<artifactId>mybatis-flex-spring</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-processor</artifactId>
|
<artifactId>mybatis-flex-processor</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
``````
|
``````
|
||||||
@ -44,12 +44,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
|
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-processor</artifactId>
|
<artifactId>mybatis-flex-processor</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
@ -70,7 +70,7 @@
|
|||||||
<path>
|
<path>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-processor</artifactId>
|
<artifactId>mybatis-flex-processor</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</path>
|
</path>
|
||||||
</annotationProcessorPaths>
|
</annotationProcessorPaths>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# Mybatis-Flex QQ 交流群
|
# MyBatis-Flex QQ 交流群
|
||||||
|
|
||||||
群号: 532992631
|
群号: 532992631
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Mybatis-Flex 支持的数据库类型
|
# MyBatis-Flex 支持的数据库类型
|
||||||
|
|
||||||
Mybatis-Flex 支持的数据库类型,如下表格所示,我们还可以通过自定义方言的方式,持续添加更多的数据库支持。
|
MyBatis-Flex 支持的数据库类型,如下表格所示,我们还可以通过自定义方言的方式,持续添加更多的数据库支持。
|
||||||
|
|
||||||
|
|
||||||
| 数据库 | 描述 |
|
| 数据库 | 描述 |
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
# 使用 Mybatis 原生功能
|
# 使用 MyBatis 原生功能
|
||||||
|
|
||||||
我们使用 Mybatis-Flex 作为 Mybatis 的增强框架进行代码开发,并不会影响原有的 Mybatis 的任何功能。
|
我们使用 MyBatis-Flex 作为 MyBatis 的增强框架进行代码开发,并不会影响原有的 MyBatis 的任何功能。
|
||||||
|
|
||||||
## 使用 `@Select` 等 Mybatis 原生注解
|
## 使用 `@Select` 等 MyBatis 原生注解
|
||||||
|
|
||||||
Mybatis 提供了 `@Insert` 、`@Delete` 、`@Update` 、`@Select` 4 个注解,用于对 Mapper 的方法进行配置,用于原生编写原生 SQL 进行增删改查,
|
MyBatis 提供了 `@Insert` 、`@Delete` 、`@Update` 、`@Select` 4 个注解,用于对 Mapper 的方法进行配置,用于原生编写原生 SQL 进行增删改查,
|
||||||
在 Mybatis-Flex 我们一样可以使用这些注解。例如:
|
在 MyBatis-Flex 我们一样可以使用这些注解。例如:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public interface MyAccountMapper extends BaseMapper<Account> {
|
public interface MyAccountMapper extends BaseMapper<Account> {
|
||||||
@ -15,9 +15,9 @@ public interface MyAccountMapper extends BaseMapper<Account> {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`@Insert` 、`@Delete` 、`@Update` 等注解也是一样的,也就是说,原有的 Mybatis 功能如何使用,在 Mybatis-Flex 就如何使用。
|
`@Insert` 、`@Delete` 、`@Update` 等注解也是一样的,也就是说,原有的 MyBatis 功能如何使用,在 MyBatis-Flex 就如何使用。
|
||||||
|
|
||||||
`@InsertProvider`、`@DeleteProvider`、`@UpdateProvider`、`@SelectProvider` 等还是和原生 Mybatis 一样的用法。
|
`@InsertProvider`、`@DeleteProvider`、`@UpdateProvider`、`@SelectProvider` 等还是和原生 MyBatis 一样的用法。
|
||||||
|
|
||||||
|
|
||||||
## 使用 xml 的方式
|
## 使用 xml 的方式
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
# Mybatis-Flex 是什么
|
# MyBatis-Flex 是什么
|
||||||
|
|
||||||
Mybatis-Flex 是一个优雅的 Mybatis 增强框架,它非常轻量、同时拥有极高的性能与灵活性。我们可以轻松的使用 Mybaits-Flex 链接任何数据库,其内置的
|
MyBatis-Flex 是一个优雅的 MyBatis 增强框架,它非常轻量、同时拥有极高的性能与灵活性。我们可以轻松的使用 Mybaits-Flex 链接任何数据库,其内置的
|
||||||
QueryWrapper<Badge type="tip" text="^亮点" /> 帮助我们极大的减少了 SQL 编写的工作的同时,减少出错的可能性。
|
QueryWrapper<Badge type="tip" text="^亮点" /> 帮助我们极大的减少了 SQL 编写的工作的同时,减少出错的可能性。
|
||||||
|
|
||||||
总而言之,Mybatis-Flex 能够极大地提高我们的开发效率和开发体验,让我们有更多的时间专注于自己的事情。
|
总而言之,MyBatis-Flex 能够极大地提高我们的开发效率和开发体验,让我们有更多的时间专注于自己的事情。
|
||||||
|
|
||||||
|
|
||||||
## 特征
|
## 特征
|
||||||
@ -12,8 +12,8 @@ QueryWrapper<Badge type="tip" text="^亮点" /> 帮助我们极大的减少了 S
|
|||||||
这带来了几个好处:1、极高的性能;2、极易对代码进行跟踪和调试; 3、把控性更高。
|
这带来了几个好处:1、极高的性能;2、极易对代码进行跟踪和调试; 3、把控性更高。
|
||||||
|
|
||||||
|
|
||||||
**2、灵活**:支持 Entity 的增删改查、以及分页查询的同时,Mybatis-Flex 提供了 Db + Row<Badge type="tip" text="^灵活" /> 工具,可以无需实体类对数据库进行增删改查以及分页查询。
|
**2、灵活**:支持 Entity 的增删改查、以及分页查询的同时,MyBatis-Flex 提供了 Db + Row<Badge type="tip" text="^灵活" /> 工具,可以无需实体类对数据库进行增删改查以及分页查询。
|
||||||
与此同时,Mybatis-Flex 内置的 QueryWrapper<Badge type="tip" text="^灵活" /> 可以轻易的帮助我们实现 **多表查询**、**链接查询**、**子查询** 等等常见的 SQL 场景。
|
与此同时,MyBatis-Flex 内置的 QueryWrapper<Badge type="tip" text="^灵活" /> 可以轻易的帮助我们实现 **多表查询**、**链接查询**、**子查询** 等等常见的 SQL 场景。
|
||||||
|
|
||||||
|
|
||||||
**3、强大**:支持任意关系型数据库,还可以通过方言持续扩展,同时支持 **多(复合)主键**、**逻辑删除**、**乐观锁配置**、**数据脱敏**、**数据审计**、
|
**3、强大**:支持任意关系型数据库,还可以通过方言持续扩展,同时支持 **多(复合)主键**、**逻辑删除**、**乐观锁配置**、**数据脱敏**、**数据审计**、
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Mybatis-Flex APT 配置
|
# MyBatis-Flex APT 配置
|
||||||
|
|
||||||
Mybatis-Flex 使用了 APT(Annotation Processing Tool)技术,在项目编译的时候,会自动根据 Entity 类定义的字段帮你生成 "ACCOUNT" 类以及 Entity 对应的 Mapper 类,
|
MyBatis-Flex 使用了 APT(Annotation Processing Tool)技术,在项目编译的时候,会自动根据 Entity 类定义的字段帮你生成 "ACCOUNT" 类以及 Entity 对应的 Mapper 类,
|
||||||
通过开发工具构建项目(如下图),或者执行 maven 编译命令: `mvn clean package` 都可以自动生成。这个原理和 lombok 一致。
|
通过开发工具构建项目(如下图),或者执行 maven 编译命令: `mvn clean package` 都可以自动生成。这个原理和 lombok 一致。
|
||||||
|
|
||||||

|

|
||||||
@ -9,7 +9,7 @@ Mybatis-Flex 使用了 APT(Annotation Processing Tool)技术,在项目编
|
|||||||
|
|
||||||
## 配置文件和选项
|
## 配置文件和选项
|
||||||
|
|
||||||
要对Mybatis-Flex 的APT细节选项进行配置,你需要在`resources`目录下创建名为`mybatis-flex.properties`的文件。
|
要对MyBatis-Flex 的APT细节选项进行配置,你需要在`resources`目录下创建名为`mybatis-flex.properties`的文件。
|
||||||
|
|
||||||
支持的配置选项如下:
|
支持的配置选项如下:
|
||||||
|
|
||||||
@ -18,6 +18,7 @@ Mybatis-Flex 使用了 APT(Annotation Processing Tool)技术,在项目编
|
|||||||
| processor.enable | 全局启用apt开关 | true/false | true |
|
| processor.enable | 全局启用apt开关 | true/false | true |
|
||||||
| processor.mappersGenerateEnable | 开启 Mapper 自动生成 | true/false | false |
|
| processor.mappersGenerateEnable | 开启 Mapper 自动生成 | true/false | false |
|
||||||
| processor.genPath | APT 代码生成路径 | 合法的绝对或相对路径 | target/generated-sources/annotations |
|
| processor.genPath | APT 代码生成路径 | 合法的绝对或相对路径 | target/generated-sources/annotations |
|
||||||
|
| processor.allInTables | 是否所有的类都生成在 Tables 类里 | true/false | false |
|
||||||
| processor.tablesPackage | Tables 类名 | 合法的包名 | ${entityPackage}.table |
|
| processor.tablesPackage | Tables 类名 | 合法的包名 | ${entityPackage}.table |
|
||||||
| processor.tablesClassName | Tables 类名 | 合法的类名 | Tables |
|
| processor.tablesClassName | Tables 类名 | 合法的类名 | Tables |
|
||||||
| processor.baseMapperClass | 自定义 Mapper 的父类 | 全路径类名 | com.mybatisflex.core.BaseMapper |
|
| processor.baseMapperClass | 自定义 Mapper 的父类 | 全路径类名 | com.mybatisflex.core.BaseMapper |
|
||||||
@ -135,7 +136,7 @@ processor.baseMapperClass=com.domain.mapper.MyBaseMapper
|
|||||||
## 和 Lombok、Mapstruct 整合
|
## 和 Lombok、Mapstruct 整合
|
||||||
|
|
||||||
在很多项目中,用到了 Lombok 帮我们减少代码编写,同时用到 Mapstruct 进行 bean 转换。使用到 Lombok 和 Mapstruct 时,其要求我们再 pom.xml 添加 `annotationProcessorPaths` 配置,
|
在很多项目中,用到了 Lombok 帮我们减少代码编写,同时用到 Mapstruct 进行 bean 转换。使用到 Lombok 和 Mapstruct 时,其要求我们再 pom.xml 添加 `annotationProcessorPaths` 配置,
|
||||||
此时,我们也需要把 Mybatis-Flex 的 annotation 添加到 `annotationProcessorPaths` 配置里去,如下图所示:
|
此时,我们也需要把 MyBatis-Flex 的 annotation 添加到 `annotationProcessorPaths` 配置里去,如下图所示:
|
||||||
|
|
||||||
```xml 24,25,26,27,28
|
```xml 24,25,26,27,28
|
||||||
<plugin>
|
<plugin>
|
||||||
@ -178,7 +179,7 @@ processor.baseMapperClass=com.domain.mapper.MyBaseMapper
|
|||||||
```
|
```
|
||||||
dependencies {
|
dependencies {
|
||||||
...
|
...
|
||||||
annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:1.2.3'
|
annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:1.3.0'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# Mybatis-Flex 代码生成器
|
# MyBatis-Flex 代码生成器
|
||||||
|
|
||||||
在 mybatis-flex 的模块 `mybatis-flex-codegen` 中,提供了可以通过数据库表,生成 Entity 类和 Mapper 类的功能。当我们把数据库表设计完成
|
在 mybatis-flex 的模块 `mybatis-flex-codegen` 中,提供了可以通过数据库表,生成 Entity 类和 Mapper 类的功能。当我们把数据库表设计完成
|
||||||
后可以使用其快速生成 Entity 和 Mapper 的 java 类。
|
后可以使用其快速生成 Entity 和 Mapper 的 java 类。
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-codegen</artifactId>
|
<artifactId>mybatis-flex-codegen</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -445,7 +445,7 @@ public class ColumnConfig implements Serializable {
|
|||||||
|
|
||||||
## 自定义属性类型
|
## 自定义属性类型
|
||||||
|
|
||||||
Mybatis-Flex 内置了一个名为:`JdbcTypeMapping` 的 java 类,我们可以用其配置映射 Jdbc 驱动的数据类型为自定义的
|
MyBatis-Flex 内置了一个名为:`JdbcTypeMapping` 的 java 类,我们可以用其配置映射 Jdbc 驱动的数据类型为自定义的
|
||||||
数据类型,在开始生成代码之前,可以先调用其进行配置,例如:
|
数据类型,在开始生成代码之前,可以先调用其进行配置,例如:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>parent</artifactId>
|
<artifactId>parent</artifactId>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>parent</artifactId>
|
<artifactId>parent</artifactId>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@ -27,7 +27,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-spring</artifactId>
|
<artifactId>mybatis-flex-spring</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,6 @@ package com.mybatisflex.codegen.test;
|
|||||||
import com.mybatisflex.codegen.Generator;
|
import com.mybatisflex.codegen.Generator;
|
||||||
import com.mybatisflex.codegen.config.GlobalConfig;
|
import com.mybatisflex.codegen.config.GlobalConfig;
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>parent</artifactId>
|
<artifactId>parent</artifactId>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@ -22,13 +22,13 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-annotation</artifactId>
|
<artifactId>mybatis-flex-annotation</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-processor</artifactId>
|
<artifactId>mybatis-flex-processor</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@ -16,22 +16,28 @@
|
|||||||
package com.mybatisflex.core;
|
package com.mybatisflex.core;
|
||||||
|
|
||||||
import com.mybatisflex.core.exception.FlexExceptions;
|
import com.mybatisflex.core.exception.FlexExceptions;
|
||||||
|
import com.mybatisflex.core.field.FieldQuery;
|
||||||
|
import com.mybatisflex.core.field.FieldQueryBuilder;
|
||||||
|
import com.mybatisflex.core.mybatis.MappedStatementTypes;
|
||||||
import com.mybatisflex.core.paginate.Page;
|
import com.mybatisflex.core.paginate.Page;
|
||||||
import com.mybatisflex.core.provider.EntitySqlProvider;
|
import com.mybatisflex.core.provider.EntitySqlProvider;
|
||||||
import com.mybatisflex.core.query.QueryColumn;
|
import com.mybatisflex.core.query.*;
|
||||||
import com.mybatisflex.core.query.QueryCondition;
|
|
||||||
import com.mybatisflex.core.query.QueryWrapper;
|
|
||||||
import com.mybatisflex.core.query.CPI;
|
|
||||||
import com.mybatisflex.core.table.TableInfo;
|
import com.mybatisflex.core.table.TableInfo;
|
||||||
import com.mybatisflex.core.table.TableInfoFactory;
|
import com.mybatisflex.core.table.TableInfoFactory;
|
||||||
|
import com.mybatisflex.core.util.CollectionUtil;
|
||||||
|
import com.mybatisflex.core.util.ConvertUtil;
|
||||||
import com.mybatisflex.core.util.ObjectUtil;
|
import com.mybatisflex.core.util.ObjectUtil;
|
||||||
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
import org.apache.ibatis.annotations.*;
|
import org.apache.ibatis.annotations.*;
|
||||||
import org.apache.ibatis.builder.annotation.ProviderContext;
|
import org.apache.ibatis.builder.annotation.ProviderContext;
|
||||||
|
import org.apache.ibatis.reflection.MetaObject;
|
||||||
|
import org.apache.ibatis.reflection.SystemMetaObject;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.function.Consumer;
|
||||||
import java.util.Map;
|
|
||||||
|
import static com.mybatisflex.core.query.QueryMethods.count;
|
||||||
|
|
||||||
public interface BaseMapper<T> {
|
public interface BaseMapper<T> {
|
||||||
|
|
||||||
@ -41,8 +47,8 @@ public interface BaseMapper<T> {
|
|||||||
* @param entity 实体类
|
* @param entity 实体类
|
||||||
* @return 返回影响的行数
|
* @return 返回影响的行数
|
||||||
*/
|
*/
|
||||||
default int insert(T entity){
|
default int insert(T entity) {
|
||||||
return insert(entity,false);
|
return insert(entity, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -53,12 +59,11 @@ public interface BaseMapper<T> {
|
|||||||
* @param entity 实体类
|
* @param entity 实体类
|
||||||
* @return 返回影响的行数
|
* @return 返回影响的行数
|
||||||
*/
|
*/
|
||||||
default int insertSelective(T entity){
|
default int insertSelective(T entity) {
|
||||||
return insert(entity,true);
|
return insert(entity, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插入 entity 数据
|
* 插入 entity 数据
|
||||||
*
|
*
|
||||||
@ -70,7 +75,6 @@ public interface BaseMapper<T> {
|
|||||||
int insert(@Param(FlexConsts.ENTITY) T entity, @Param(FlexConsts.IGNORE_NULLS) boolean ignoreNulls);
|
int insert(@Param(FlexConsts.ENTITY) T entity, @Param(FlexConsts.IGNORE_NULLS) boolean ignoreNulls);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量插入 entity 数据,只会根据第一条数据来构建插入的字段内容
|
* 批量插入 entity 数据,只会根据第一条数据来构建插入的字段内容
|
||||||
*
|
*
|
||||||
@ -144,12 +148,12 @@ public interface BaseMapper<T> {
|
|||||||
/**
|
/**
|
||||||
* 根据多个 id 批量删除数据
|
* 根据多个 id 批量删除数据
|
||||||
*
|
*
|
||||||
* @param ids ids 列表
|
* @param ids ids 列表
|
||||||
* @param size 切分大小
|
* @param size 切分大小
|
||||||
* @return 返回影响的行数
|
* @return 返回影响的行数
|
||||||
* @see com.mybatisflex.core.provider.EntitySqlProvider#deleteBatchByIds(Map, ProviderContext)
|
* @see com.mybatisflex.core.provider.EntitySqlProvider#deleteBatchByIds(Map, ProviderContext)
|
||||||
*/
|
*/
|
||||||
default int deleteBatchByIds(@Param(FlexConsts.PRIMARY_VALUE) List<? extends Serializable> ids, int size) {
|
default int deleteBatchByIds(List<? extends Serializable> ids, int size) {
|
||||||
if (size <= 0) {
|
if (size <= 0) {
|
||||||
size = 1000;//默认1000
|
size = 1000;//默认1000
|
||||||
}
|
}
|
||||||
@ -263,7 +267,7 @@ public interface BaseMapper<T> {
|
|||||||
* @return 返回影响的行数
|
* @return 返回影响的行数
|
||||||
*/
|
*/
|
||||||
|
|
||||||
default int updateByQuery(@Param(FlexConsts.ENTITY) T entity, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper) {
|
default int updateByQuery(T entity, QueryWrapper queryWrapper) {
|
||||||
return updateByQuery(entity, true, queryWrapper);
|
return updateByQuery(entity, true, queryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,11 +322,29 @@ public interface BaseMapper<T> {
|
|||||||
* @param queryWrapper query 条件
|
* @param queryWrapper query 条件
|
||||||
* @return entity 数据
|
* @return entity 数据
|
||||||
*/
|
*/
|
||||||
default T selectOneByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper) {
|
default T selectOneByQuery(QueryWrapper queryWrapper) {
|
||||||
List<T> entities = selectListByQuery(queryWrapper.limit(1));
|
List<T> entities = selectListByQuery(queryWrapper.limit(1));
|
||||||
return (entities == null || entities.isEmpty()) ? null : entities.get(0);
|
return (entities == null || entities.isEmpty()) ? null : entities.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 queryWrapper 构建的条件来查询 1 条数据
|
||||||
|
*
|
||||||
|
* @param queryWrapper query 条件
|
||||||
|
* @param asType 接收类型
|
||||||
|
* @return 数据内容
|
||||||
|
*/
|
||||||
|
default <R> R selectOneByQueryAs(QueryWrapper queryWrapper, Class<R> asType) {
|
||||||
|
try {
|
||||||
|
MappedStatementTypes.setCurrentType(asType);
|
||||||
|
List<R> entities = selectListByQueryAs(queryWrapper.limit(1), asType);
|
||||||
|
return (entities == null || entities.isEmpty()) ? null : entities.get(0);
|
||||||
|
} finally {
|
||||||
|
MappedStatementTypes.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据多个主键来查询多条数据
|
* 根据多个主键来查询多条数据
|
||||||
*
|
*
|
||||||
@ -390,6 +412,133 @@ public interface BaseMapper<T> {
|
|||||||
List<T> selectListByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
List<T> selectListByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||||
|
|
||||||
|
|
||||||
|
default List<T> selectListByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper
|
||||||
|
, Consumer<FieldQueryBuilder<T>>... consumers) {
|
||||||
|
|
||||||
|
List<T> list = selectListByQuery(queryWrapper);
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
list.forEach(entity -> {
|
||||||
|
for (Consumer<FieldQueryBuilder<T>> consumer : consumers) {
|
||||||
|
FieldQueryBuilder<T> fieldQueryBuilder = new FieldQueryBuilder<>(entity);
|
||||||
|
consumer.accept(fieldQueryBuilder);
|
||||||
|
FieldQuery fieldQuery = fieldQueryBuilder.build();
|
||||||
|
QueryWrapper childQuery = fieldQuery.getQueryWrapper();
|
||||||
|
MetaObject entityMetaObject = SystemMetaObject.forObject(entity);
|
||||||
|
Class<?> setterType = entityMetaObject.getSetterType(fieldQuery.getField());
|
||||||
|
|
||||||
|
Class<?> mappingType = fieldQuery.getMappingType();
|
||||||
|
if (mappingType == null) {
|
||||||
|
if (setterType.isAssignableFrom(Collection.class)) {
|
||||||
|
throw new IllegalStateException("Mapping Type can not be null for query Many.");
|
||||||
|
} else if (setterType.isArray()) {
|
||||||
|
mappingType = setterType.getComponentType();
|
||||||
|
} else {
|
||||||
|
mappingType = setterType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object value;
|
||||||
|
try {
|
||||||
|
MappedStatementTypes.setCurrentType(mappingType);
|
||||||
|
if (setterType.isAssignableFrom(List.class)) {
|
||||||
|
value = selectListByQueryAs(childQuery, mappingType);
|
||||||
|
} else if (setterType.isAssignableFrom(Set.class)) {
|
||||||
|
value = selectListByQueryAs(childQuery, mappingType);
|
||||||
|
value = new HashSet<>((Collection<?>) value);
|
||||||
|
} else if (setterType.isArray()) {
|
||||||
|
value = selectListByQueryAs(childQuery, mappingType);
|
||||||
|
value = ((List<?>) value).toArray();
|
||||||
|
} else {
|
||||||
|
value = selectOneByQueryAs(childQuery, mappingType);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
MappedStatementTypes.clear();
|
||||||
|
}
|
||||||
|
entityMetaObject.setValue(fieldQuery.getField(), value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 query 来构建条件查询数据列表,要求返回的数据为 asType
|
||||||
|
* 这种场景一般用在 left join 时,有多出了 entity 本身的字段内容,可以转换为 dto、vo 等场景时
|
||||||
|
*
|
||||||
|
* @param queryWrapper 查询条件
|
||||||
|
* @param asType 接收数据类型
|
||||||
|
* @return 数据列表
|
||||||
|
*/
|
||||||
|
@SelectProvider(type = EntitySqlProvider.class, method = "selectListByQuery")
|
||||||
|
<R> List<R> selectListByQueryAs(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper, Class<R> asType);
|
||||||
|
|
||||||
|
|
||||||
|
default <R> List<R> selectListByQueryAs(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper, Class<R> asType
|
||||||
|
, Consumer<FieldQueryBuilder<R>>... consumers) {
|
||||||
|
List<R> list;
|
||||||
|
try {
|
||||||
|
MappedStatementTypes.setCurrentType(asType);
|
||||||
|
list = selectListByQueryAs(queryWrapper, asType);
|
||||||
|
} finally {
|
||||||
|
MappedStatementTypes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
list.forEach(entity -> {
|
||||||
|
for (Consumer<FieldQueryBuilder<R>> consumer : consumers) {
|
||||||
|
FieldQueryBuilder<R> fieldQueryBuilder = new FieldQueryBuilder<>(entity);
|
||||||
|
consumer.accept(fieldQueryBuilder);
|
||||||
|
FieldQuery fieldQuery = fieldQueryBuilder.build();
|
||||||
|
QueryWrapper childQuery = fieldQuery.getQueryWrapper();
|
||||||
|
|
||||||
|
MetaObject entityMetaObject = SystemMetaObject.forObject(entity);
|
||||||
|
Class<?> setterType = entityMetaObject.getSetterType(fieldQuery.getField());
|
||||||
|
|
||||||
|
Class<?> mappingType = fieldQuery.getMappingType();
|
||||||
|
if (mappingType == null) {
|
||||||
|
if (setterType.isAssignableFrom(Collection.class)) {
|
||||||
|
throw new IllegalStateException("Mapping Type can not be null for query Many.");
|
||||||
|
} else if (setterType.isArray()) {
|
||||||
|
mappingType = setterType.getComponentType();
|
||||||
|
} else {
|
||||||
|
mappingType = setterType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object value;
|
||||||
|
try {
|
||||||
|
MappedStatementTypes.setCurrentType(mappingType);
|
||||||
|
if (setterType.isAssignableFrom(List.class)) {
|
||||||
|
value = selectListByQueryAs(childQuery, mappingType);
|
||||||
|
} else if (setterType.isAssignableFrom(Set.class)) {
|
||||||
|
value = selectListByQueryAs(childQuery, mappingType);
|
||||||
|
value = new HashSet<>((Collection<?>) value);
|
||||||
|
} else if (setterType.isArray()) {
|
||||||
|
value = selectListByQueryAs(childQuery, mappingType);
|
||||||
|
value = ((List<?>) value).toArray();
|
||||||
|
} else {
|
||||||
|
value = selectOneByQueryAs(childQuery, mappingType);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
MappedStatementTypes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
entityMetaObject.setValue(fieldQuery.getField(), value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询全部数据
|
* 查询全部数据
|
||||||
*
|
*
|
||||||
@ -400,6 +549,76 @@ public interface BaseMapper<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 queryWrapper 1 条数据
|
||||||
|
* queryWrapper 执行的结果应该只有 1 列,例如 QueryWrapper.create().select(ACCOUNT.id).where...
|
||||||
|
*
|
||||||
|
* @param queryWrapper 查询包装器
|
||||||
|
* @return 数据量
|
||||||
|
*/
|
||||||
|
default Object selectObjectByQuery(QueryWrapper queryWrapper) {
|
||||||
|
List<Object> objects = selectObjectListByQuery(queryWrapper.limit(1));
|
||||||
|
return objects == null || objects.isEmpty() ? null : objects.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 queryWrapper 来查询数据列表
|
||||||
|
* queryWrapper 执行的结果应该只有 1 列,例如 QueryWrapper.create().select(ACCOUNT.id).where...
|
||||||
|
*
|
||||||
|
* @param queryWrapper 查询包装器
|
||||||
|
* @return 数据列表
|
||||||
|
* @see EntitySqlProvider#selectObjectByQuery(Map, ProviderContext)
|
||||||
|
*/
|
||||||
|
@SelectProvider(type = EntitySqlProvider.class, method = "selectObjectByQuery")
|
||||||
|
List<Object> selectObjectListByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对 {@link #selectObjectListByQuery(QueryWrapper)} 进行数据转换
|
||||||
|
* 根据 queryWrapper 来查询数据列表
|
||||||
|
* queryWrapper 执行的结果应该只有 1 列,例如 QueryWrapper.create().select(ACCOUNT.id).where...
|
||||||
|
*
|
||||||
|
* @param queryWrapper 查询包装器
|
||||||
|
* @param asType 转换成的数据类型
|
||||||
|
* @return 数据列表
|
||||||
|
*/
|
||||||
|
default <R> List<R> selectObjectListByQueryAs(QueryWrapper queryWrapper, Class<R> asType) {
|
||||||
|
List<Object> queryResults = selectObjectListByQuery(queryWrapper);
|
||||||
|
if (queryResults == null || queryResults.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<R> results = new ArrayList<>();
|
||||||
|
for (Object queryResult : queryResults) {
|
||||||
|
results.add((R) ConvertUtil.convert(queryResult, asType));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询数据量
|
||||||
|
*
|
||||||
|
* @param queryWrapper 查询包装器
|
||||||
|
* @return 数据量
|
||||||
|
*/
|
||||||
|
default long selectCountByQuery(QueryWrapper queryWrapper) {
|
||||||
|
List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
|
||||||
|
if (CollectionUtil.isEmpty(selectColumns)) {
|
||||||
|
queryWrapper.select(count());
|
||||||
|
}
|
||||||
|
List<Object> objects = selectObjectListByQuery(queryWrapper);
|
||||||
|
Object object = objects == null || objects.isEmpty() ? null : objects.get(0);
|
||||||
|
if (object == null) {
|
||||||
|
return 0;
|
||||||
|
} else if (object instanceof Number) {
|
||||||
|
return ((Number) object).longValue();
|
||||||
|
} else {
|
||||||
|
throw FlexExceptions.wrap("selectCountByQuery error, Can not get number value for queryWrapper: %s", queryWrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据条件查询数据总量
|
* 根据条件查询数据总量
|
||||||
*
|
*
|
||||||
@ -411,17 +630,6 @@ public interface BaseMapper<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据 queryWrapper 来查询数据量
|
|
||||||
*
|
|
||||||
* @param queryWrapper 查询包装器
|
|
||||||
* @return 数据量
|
|
||||||
* @see EntitySqlProvider#selectCountByQuery(Map, ProviderContext)
|
|
||||||
*/
|
|
||||||
@SelectProvider(type = EntitySqlProvider.class, method = "selectCountByQuery")
|
|
||||||
long selectCountByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询
|
* 分页查询
|
||||||
*
|
*
|
||||||
@ -486,17 +694,63 @@ public interface BaseMapper<T> {
|
|||||||
* @param queryWrapper 查询条件
|
* @param queryWrapper 查询条件
|
||||||
* @return page 数据
|
* @return page 数据
|
||||||
*/
|
*/
|
||||||
default Page<T> paginate(@Param("page") Page<T> page, @Param("query") QueryWrapper queryWrapper) {
|
default Page<T> paginate(Page<T> page, QueryWrapper queryWrapper) {
|
||||||
|
return paginateAs(page, queryWrapper, null);
|
||||||
|
}
|
||||||
|
|
||||||
List<QueryColumn> groupByColumns = null;
|
|
||||||
|
|
||||||
|
default <R> Page<R> paginateAs(Page<R> page, QueryWrapper queryWrapper, Class<R> asType) {
|
||||||
|
List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
|
||||||
|
List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
|
||||||
|
|
||||||
|
List<Join> joins = CPI.getJoins(queryWrapper);
|
||||||
|
boolean removedJoins = true;
|
||||||
// 只有 totalRow 小于 0 的时候才会去查询总量
|
// 只有 totalRow 小于 0 的时候才会去查询总量
|
||||||
// 这样方便用户做总数缓存,而非每次都要去查询总量
|
// 这样方便用户做总数缓存,而非每次都要去查询总量
|
||||||
// 一般的分页场景中,只有第一页的时候有必要去查询总量,第二页以后是不需要的
|
// 一般的分页场景中,只有第一页的时候有必要去查询总量,第二页以后是不需要的
|
||||||
if (page.getTotalRow() < 0) {
|
if (page.getTotalRow() < 0) {
|
||||||
groupByColumns = CPI.getGroupByColumns(queryWrapper);
|
|
||||||
//清除group by 去查询数据
|
//移除 select
|
||||||
CPI.setGroupByColumns(queryWrapper, null);
|
CPI.setSelectColumns(queryWrapper, Collections.singletonList(count().as("total")));
|
||||||
|
|
||||||
|
//移除 OrderBy
|
||||||
|
if (CollectionUtil.isNotEmpty(orderBys)) {
|
||||||
|
CPI.setOrderBys(queryWrapper, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//移除 left join
|
||||||
|
if (joins != null && !joins.isEmpty()) {
|
||||||
|
for (Join join : joins) {
|
||||||
|
if (!Join.TYPE_LEFT.equals(CPI.getJoinType(join))) {
|
||||||
|
removedJoins = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
removedJoins = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedJoins) {
|
||||||
|
List<String> joinTables = new ArrayList<>();
|
||||||
|
joins.forEach(join -> {
|
||||||
|
QueryTable joinQueryTable = CPI.getJoinQueryTable(join);
|
||||||
|
if (joinQueryTable != null && StringUtil.isNotBlank(joinQueryTable.getName())) {
|
||||||
|
joinTables.add(joinQueryTable.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QueryCondition where = CPI.getWhereQueryCondition(queryWrapper);
|
||||||
|
if (CPI.containsTable(where, CollectionUtil.toArrayString(joinTables))) {
|
||||||
|
removedJoins = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedJoins) {
|
||||||
|
CPI.setJoins(queryWrapper, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
long count = selectCountByQuery(queryWrapper);
|
long count = selectCountByQuery(queryWrapper);
|
||||||
page.setTotalRow(count);
|
page.setTotalRow(count);
|
||||||
}
|
}
|
||||||
@ -505,15 +759,36 @@ public interface BaseMapper<T> {
|
|||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
//恢复数量查询清除的 groupBy
|
//重置 selectColumns
|
||||||
if (groupByColumns != null) {
|
CPI.setSelectColumns(queryWrapper, selectColumns);
|
||||||
CPI.setGroupByColumns(queryWrapper, groupByColumns);
|
|
||||||
|
//重置 orderBys
|
||||||
|
if (CollectionUtil.isNotEmpty(orderBys)) {
|
||||||
|
CPI.setOrderBys(queryWrapper, orderBys);
|
||||||
|
}
|
||||||
|
|
||||||
|
//重置 join
|
||||||
|
if (removedJoins) {
|
||||||
|
CPI.setJoins(queryWrapper, joins);
|
||||||
}
|
}
|
||||||
|
|
||||||
int offset = page.getPageSize() * (page.getPageNumber() - 1);
|
int offset = page.getPageSize() * (page.getPageNumber() - 1);
|
||||||
queryWrapper.limit(offset, page.getPageSize());
|
queryWrapper.limit(offset, page.getPageSize());
|
||||||
List<T> rows = selectListByQuery(queryWrapper);
|
|
||||||
page.setRecords(rows);
|
if (asType != null) {
|
||||||
|
try {
|
||||||
|
// 调用内部方法,不走代理,需要主动设置 MappedStatementType
|
||||||
|
// fixed https://gitee.com/mybatis-flex/mybatis-flex/issues/I73BP6
|
||||||
|
MappedStatementTypes.setCurrentType(asType);
|
||||||
|
List<R> records = selectListByQueryAs(queryWrapper, asType);
|
||||||
|
page.setRecords(records);
|
||||||
|
} finally {
|
||||||
|
MappedStatementTypes.clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
List<R> records = (List<R>) selectListByQuery(queryWrapper);
|
||||||
|
page.setRecords(records);
|
||||||
|
}
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ package com.mybatisflex.core;
|
|||||||
*/
|
*/
|
||||||
public class FlexConsts {
|
public class FlexConsts {
|
||||||
public static final String NAME = "MyBatis-Flex";
|
public static final String NAME = "MyBatis-Flex";
|
||||||
public static final String VERSION = "1.2.3";
|
public static final String VERSION = "1.3.0";
|
||||||
|
|
||||||
public static final String DEFAULT_PRIMARY_FIELD = "id";
|
public static final String DEFAULT_PRIMARY_FIELD = "id";
|
||||||
|
|
||||||
@ -39,6 +39,7 @@ public class FlexConsts {
|
|||||||
public static final String IGNORE_NULLS = "$$ignoreNulls";
|
public static final String IGNORE_NULLS = "$$ignoreNulls";
|
||||||
|
|
||||||
public static final String METHOD_INSERT_BATCH = "insertBatch";
|
public static final String METHOD_INSERT_BATCH = "insertBatch";
|
||||||
|
public static final String METHOD_SELECT_LIST_BY_QUERY_AS = "selectListByQueryAs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当 entity 使用逻辑删除时,0 为 entity 的正常状态
|
* 当 entity 使用逻辑删除时,0 为 entity 的正常状态
|
||||||
|
|||||||
@ -54,10 +54,6 @@ public enum DbType {
|
|||||||
* POSTGRE
|
* POSTGRE
|
||||||
*/
|
*/
|
||||||
POSTGRE_SQL("postgresql", "PostgreSQL 数据库"),
|
POSTGRE_SQL("postgresql", "PostgreSQL 数据库"),
|
||||||
/**
|
|
||||||
* SQLSERVER 2005
|
|
||||||
*/
|
|
||||||
SQLSERVER_2005("sqlserver2005", "SQLServer2005 数据库"),
|
|
||||||
/**
|
/**
|
||||||
* SQLSERVER
|
* SQLSERVER
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -87,7 +87,7 @@ public class DbTypeUtil {
|
|||||||
* @param jdbcUrl jdbcURL
|
* @param jdbcUrl jdbcURL
|
||||||
* @return 返回数据库类型
|
* @return 返回数据库类型
|
||||||
*/
|
*/
|
||||||
private static DbType parseDbType(String jdbcUrl) {
|
public static DbType parseDbType(String jdbcUrl) {
|
||||||
jdbcUrl = jdbcUrl.toLowerCase();
|
jdbcUrl = jdbcUrl.toLowerCase();
|
||||||
if (jdbcUrl.contains(":mysql:") || jdbcUrl.contains(":cobar:")) {
|
if (jdbcUrl.contains(":mysql:") || jdbcUrl.contains(":cobar:")) {
|
||||||
return DbType.MYSQL;
|
return DbType.MYSQL;
|
||||||
@ -95,9 +95,7 @@ public class DbTypeUtil {
|
|||||||
return DbType.MARIADB;
|
return DbType.MARIADB;
|
||||||
} else if (jdbcUrl.contains(":oracle:")) {
|
} else if (jdbcUrl.contains(":oracle:")) {
|
||||||
return DbType.ORACLE;
|
return DbType.ORACLE;
|
||||||
} else if (jdbcUrl.contains(":sqlserver:") || jdbcUrl.contains(":microsoft:")) {
|
} else if (jdbcUrl.contains(":sqlserver:") || jdbcUrl.contains(":microsoft:") || jdbcUrl.contains(":sqlserver2012:")) {
|
||||||
return DbType.SQLSERVER_2005;
|
|
||||||
} else if (jdbcUrl.contains(":sqlserver2012:")) {
|
|
||||||
return DbType.SQLSERVER;
|
return DbType.SQLSERVER;
|
||||||
} else if (jdbcUrl.contains(":postgresql:")) {
|
} else if (jdbcUrl.contains(":postgresql:")) {
|
||||||
return DbType.POSTGRE_SQL;
|
return DbType.POSTGRE_SQL;
|
||||||
|
|||||||
@ -106,7 +106,7 @@ public class DialectFactory {
|
|||||||
case CSIIDB:
|
case CSIIDB:
|
||||||
return new CommonsDialectImpl(KeywordWrap.BACKQUOTE, LimitOffsetProcessor.MYSQL);
|
return new CommonsDialectImpl(KeywordWrap.BACKQUOTE, LimitOffsetProcessor.MYSQL);
|
||||||
case ORACLE:
|
case ORACLE:
|
||||||
return new OracleDialect(LimitOffsetProcessor.ORACLE);
|
return new OracleDialect();
|
||||||
case DM:
|
case DM:
|
||||||
case GAUSS:
|
case GAUSS:
|
||||||
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.ORACLE);
|
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.ORACLE);
|
||||||
@ -121,21 +121,19 @@ public class DialectFactory {
|
|||||||
case VERTICA:
|
case VERTICA:
|
||||||
case REDSHIFT:
|
case REDSHIFT:
|
||||||
case OPENGAUSS:
|
case OPENGAUSS:
|
||||||
case TDENGINE:
|
|
||||||
case UXDB:
|
case UXDB:
|
||||||
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.POSTGRESQL);
|
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.POSTGRESQL);
|
||||||
|
case TDENGINE:
|
||||||
|
return new CommonsDialectImpl(KeywordWrap.BACKQUOTE, LimitOffsetProcessor.POSTGRESQL);
|
||||||
case ORACLE_12C:
|
case ORACLE_12C:
|
||||||
return new OracleDialect(LimitOffsetProcessor.DERBY);
|
return new OracleDialect(LimitOffsetProcessor.DERBY);
|
||||||
case FIREBIRD:
|
case FIREBIRD:
|
||||||
|
case DB2:
|
||||||
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.DERBY);
|
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.DERBY);
|
||||||
case SQLSERVER:
|
case SQLSERVER:
|
||||||
return new CommonsDialectImpl(KeywordWrap.SQUARE_BRACKETS, LimitOffsetProcessor.DERBY);
|
return new CommonsDialectImpl(KeywordWrap.SQUARE_BRACKETS, LimitOffsetProcessor.SQLSERVER);
|
||||||
case SQLSERVER_2005:
|
|
||||||
return new CommonsDialectImpl(KeywordWrap.SQUARE_BRACKETS, LimitOffsetProcessor.DB2);
|
|
||||||
case INFORMIX:
|
case INFORMIX:
|
||||||
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.INFORMIX);
|
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.INFORMIX);
|
||||||
case DB2:
|
|
||||||
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.DB2);
|
|
||||||
case SYBASE:
|
case SYBASE:
|
||||||
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.SYBASE);
|
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.SYBASE);
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -25,6 +25,8 @@ public interface IDialect {
|
|||||||
|
|
||||||
String wrap(String keyword);
|
String wrap(String keyword);
|
||||||
|
|
||||||
|
String forHint(String hintString);
|
||||||
|
|
||||||
String forInsertRow(String tableName, Row row);
|
String forInsertRow(String tableName, Row row);
|
||||||
|
|
||||||
String forInsertBatchWithFirstRowColumns(String tableName, List<Row> rows);
|
String forInsertBatchWithFirstRowColumns(String tableName, List<Row> rows);
|
||||||
@ -43,15 +45,10 @@ public interface IDialect {
|
|||||||
|
|
||||||
String forSelectOneById(String tableName, String[] primaryKeys, Object[] primaryValues);
|
String forSelectOneById(String tableName, String[] primaryKeys, Object[] primaryValues);
|
||||||
|
|
||||||
String forSelectListByQuery(QueryWrapper queryWrapper);
|
String forSelectByQuery(QueryWrapper queryWrapper);
|
||||||
|
|
||||||
String forSelectCountByQuery(QueryWrapper queryWrapper);
|
|
||||||
|
|
||||||
|
|
||||||
String buildSelectSql(QueryWrapper queryWrapper);
|
String buildSelectSql(QueryWrapper queryWrapper);
|
||||||
|
|
||||||
String buildSelectCountSql(QueryWrapper queryWrapper);
|
|
||||||
|
|
||||||
String buildDeleteSql(QueryWrapper queryWrapper);
|
String buildDeleteSql(QueryWrapper queryWrapper);
|
||||||
|
|
||||||
String buildWhereConditionSql(QueryWrapper queryWrapper);
|
String buildWhereConditionSql(QueryWrapper queryWrapper);
|
||||||
|
|||||||
@ -17,6 +17,10 @@ package com.mybatisflex.core.dialect;
|
|||||||
|
|
||||||
import com.mybatisflex.core.util.StringUtil;
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于对数据库的关键字包装
|
* 用于对数据库的关键字包装
|
||||||
*/
|
*/
|
||||||
@ -47,6 +51,16 @@ public class KeywordWrap {
|
|||||||
*/
|
*/
|
||||||
public final static KeywordWrap SQUARE_BRACKETS = new KeywordWrap("[", "]");
|
public final static KeywordWrap SQUARE_BRACKETS = new KeywordWrap("[", "]");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 大小写敏感
|
||||||
|
*/
|
||||||
|
private boolean caseSensitive = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库关键字
|
||||||
|
*/
|
||||||
|
private Set<String> keywords = Collections.emptySet();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 前缀
|
* 前缀
|
||||||
*/
|
*/
|
||||||
@ -59,12 +73,28 @@ public class KeywordWrap {
|
|||||||
|
|
||||||
|
|
||||||
public KeywordWrap(String prefix, String suffix) {
|
public KeywordWrap(String prefix, String suffix) {
|
||||||
|
this(false, Collections.emptySet(), prefix, suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeywordWrap(Set<String> keywords, String prefix, String suffix) {
|
||||||
|
this(false, keywords, prefix, suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeywordWrap(boolean caseSensitive, Set<String> keywords, String prefix, String suffix) {
|
||||||
|
this.caseSensitive = caseSensitive;
|
||||||
|
this.keywords = keywords;
|
||||||
this.prefix = prefix;
|
this.prefix = prefix;
|
||||||
this.suffix = suffix;
|
this.suffix = suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String wrap(String keyword) {
|
public String wrap(String keyword) {
|
||||||
return StringUtil.isBlank(keyword) ? "" : prefix + keyword + suffix;
|
if (StringUtil.isBlank(keyword) || "*".equals(keyword)) {
|
||||||
|
return keyword;
|
||||||
|
}
|
||||||
|
if (caseSensitive || keywords.isEmpty() || keywords.contains(keyword.toUpperCase(Locale.ENGLISH))) {
|
||||||
|
return prefix + keyword + suffix;
|
||||||
|
}
|
||||||
|
return keyword;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.mybatisflex.core.dialect;
|
package com.mybatisflex.core.dialect;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.query.CPI;
|
||||||
|
import com.mybatisflex.core.query.QueryOrderBy;
|
||||||
import com.mybatisflex.core.query.QueryWrapper;
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
|
import com.mybatisflex.core.util.CollectionUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* limit 和 offset 参数的处理器
|
* limit 和 offset 参数的处理器
|
||||||
@ -57,29 +62,33 @@ public interface LimitOffsetProcessor {
|
|||||||
LimitOffsetProcessor DERBY = (sql, queryWrapper, limitRows, limitOffset) -> {
|
LimitOffsetProcessor DERBY = (sql, queryWrapper, limitRows, limitOffset) -> {
|
||||||
if (limitRows != null && limitOffset != null) {
|
if (limitRows != null && limitOffset != null) {
|
||||||
// OFFSET ** ROWS FETCH NEXT ** ROWS ONLY")
|
// OFFSET ** ROWS FETCH NEXT ** ROWS ONLY")
|
||||||
sql.append(" OFFSET ").append(limitOffset).append(" ROWS FETCH NEXT ").append(limitRows).append(" ROWS ONLY");
|
sql.append(" OFFSET ").append(limitOffset).append(" ROWS FETCH NEXT ").append(limitRows).append(" ROWS ONLY");
|
||||||
} else if (limitRows != null) {
|
} else if (limitRows != null) {
|
||||||
// FETCH FIRST 20 ROWS ONLY
|
sql.append(" OFFSET 0 ROWS FETCH NEXT ").append(limitRows).append(" ROWS ONLY");
|
||||||
sql.append(" FETCH FIRST ").append(limitRows).append(" ROWS ONLY");
|
|
||||||
}
|
}
|
||||||
return sql;
|
return sql;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* db2 的处理器
|
* derby 的处理器
|
||||||
* 适合 {@link DbType#DB2,DbType#SQLSERVER_2005}
|
* 适合 {@link DbType#DERBY,DbType#ORACLE_12C,DbType#SQLSERVER ,DbType#POSTGRE_SQL}
|
||||||
*/
|
*/
|
||||||
LimitOffsetProcessor DB2 = (sql, queryWrapper, limitRows, limitOffset) -> {
|
LimitOffsetProcessor SQLSERVER = (sql, queryWrapper, limitRows, limitOffset) -> {
|
||||||
if (limitRows != null && limitOffset != null) {
|
if (limitRows != null && limitOffset != null) {
|
||||||
// OFFSET ** ROWS FETCH NEXT ** ROWS ONLY")
|
// OFFSET ** ROWS FETCH NEXT ** ROWS ONLY")
|
||||||
sql.append(" OFFSET ").append(limitOffset).append(" ROWS FETCH NEXT ").append(limitRows).append(" ROWS ONLY");
|
sql.append(" OFFSET ").append(limitOffset).append(" ROWS FETCH NEXT ").append(limitRows).append(" ROWS ONLY");
|
||||||
} else if (limitRows != null) {
|
} else if (limitRows != null) {
|
||||||
// FETCH FIRST 20 ROWS ONLY
|
List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
|
||||||
sql.append(" FETCH FIRST ").append(limitRows).append(" ROWS ONLY");
|
if (CollectionUtil.isNotEmpty(orderBys)) {
|
||||||
|
sql.append(" OFFSET 0 ROWS FETCH NEXT ").append(limitRows).append(" ROWS ONLY");
|
||||||
|
} else {
|
||||||
|
sql.insert(6, " TOP " + limitRows);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return sql;
|
return sql;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Informix 的处理器
|
* Informix 的处理器
|
||||||
* 适合 {@link DbType#INFORMIX}
|
* 适合 {@link DbType#INFORMIX}
|
||||||
|
|||||||
@ -56,7 +56,12 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String wrap(String keyword) {
|
public String wrap(String keyword) {
|
||||||
return keywordWrap.wrap(keyword);
|
return "*".equals(keyword) ? keyword : keywordWrap.wrap(keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String forHint(String hintString) {
|
||||||
|
return StringUtil.isNotBlank(hintString) ? "/*+ " + hintString + " */ " : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -255,17 +260,11 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String forSelectListByQuery(QueryWrapper queryWrapper) {
|
public String forSelectByQuery(QueryWrapper queryWrapper) {
|
||||||
return buildSelectSql(queryWrapper);
|
return buildSelectSql(queryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String forSelectCountByQuery(QueryWrapper queryWrapper) {
|
|
||||||
return buildSelectCountSql(queryWrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////build query sql///////
|
////////////build query sql///////
|
||||||
@Override
|
@Override
|
||||||
public String buildSelectSql(QueryWrapper queryWrapper) {
|
public String buildSelectSql(QueryWrapper queryWrapper) {
|
||||||
@ -275,7 +274,7 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
|
|
||||||
List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
|
List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
|
||||||
|
|
||||||
StringBuilder sqlBuilder = buildSelectColumnSql(allTables, selectColumns);
|
StringBuilder sqlBuilder = buildSelectColumnSql(allTables, selectColumns, CPI.getHint(queryWrapper));
|
||||||
sqlBuilder.append(" FROM ").append(StringUtil.join(", ", queryTables, queryTable -> queryTable.toSql(this)));
|
sqlBuilder.append(" FROM ").append(StringUtil.join(", ", queryTables, queryTable -> queryTable.toSql(this)));
|
||||||
|
|
||||||
buildJoinSql(sqlBuilder, queryWrapper, allTables);
|
buildJoinSql(sqlBuilder, queryWrapper, allTables);
|
||||||
@ -298,11 +297,19 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset);
|
sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> endFragments = CPI.getEndFragments(queryWrapper);
|
||||||
|
if (CollectionUtil.isNotEmpty(endFragments)) {
|
||||||
|
for (String endFragment : endFragments) {
|
||||||
|
sqlBuilder.append(" ").append(endFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sqlBuilder.toString();
|
return sqlBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private StringBuilder buildSelectColumnSql(List<QueryTable> queryTables, List<QueryColumn> selectColumns) {
|
private StringBuilder buildSelectColumnSql(List<QueryTable> queryTables, List<QueryColumn> selectColumns, String hint) {
|
||||||
StringBuilder sqlBuilder = new StringBuilder("SELECT ");
|
StringBuilder sqlBuilder = new StringBuilder("SELECT ");
|
||||||
|
sqlBuilder.append(forHint(hint));
|
||||||
if (selectColumns == null || selectColumns.isEmpty()) {
|
if (selectColumns == null || selectColumns.isEmpty()) {
|
||||||
sqlBuilder.append("*");
|
sqlBuilder.append("*");
|
||||||
} else {
|
} else {
|
||||||
@ -320,29 +327,6 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String buildSelectCountSql(QueryWrapper queryWrapper) {
|
|
||||||
List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
|
|
||||||
List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
|
|
||||||
List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
|
|
||||||
|
|
||||||
//ignore selectColumns
|
|
||||||
StringBuilder sqlBuilder = new StringBuilder("SELECT COUNT(*) FROM ");
|
|
||||||
sqlBuilder.append(StringUtil.join(", ", queryTables, queryTable -> queryTable.toSql(this)));
|
|
||||||
|
|
||||||
|
|
||||||
buildJoinSql(sqlBuilder, queryWrapper, allTables);
|
|
||||||
buildWhereSql(sqlBuilder, queryWrapper, allTables, true);
|
|
||||||
buildGroupBySql(sqlBuilder, queryWrapper, allTables);
|
|
||||||
buildHavingSql(sqlBuilder, queryWrapper, allTables);
|
|
||||||
|
|
||||||
// ignore orderBy and limit
|
|
||||||
// buildOrderBySql(sqlBuilder, queryWrapper);
|
|
||||||
// buildLimitSql(sqlBuilder, queryWrapper);
|
|
||||||
|
|
||||||
return sqlBuilder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String buildDeleteSql(QueryWrapper queryWrapper) {
|
public String buildDeleteSql(QueryWrapper queryWrapper) {
|
||||||
List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
|
List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
|
||||||
@ -350,7 +334,7 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
|
List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
|
||||||
|
|
||||||
//ignore selectColumns
|
//ignore selectColumns
|
||||||
StringBuilder sqlBuilder = new StringBuilder("DELETE FROM ");
|
StringBuilder sqlBuilder = new StringBuilder("DELETE " + forHint(CPI.getHint(queryWrapper)) + "FROM ");
|
||||||
sqlBuilder.append(StringUtil.join(", ", queryTables, queryTable -> queryTable.toSql(this)));
|
sqlBuilder.append(StringUtil.join(", ", queryTables, queryTable -> queryTable.toSql(this)));
|
||||||
|
|
||||||
buildJoinSql(sqlBuilder, queryWrapper, allTables);
|
buildJoinSql(sqlBuilder, queryWrapper, allTables);
|
||||||
@ -362,6 +346,13 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
//buildOrderBySql(sqlBuilder, queryWrapper);
|
//buildOrderBySql(sqlBuilder, queryWrapper);
|
||||||
//buildLimitSql(sqlBuilder, queryWrapper);
|
//buildLimitSql(sqlBuilder, queryWrapper);
|
||||||
|
|
||||||
|
List<String> endFragments = CPI.getEndFragments(queryWrapper);
|
||||||
|
if (CollectionUtil.isNotEmpty(endFragments)) {
|
||||||
|
for (String endFragment : endFragments) {
|
||||||
|
sqlBuilder.append(" ").append(endFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sqlBuilder.toString();
|
return sqlBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,7 +536,7 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
|
List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
|
||||||
|
|
||||||
//ignore selectColumns
|
//ignore selectColumns
|
||||||
StringBuilder sqlBuilder = new StringBuilder("UPDATE ");
|
StringBuilder sqlBuilder = new StringBuilder("UPDATE ").append(forHint(CPI.getHint(queryWrapper)));
|
||||||
sqlBuilder.append(wrap(tableInfo.getTableName()));
|
sqlBuilder.append(wrap(tableInfo.getTableName()));
|
||||||
sqlBuilder.append(" SET ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicDeletedValue());
|
sqlBuilder.append(" SET ").append(wrap(logicDeleteColumn)).append(" = ").append(getLogicDeletedValue());
|
||||||
|
|
||||||
@ -635,7 +626,7 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
|
|
||||||
Set<String> modifyAttrs = tableInfo.obtainUpdateColumns(entity, ignoreNulls, true);
|
Set<String> modifyAttrs = tableInfo.obtainUpdateColumns(entity, ignoreNulls, true);
|
||||||
|
|
||||||
sql.append("UPDATE ").append(wrap(tableInfo.getTableName())).append(" SET ");
|
sql.append("UPDATE ").append(forHint(CPI.getHint(queryWrapper))).append(wrap(tableInfo.getTableName())).append(" SET ");
|
||||||
|
|
||||||
StringJoiner stringJoiner = new StringJoiner(", ");
|
StringJoiner stringJoiner = new StringJoiner(", ");
|
||||||
|
|
||||||
@ -665,12 +656,20 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sql.append(" WHERE ").append(whereConditionSql);
|
sql.append(" WHERE ").append(whereConditionSql);
|
||||||
|
|
||||||
|
List<String> endFragments = CPI.getEndFragments(queryWrapper);
|
||||||
|
if (CollectionUtil.isNotEmpty(endFragments)) {
|
||||||
|
for (String endFragment : endFragments) {
|
||||||
|
sql.append(" ").append(endFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sql.toString();
|
return sql.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String forSelectOneEntityById(TableInfo tableInfo) {
|
public String forSelectOneEntityById(TableInfo tableInfo) {
|
||||||
StringBuilder sql = buildSelectColumnSql(null, tableInfo.getDefaultQueryColumn());
|
StringBuilder sql = buildSelectColumnSql(null, null, null);
|
||||||
sql.append(" FROM ").append(wrap(tableInfo.getTableName()));
|
sql.append(" FROM ").append(wrap(tableInfo.getTableName()));
|
||||||
sql.append(" WHERE ");
|
sql.append(" WHERE ");
|
||||||
String[] pKeys = tableInfo.getPrimaryKeys();
|
String[] pKeys = tableInfo.getPrimaryKeys();
|
||||||
@ -699,7 +698,7 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String forSelectEntityListByIds(TableInfo tableInfo, Object[] primaryValues) {
|
public String forSelectEntityListByIds(TableInfo tableInfo, Object[] primaryValues) {
|
||||||
StringBuilder sql = buildSelectColumnSql(null, tableInfo.getDefaultQueryColumn());
|
StringBuilder sql = buildSelectColumnSql(null, tableInfo.getDefaultQueryColumn(), null);
|
||||||
sql.append(" FROM ").append(wrap(tableInfo.getTableName()));
|
sql.append(" FROM ").append(wrap(tableInfo.getTableName()));
|
||||||
sql.append(" WHERE ");
|
sql.append(" WHERE ");
|
||||||
String[] primaryKeys = tableInfo.getPrimaryKeys();
|
String[] primaryKeys = tableInfo.getPrimaryKeys();
|
||||||
@ -845,7 +844,8 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
|
|
||||||
protected Object getLogicNormalValue() {
|
protected Object getLogicNormalValue() {
|
||||||
Object normalValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete();
|
Object normalValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete();
|
||||||
if (normalValueOfLogicDelete instanceof Number) {
|
if (normalValueOfLogicDelete instanceof Number
|
||||||
|
|| normalValueOfLogicDelete instanceof Boolean) {
|
||||||
return normalValueOfLogicDelete;
|
return normalValueOfLogicDelete;
|
||||||
}
|
}
|
||||||
return "\"" + normalValueOfLogicDelete.toString() + "\"";
|
return "\"" + normalValueOfLogicDelete.toString() + "\"";
|
||||||
@ -854,7 +854,8 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
|
|
||||||
protected Object getLogicDeletedValue() {
|
protected Object getLogicDeletedValue() {
|
||||||
Object deletedValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getDeletedValueOfLogicDelete();
|
Object deletedValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getDeletedValueOfLogicDelete();
|
||||||
if (deletedValueOfLogicDelete instanceof Number) {
|
if (deletedValueOfLogicDelete instanceof Number
|
||||||
|
|| deletedValueOfLogicDelete instanceof Boolean) {
|
||||||
return deletedValueOfLogicDelete;
|
return deletedValueOfLogicDelete;
|
||||||
}
|
}
|
||||||
return "\"" + deletedValueOfLogicDelete.toString() + "\"";
|
return "\"" + deletedValueOfLogicDelete.toString() + "\"";
|
||||||
|
|||||||
@ -18,14 +18,12 @@ package com.mybatisflex.core.dialect.impl;
|
|||||||
import com.mybatisflex.core.dialect.KeywordWrap;
|
import com.mybatisflex.core.dialect.KeywordWrap;
|
||||||
import com.mybatisflex.core.dialect.LimitOffsetProcessor;
|
import com.mybatisflex.core.dialect.LimitOffsetProcessor;
|
||||||
import com.mybatisflex.core.util.CollectionUtil;
|
import com.mybatisflex.core.util.CollectionUtil;
|
||||||
import com.mybatisflex.core.util.StringUtil;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Set;
|
||||||
|
|
||||||
public class OracleDialect extends CommonsDialectImpl {
|
public class OracleDialect extends CommonsDialectImpl {
|
||||||
|
|
||||||
private boolean caseSensitive;
|
private static final Set<String> keywords = CollectionUtil.newHashSet(
|
||||||
private final Set<String> keywords = CollectionUtil.newHashSet(
|
|
||||||
"ACCESS", "ADD", "ALL", "ALTER",
|
"ACCESS", "ADD", "ALL", "ALTER",
|
||||||
"AND", "ANY", "ARRAYLEN", "AS",
|
"AND", "ANY", "ARRAYLEN", "AS",
|
||||||
"ASC", "AUDIT", "BETWEEN", "BY",
|
"ASC", "AUDIT", "BETWEEN", "BY",
|
||||||
@ -55,26 +53,11 @@ public class OracleDialect extends CommonsDialectImpl {
|
|||||||
"VALIDATE", "VALUES", "VARCHAR", "VARCHAR2"
|
"VALIDATE", "VALUES", "VARCHAR", "VARCHAR2"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public OracleDialect() {
|
||||||
|
this(LimitOffsetProcessor.ORACLE);
|
||||||
|
}
|
||||||
public OracleDialect(LimitOffsetProcessor limitOffsetProcessor) {
|
public OracleDialect(LimitOffsetProcessor limitOffsetProcessor) {
|
||||||
super(KeywordWrap.NONE, limitOffsetProcessor);
|
super(new KeywordWrap(keywords,"\"","\""),limitOffsetProcessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCaseSensitive() {
|
|
||||||
return caseSensitive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCaseSensitive(boolean caseSensitive) {
|
|
||||||
this.caseSensitive = caseSensitive;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String wrap(String keyword) {
|
|
||||||
if (StringUtil.isBlank(keyword)) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
if (caseSensitive || keywords.contains(keyword.toUpperCase(Locale.ENGLISH))) {
|
|
||||||
return "\"" + keyword + "\"";
|
|
||||||
}
|
|
||||||
return keyword;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* 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.field;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class FieldQuery implements Serializable {
|
||||||
|
|
||||||
|
private String field;
|
||||||
|
private Class<?> mappingType;
|
||||||
|
private QueryWrapper queryWrapper;
|
||||||
|
|
||||||
|
public String getField() {
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setField(String field) {
|
||||||
|
this.field = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getMappingType() {
|
||||||
|
return mappingType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMappingType(Class<?> mappingType) {
|
||||||
|
this.mappingType = mappingType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryWrapper getQueryWrapper() {
|
||||||
|
return queryWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQueryWrapper(QueryWrapper queryWrapper) {
|
||||||
|
this.queryWrapper = queryWrapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* 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.field;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.util.LambdaGetter;
|
||||||
|
import com.mybatisflex.core.util.LambdaUtil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class FieldQueryBuilder<T> implements Serializable {
|
||||||
|
|
||||||
|
private T entity;
|
||||||
|
private FieldQuery fieldQuery = new FieldQuery();
|
||||||
|
|
||||||
|
public FieldQueryBuilder(T entity) {
|
||||||
|
this.entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldQueryBuilder<T> field(String field){
|
||||||
|
fieldQuery.setField(field);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldQueryBuilder<T> field(LambdaGetter<T> fn){
|
||||||
|
return field(LambdaUtil.getFieldName(fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldQueryBuilder<T> type(Class<?> mappingType){
|
||||||
|
fieldQuery.setMappingType(mappingType);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldQueryBuilder<T> queryWrapper(QueryBuilder<T> fun){
|
||||||
|
fieldQuery.setQueryWrapper(fun.build(entity));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldQuery build() {
|
||||||
|
return fieldQuery;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* 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.field;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
|
|
||||||
|
public interface QueryBuilder<T> {
|
||||||
|
QueryWrapper build(T entity);
|
||||||
|
}
|
||||||
@ -15,58 +15,27 @@
|
|||||||
*/
|
*/
|
||||||
package com.mybatisflex.core.handler;
|
package com.mybatisflex.core.handler;
|
||||||
|
|
||||||
import com.mybatisflex.annotation.EnumValue;
|
import com.mybatisflex.core.util.EnumWrapper;
|
||||||
import com.mybatisflex.core.exception.FlexExceptions;
|
|
||||||
import com.mybatisflex.core.util.ClassUtil;
|
|
||||||
import com.mybatisflex.core.util.StringUtil;
|
|
||||||
import org.apache.ibatis.type.BaseTypeHandler;
|
import org.apache.ibatis.type.BaseTypeHandler;
|
||||||
import org.apache.ibatis.type.JdbcType;
|
import org.apache.ibatis.type.JdbcType;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.sql.CallableStatement;
|
import java.sql.CallableStatement;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class FlexEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
|
public class FlexEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
|
||||||
|
|
||||||
private final Class<?> enumPropertyType;
|
private EnumWrapper<E> enumWrapper;
|
||||||
private final E[] enums;
|
|
||||||
private Field property;
|
|
||||||
private Method getter;
|
|
||||||
|
|
||||||
public FlexEnumTypeHandler(Class<E> enumClass) {
|
public FlexEnumTypeHandler(Class<E> enumClass) {
|
||||||
List<Field> allFields = ClassUtil.getAllFields(enumClass, field -> field.getAnnotation(EnumValue.class) != null);
|
enumWrapper = EnumWrapper.of(enumClass);
|
||||||
Field field = allFields.get(0);
|
|
||||||
|
|
||||||
String fieldGetterName = "get" + StringUtil.firstCharToUpperCase(field.getName());
|
|
||||||
List<Method> allMethods = ClassUtil.getAllMethods(enumClass, method -> {
|
|
||||||
String methodName = method.getName();
|
|
||||||
return methodName.equals(fieldGetterName);
|
|
||||||
});
|
|
||||||
|
|
||||||
enumPropertyType = ClassUtil.wrap(field.getType());
|
|
||||||
enums = enumClass.getEnumConstants();
|
|
||||||
|
|
||||||
if (allMethods.isEmpty()) {
|
|
||||||
if (Modifier.isPublic(field.getModifiers())) {
|
|
||||||
property = field;
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Can not find \"" + fieldGetterName + "()\" method in enum: " + enumClass.getName());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
getter = allMethods.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
|
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
|
||||||
Object value = getValue(parameter);
|
Object value = enumWrapper.getEnumValue(parameter);
|
||||||
if (jdbcType == null) {
|
if (jdbcType == null) {
|
||||||
ps.setObject(i, value);
|
ps.setObject(i, value);
|
||||||
} else {
|
} else {
|
||||||
@ -74,55 +43,34 @@ public class FlexEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Object getValue(E object) {
|
|
||||||
try {
|
|
||||||
return getter != null
|
|
||||||
? getter.invoke(object)
|
|
||||||
: property.get(object);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw FlexExceptions.wrap(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||||
Object value = rs.getObject(columnName, this.enumPropertyType);
|
Object value = rs.getObject(columnName, enumWrapper.getEnumPropertyType());
|
||||||
if (null == value && rs.wasNull()) {
|
if (null == value && rs.wasNull()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return convertToEnum(value);
|
return enumWrapper.toEnum(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||||
Object value = rs.getObject(columnIndex, this.enumPropertyType);
|
Object value = rs.getObject(columnIndex, enumWrapper.getEnumPropertyType());
|
||||||
if (null == value && rs.wasNull()) {
|
if (null == value && rs.wasNull()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return convertToEnum(value);
|
return enumWrapper.toEnum(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||||
Object value = cs.getObject(columnIndex, this.enumPropertyType);
|
Object value = cs.getObject(columnIndex, enumWrapper.getEnumPropertyType());
|
||||||
if (null == value && cs.wasNull()) {
|
if (null == value && cs.wasNull()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return convertToEnum(value);
|
return enumWrapper.toEnum(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private E convertToEnum(Object value) {
|
|
||||||
for (E e : enums) {
|
|
||||||
if (value.equals(getValue(e))) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,9 +30,9 @@ public class KeyGeneratorFactory {
|
|||||||
/** 内置了 uuid 的生成器,因此主键配置的时候可以直接配置为 @Id(keyType = KeyType.Generator, value = "uuid")
|
/** 内置了 uuid 的生成器,因此主键配置的时候可以直接配置为 @Id(keyType = KeyType.Generator, value = "uuid")
|
||||||
* {@link com.mybatisflex.annotation.Id}
|
* {@link com.mybatisflex.annotation.Id}
|
||||||
*/
|
*/
|
||||||
register("uuid", new UUIDKeyGenerator());
|
register(KeyGenerators.uuid, new UUIDKeyGenerator());
|
||||||
register("flexId", new FlexIDKeyGenerator());
|
register(KeyGenerators.flexId, new FlexIDKeyGenerator());
|
||||||
register("snowFlakeId", new SnowFlakeIDKeyGenerator());
|
register(KeyGenerators.snowFlakeId, new SnowFlakeIDKeyGenerator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* 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.keygen;
|
||||||
|
|
||||||
|
public class KeyGenerators {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uuid 主键生成器
|
||||||
|
* {@link com.mybatisflex.core.keygen.impl.UUIDKeyGenerator}
|
||||||
|
*/
|
||||||
|
public static final String uuid = "uuid";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* flexId 主键生成器
|
||||||
|
* {@link com.mybatisflex.core.keygen.impl.FlexIDKeyGenerator}
|
||||||
|
*/
|
||||||
|
public static final String flexId = "flexId";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 雪花算法主键生成器
|
||||||
|
* {@link com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator}
|
||||||
|
*/
|
||||||
|
public static final String snowFlakeId = "snowFlakeId";
|
||||||
|
}
|
||||||
@ -42,12 +42,15 @@ import org.apache.ibatis.mapping.MappedStatement;
|
|||||||
import org.apache.ibatis.mapping.ResultMap;
|
import org.apache.ibatis.mapping.ResultMap;
|
||||||
import org.apache.ibatis.session.*;
|
import org.apache.ibatis.session.*;
|
||||||
import org.apache.ibatis.transaction.Transaction;
|
import org.apache.ibatis.transaction.Transaction;
|
||||||
|
import org.apache.ibatis.util.MapUtil;
|
||||||
|
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class FlexConfiguration extends Configuration {
|
public class FlexConfiguration extends Configuration {
|
||||||
|
|
||||||
|
private static Map<Class<?>, MappedStatement> dynamicMappedStatementCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public FlexConfiguration(Environment environment) {
|
public FlexConfiguration(Environment environment) {
|
||||||
super(environment);
|
super(environment);
|
||||||
@ -126,6 +129,22 @@ public class FlexConfiguration extends Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MappedStatement getMappedStatement(String id) {
|
||||||
|
MappedStatement ms = super.getMappedStatement(id);
|
||||||
|
|
||||||
|
//动态 resultsMap
|
||||||
|
if (id.endsWith(FlexConsts.METHOD_SELECT_LIST_BY_QUERY_AS)) {
|
||||||
|
Class<?> asType = MappedStatementTypes.getCurrentType();
|
||||||
|
return MapUtil.computeIfAbsent(dynamicMappedStatementCache, asType,
|
||||||
|
aClass -> replaceResultMap(ms, TableInfoFactory.ofEntityClass(asType))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addMappedStatement(MappedStatement ms) {
|
public void addMappedStatement(MappedStatement ms) {
|
||||||
//替换 RowMapper.insert 的主键生成器
|
//替换 RowMapper.insert 的主键生成器
|
||||||
@ -141,7 +160,7 @@ public class FlexConfiguration extends Configuration {
|
|||||||
//entity select
|
//entity select
|
||||||
else if (StringUtil.endsWithAny(ms.getId(), "selectOneById", "selectListByIds"
|
else if (StringUtil.endsWithAny(ms.getId(), "selectOneById", "selectListByIds"
|
||||||
, "selectListByQuery")) {
|
, "selectListByQuery")) {
|
||||||
ms = replaceResultMap(ms);
|
ms = replaceResultMap(ms, getTableInfo(ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
super.addMappedStatement(ms);
|
super.addMappedStatement(ms);
|
||||||
@ -151,9 +170,8 @@ public class FlexConfiguration extends Configuration {
|
|||||||
/**
|
/**
|
||||||
* 替换 entity 查询的 ResultMap
|
* 替换 entity 查询的 ResultMap
|
||||||
*/
|
*/
|
||||||
private MappedStatement replaceResultMap(MappedStatement ms) {
|
private MappedStatement replaceResultMap(MappedStatement ms, TableInfo tableInfo) {
|
||||||
|
|
||||||
TableInfo tableInfo = getTableInfo(ms);
|
|
||||||
if (tableInfo == null) {
|
if (tableInfo == null) {
|
||||||
return ms;
|
return ms;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* 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.mybatis;
|
||||||
|
|
||||||
|
public class MappedStatementTypes {
|
||||||
|
|
||||||
|
private static ThreadLocal<Class<?>> currentTypeTL = new ThreadLocal<>();
|
||||||
|
|
||||||
|
public static void setCurrentType(Class<?> type){
|
||||||
|
currentTypeTL.set(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> getCurrentType(){
|
||||||
|
return currentTypeTL.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear(){
|
||||||
|
currentTypeTL.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -16,6 +16,7 @@
|
|||||||
package com.mybatisflex.core.mybatis;
|
package com.mybatisflex.core.mybatis;
|
||||||
|
|
||||||
import com.mybatisflex.annotation.UseDataSource;
|
import com.mybatisflex.annotation.UseDataSource;
|
||||||
|
import com.mybatisflex.core.FlexConsts;
|
||||||
import com.mybatisflex.core.FlexGlobalConfig;
|
import com.mybatisflex.core.FlexGlobalConfig;
|
||||||
import com.mybatisflex.core.datasource.DataSourceKey;
|
import com.mybatisflex.core.datasource.DataSourceKey;
|
||||||
import com.mybatisflex.core.datasource.FlexDataSource;
|
import com.mybatisflex.core.datasource.FlexDataSource;
|
||||||
@ -45,7 +46,11 @@ public class MapperInvocationHandler implements InvocationHandler {
|
|||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
boolean clearDsKey = false;
|
boolean clearDsKey = false;
|
||||||
boolean clearDbType = false;
|
boolean clearDbType = false;
|
||||||
|
boolean isSelectListByQueryAsMethod = FlexConsts.METHOD_SELECT_LIST_BY_QUERY_AS.equals(method.getName());
|
||||||
try {
|
try {
|
||||||
|
if (isSelectListByQueryAsMethod){
|
||||||
|
MappedStatementTypes.setCurrentType((Class<?>) args[1]);
|
||||||
|
}
|
||||||
//获取用户动态指定,由用户指定数据源,则应该有用户清除
|
//获取用户动态指定,由用户指定数据源,则应该有用户清除
|
||||||
String dataSourceKey = DataSourceKey.get();
|
String dataSourceKey = DataSourceKey.get();
|
||||||
|
|
||||||
@ -73,6 +78,9 @@ public class MapperInvocationHandler implements InvocationHandler {
|
|||||||
}
|
}
|
||||||
return method.invoke(mapper, args);
|
return method.invoke(mapper, args);
|
||||||
} finally {
|
} finally {
|
||||||
|
if (isSelectListByQueryAsMethod){
|
||||||
|
MappedStatementTypes.clear();
|
||||||
|
}
|
||||||
if (clearDbType) {
|
if (clearDbType) {
|
||||||
DialectFactory.clearHintDbType();
|
DialectFactory.clearHintDbType();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,12 +32,12 @@ public class Page<T> implements Serializable {
|
|||||||
private long totalPage = INIT_VALUE;
|
private long totalPage = INIT_VALUE;
|
||||||
private long totalRow = INIT_VALUE;
|
private long totalRow = INIT_VALUE;
|
||||||
|
|
||||||
public static Page of(int pageNumber, int pageSize) {
|
public static <T> Page<T> of(int pageNumber, int pageSize) {
|
||||||
return new Page(pageNumber, pageSize);
|
return new Page<>(pageNumber, pageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Page of(int pageNumber, int pageSize, long totalRow) {
|
public static <T> Page<T> of(int pageNumber, int pageSize, long totalRow) {
|
||||||
return new Page(pageNumber, pageSize, totalRow);
|
return new Page<>(pageNumber, pageSize, totalRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page() {
|
public Page() {
|
||||||
@ -114,7 +114,7 @@ public class Page<T> implements Serializable {
|
|||||||
newPage.totalPage = totalPage;
|
newPage.totalPage = totalPage;
|
||||||
newPage.totalRow = totalRow;
|
newPage.totalRow = totalRow;
|
||||||
|
|
||||||
if (records != null) {
|
if (records != null && !records.isEmpty()) {
|
||||||
List<R> newRecords = new ArrayList<>(records.size());
|
List<R> newRecords = new ArrayList<>(records.size());
|
||||||
for (T t : records) {
|
for (T t : records) {
|
||||||
newRecords.add(mapper.apply(t));
|
newRecords.add(mapper.apply(t));
|
||||||
|
|||||||
@ -18,16 +18,17 @@ package com.mybatisflex.core.provider;
|
|||||||
import com.mybatisflex.core.dialect.DialectFactory;
|
import com.mybatisflex.core.dialect.DialectFactory;
|
||||||
import com.mybatisflex.core.exception.FlexExceptions;
|
import com.mybatisflex.core.exception.FlexExceptions;
|
||||||
import com.mybatisflex.core.query.CPI;
|
import com.mybatisflex.core.query.CPI;
|
||||||
|
import com.mybatisflex.core.query.QueryTable;
|
||||||
import com.mybatisflex.core.query.QueryWrapper;
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
import com.mybatisflex.core.table.TableInfo;
|
import com.mybatisflex.core.table.TableInfo;
|
||||||
|
import com.mybatisflex.core.table.TableInfoFactory;
|
||||||
import com.mybatisflex.core.util.ArrayUtil;
|
import com.mybatisflex.core.util.ArrayUtil;
|
||||||
import com.mybatisflex.core.util.CollectionUtil;
|
import com.mybatisflex.core.util.CollectionUtil;
|
||||||
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
import org.apache.ibatis.builder.annotation.ProviderContext;
|
import org.apache.ibatis.builder.annotation.ProviderContext;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class EntitySqlProvider {
|
public class EntitySqlProvider {
|
||||||
|
|
||||||
@ -76,7 +77,6 @@ public class EntitySqlProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* insertBatch 的 sql 构建
|
* insertBatch 的 sql 构建
|
||||||
*
|
*
|
||||||
@ -308,17 +308,18 @@ public class EntitySqlProvider {
|
|||||||
if (queryWrapper == null) {
|
if (queryWrapper == null) {
|
||||||
throw FlexExceptions.wrap("queryWrapper can not be null.");
|
throw FlexExceptions.wrap("queryWrapper can not be null.");
|
||||||
}
|
}
|
||||||
|
List<TableInfo> tableInfos = getTableInfos(context, queryWrapper);
|
||||||
|
for (TableInfo tableInfo : tableInfos) {
|
||||||
|
tableInfo.appendConditions(null, queryWrapper);
|
||||||
|
|
||||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
CPI.setSelectColumnsIfNecessary(queryWrapper, tableInfo.getDefaultQueryColumn());
|
||||||
tableInfo.appendConditions(null, queryWrapper);
|
CPI.setFromIfNecessary(queryWrapper, tableInfo.getTableName());
|
||||||
|
}
|
||||||
|
|
||||||
Object[] values = CPI.getValueArray(queryWrapper);
|
Object[] values = CPI.getValueArray(queryWrapper);
|
||||||
ProviderUtil.setSqlArgs(params, values);
|
ProviderUtil.setSqlArgs(params, values);
|
||||||
|
|
||||||
CPI.setSelectColumnsIfNecessary(queryWrapper, tableInfo.getDefaultQueryColumn());
|
return DialectFactory.getDialect().forSelectByQuery(queryWrapper);
|
||||||
CPI.setFromIfNecessary(queryWrapper, tableInfo.getTableName());
|
|
||||||
|
|
||||||
return DialectFactory.getDialect().forSelectListByQuery(queryWrapper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -327,22 +328,50 @@ public class EntitySqlProvider {
|
|||||||
* @param params
|
* @param params
|
||||||
* @param context
|
* @param context
|
||||||
* @return sql
|
* @return sql
|
||||||
* @see com.mybatisflex.core.BaseMapper#selectCountByQuery(QueryWrapper)
|
* @see com.mybatisflex.core.BaseMapper#selectObjectByQuery(QueryWrapper)
|
||||||
*/
|
*/
|
||||||
public static String selectCountByQuery(Map params, ProviderContext context) {
|
public static String selectObjectByQuery(Map params, ProviderContext context) {
|
||||||
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
||||||
if (queryWrapper == null) {
|
if (queryWrapper == null) {
|
||||||
throw FlexExceptions.wrap("queryWrapper can not be null.");
|
throw FlexExceptions.wrap("queryWrapper can not be null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
List<TableInfo> tableInfos = getTableInfos(context, queryWrapper);
|
||||||
tableInfo.appendConditions(null, queryWrapper);
|
|
||||||
|
for (TableInfo tableInfo : tableInfos) {
|
||||||
|
tableInfo.appendConditions(null, queryWrapper);
|
||||||
|
CPI.setFromIfNecessary(queryWrapper, tableInfo.getTableName());
|
||||||
|
}
|
||||||
|
|
||||||
Object[] values = CPI.getValueArray(queryWrapper);
|
Object[] values = CPI.getValueArray(queryWrapper);
|
||||||
ProviderUtil.setSqlArgs(params, values);
|
ProviderUtil.setSqlArgs(params, values);
|
||||||
|
|
||||||
CPI.setFromIfNecessary(queryWrapper, tableInfo.getTableName());
|
return DialectFactory.getDialect().forSelectByQuery(queryWrapper);
|
||||||
return DialectFactory.getDialect().forSelectCountByQuery(queryWrapper);
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static List<TableInfo> getTableInfos(ProviderContext context, QueryWrapper queryWrapper) {
|
||||||
|
List<TableInfo> tableInfos;
|
||||||
|
List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
|
||||||
|
if (CollectionUtil.isNotEmpty(queryTables)) {
|
||||||
|
tableInfos = new ArrayList<>();
|
||||||
|
for (QueryTable queryTable : queryTables) {
|
||||||
|
String tableName = queryTable.getName();
|
||||||
|
if (StringUtil.isNotBlank(tableName)) {
|
||||||
|
TableInfo tableInfo = TableInfoFactory.ofTableName(tableName);
|
||||||
|
if (tableInfo != null) {
|
||||||
|
tableInfos.add(tableInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tableInfos = Collections.singletonList(ProviderUtil.getTableInfo(context));
|
||||||
|
}
|
||||||
|
return tableInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,8 @@ import com.mybatisflex.core.query.QueryWrapper;
|
|||||||
import com.mybatisflex.core.row.Row;
|
import com.mybatisflex.core.row.Row;
|
||||||
import com.mybatisflex.core.row.RowCPI;
|
import com.mybatisflex.core.row.RowCPI;
|
||||||
import com.mybatisflex.core.row.RowMapper;
|
import com.mybatisflex.core.row.RowMapper;
|
||||||
|
import com.mybatisflex.core.table.TableInfo;
|
||||||
|
import com.mybatisflex.core.table.TableInfoFactory;
|
||||||
import com.mybatisflex.core.util.ArrayUtil;
|
import com.mybatisflex.core.util.ArrayUtil;
|
||||||
import com.mybatisflex.core.util.CollectionUtil;
|
import com.mybatisflex.core.util.CollectionUtil;
|
||||||
|
|
||||||
@ -213,6 +215,36 @@ public class RowSqlProvider {
|
|||||||
return DialectFactory.getDialect().forUpdateBatchById(tableName, rows);
|
return DialectFactory.getDialect().forUpdateBatchById(tableName, rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* updateEntity 的 sql 构建
|
||||||
|
*
|
||||||
|
* @param params
|
||||||
|
* @return sql
|
||||||
|
* @see RowMapper#updateEntity(Object entities)
|
||||||
|
*/
|
||||||
|
public static String updateEntity(Map params) {
|
||||||
|
Object entity = ProviderUtil.getEntity(params);
|
||||||
|
if (entity == null) {
|
||||||
|
throw FlexExceptions.wrap("entity can not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 该 Mapper 是通用 Mapper 无法通过 ProviderContext 获取,直接使用 TableInfoFactory
|
||||||
|
TableInfo tableInfo = TableInfoFactory.ofEntityClass(entity.getClass());
|
||||||
|
|
||||||
|
// 执行 onUpdate 监听器
|
||||||
|
tableInfo.invokeOnUpdateListener(entity);
|
||||||
|
|
||||||
|
Object[] updateValues = tableInfo.buildUpdateSqlArgs(entity, false, false);
|
||||||
|
Object[] primaryValues = tableInfo.buildPkSqlArgs(entity);
|
||||||
|
Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
|
||||||
|
|
||||||
|
FlexExceptions.assertAreNotNull(primaryValues, "The value of primary key must not be null, entity[%s]", entity);
|
||||||
|
|
||||||
|
ProviderUtil.setSqlArgs(params, ArrayUtil.concat(updateValues, primaryValues, tenantIdArgs));
|
||||||
|
|
||||||
|
return DialectFactory.getDialect().forUpdateEntity(tableInfo, entity, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* selectOneById 的 sql 构建
|
* selectOneById 的 sql 构建
|
||||||
@ -248,7 +280,7 @@ public class RowSqlProvider {
|
|||||||
ProviderUtil.setSqlArgs(params, valueArray);
|
ProviderUtil.setSqlArgs(params, valueArray);
|
||||||
|
|
||||||
|
|
||||||
return DialectFactory.getDialect().forSelectListByQuery(queryWrapper);
|
return DialectFactory.getDialect().forSelectByQuery(queryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -258,7 +290,7 @@ public class RowSqlProvider {
|
|||||||
* @return sql
|
* @return sql
|
||||||
* @see RowMapper#selectCountByQuery(String, QueryWrapper)
|
* @see RowMapper#selectCountByQuery(String, QueryWrapper)
|
||||||
*/
|
*/
|
||||||
public static String selectCountByQuery(Map params) {
|
public static String selectObjectByQuery(Map params) {
|
||||||
String tableName = ProviderUtil.getTableName(params);
|
String tableName = ProviderUtil.getTableName(params);
|
||||||
|
|
||||||
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
||||||
@ -267,7 +299,7 @@ public class RowSqlProvider {
|
|||||||
Object[] valueArray = CPI.getValueArray(queryWrapper);
|
Object[] valueArray = CPI.getValueArray(queryWrapper);
|
||||||
ProviderUtil.setSqlArgs(params, valueArray);
|
ProviderUtil.setSqlArgs(params, valueArray);
|
||||||
|
|
||||||
return DialectFactory.getDialect().forSelectCountByQuery(queryWrapper);
|
return DialectFactory.getDialect().forSelectByQuery(queryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@ public class BaseQueryWrapper<T> implements Serializable {
|
|||||||
|
|
||||||
protected List<QueryTable> queryTables;
|
protected List<QueryTable> queryTables;
|
||||||
protected String dataSource;
|
protected String dataSource;
|
||||||
|
protected String hint;
|
||||||
|
|
||||||
protected List<QueryColumn> selectColumns;
|
protected List<QueryColumn> selectColumns;
|
||||||
protected List<Join> joins;
|
protected List<Join> joins;
|
||||||
@ -37,6 +38,8 @@ public class BaseQueryWrapper<T> implements Serializable {
|
|||||||
protected Integer limitOffset;
|
protected Integer limitOffset;
|
||||||
protected Integer limitRows;
|
protected Integer limitRows;
|
||||||
|
|
||||||
|
protected List<String> endFragments;
|
||||||
|
|
||||||
protected Map<String, Object> context;
|
protected Map<String, Object> context;
|
||||||
|
|
||||||
// protected boolean ignoreBlankStrings = false;
|
// protected boolean ignoreBlankStrings = false;
|
||||||
@ -52,7 +55,7 @@ public class BaseQueryWrapper<T> implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected T AddJoin(Join join) {
|
protected T addJoin(Join join) {
|
||||||
if (joins == null) {
|
if (joins == null) {
|
||||||
joins = new LinkedList<>();
|
joins = new LinkedList<>();
|
||||||
}
|
}
|
||||||
@ -119,6 +122,13 @@ public class BaseQueryWrapper<T> implements Serializable {
|
|||||||
joinTables.add(queryTable);
|
joinTables.add(queryTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void addEndFragment(String fragment){
|
||||||
|
if (endFragments == null){
|
||||||
|
endFragments = new ArrayList<>();
|
||||||
|
}
|
||||||
|
endFragments.add(fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected List<QueryTable> getQueryTables() {
|
protected List<QueryTable> getQueryTables() {
|
||||||
return queryTables;
|
return queryTables;
|
||||||
@ -136,6 +146,14 @@ public class BaseQueryWrapper<T> implements Serializable {
|
|||||||
this.dataSource = dataSource;
|
this.dataSource = dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String getHint() {
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setHint(String hint) {
|
||||||
|
this.hint = hint;
|
||||||
|
}
|
||||||
|
|
||||||
protected List<QueryColumn> getSelectColumns() {
|
protected List<QueryColumn> getSelectColumns() {
|
||||||
return selectColumns;
|
return selectColumns;
|
||||||
}
|
}
|
||||||
@ -212,6 +230,14 @@ public class BaseQueryWrapper<T> implements Serializable {
|
|||||||
this.limitRows = limitRows;
|
this.limitRows = limitRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected List<String> getEndFragments() {
|
||||||
|
return endFragments;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setEndFragments(List<String> endFragments) {
|
||||||
|
this.endFragments = endFragments;
|
||||||
|
}
|
||||||
|
|
||||||
protected Map<String, Object> getContext() {
|
protected Map<String, Object> getContext() {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,11 +25,10 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class Brackets extends QueryCondition {
|
public class Brackets extends QueryCondition {
|
||||||
|
|
||||||
private final QueryCondition childCondition;
|
private final QueryCondition child;
|
||||||
|
|
||||||
|
|
||||||
public Brackets(QueryCondition childCondition) {
|
public Brackets(QueryCondition childCondition) {
|
||||||
this.childCondition = childCondition;
|
this.child = childCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -46,16 +45,16 @@ public class Brackets extends QueryCondition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void connectToChild(QueryCondition nextCondition, SqlConnector connector) {
|
protected void connectToChild(QueryCondition nextCondition, SqlConnector connector) {
|
||||||
childCondition.connect(nextCondition, connector);
|
child.connect(nextCondition, connector);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getValue() {
|
public Object getValue() {
|
||||||
return checkEffective() ? WrapperUtil.getValues(childCondition) : null;
|
return checkEffective() ? WrapperUtil.getValues(child) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryCondition getChildCondition() {
|
public QueryCondition getChild() {
|
||||||
return childCondition;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -64,7 +63,7 @@ public class Brackets extends QueryCondition {
|
|||||||
if (!effective) {
|
if (!effective) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QueryCondition condition = this.childCondition;
|
QueryCondition condition = this.child;
|
||||||
while (condition != null) {
|
while (condition != null) {
|
||||||
if (condition.checkEffective()) {
|
if (condition.checkEffective()) {
|
||||||
return true;
|
return true;
|
||||||
@ -81,7 +80,7 @@ public class Brackets extends QueryCondition {
|
|||||||
|
|
||||||
StringBuilder sql = new StringBuilder();
|
StringBuilder sql = new StringBuilder();
|
||||||
if (checkEffective()) {
|
if (checkEffective()) {
|
||||||
String childSql = childCondition.toSql(queryTables, dialect);
|
String childSql = child.toSql(queryTables, dialect);
|
||||||
if (StringUtil.isNotBlank(childSql)) {
|
if (StringUtil.isNotBlank(childSql)) {
|
||||||
QueryCondition effectiveBefore = getEffectiveBefore();
|
QueryCondition effectiveBefore = getEffectiveBefore();
|
||||||
if (effectiveBefore != null) {
|
if (effectiveBefore != null) {
|
||||||
@ -101,10 +100,15 @@ public class Brackets extends QueryCondition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean containsTable(String... tables) {
|
||||||
|
return child != null && child.containsTable(tables);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Brackets{" +
|
return "Brackets{" +
|
||||||
"childCondition=" + childCondition +
|
"childCondition=" + child +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,6 +55,14 @@ public class CPI {
|
|||||||
queryWrapper.setDataSource(datasource);
|
queryWrapper.setDataSource(datasource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getHint(QueryWrapper queryWrapper) {
|
||||||
|
return queryWrapper.getHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setHint(QueryWrapper queryWrapper, String hint) {
|
||||||
|
queryWrapper.setHint(hint);
|
||||||
|
}
|
||||||
|
|
||||||
public static List<QueryColumn> getSelectColumns(QueryWrapper queryWrapper) {
|
public static List<QueryColumn> getSelectColumns(QueryWrapper queryWrapper) {
|
||||||
return queryWrapper.getSelectColumns();
|
return queryWrapper.getSelectColumns();
|
||||||
}
|
}
|
||||||
@ -80,6 +88,13 @@ public class CPI {
|
|||||||
queryWrapper.setJoins(joins);
|
queryWrapper.setJoins(joins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getJoinType(Join join) {
|
||||||
|
return join.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static QueryTable getJoinQueryTable(Join join) {
|
||||||
|
return join.getQueryTable();
|
||||||
|
}
|
||||||
|
|
||||||
public static List<QueryTable> getJoinTables(QueryWrapper queryWrapper) {
|
public static List<QueryTable> getJoinTables(QueryWrapper queryWrapper) {
|
||||||
return queryWrapper.getJoinTables();
|
return queryWrapper.getJoinTables();
|
||||||
@ -143,6 +158,15 @@ public class CPI {
|
|||||||
queryWrapper.setLimitRows(limitRows);
|
queryWrapper.setLimitRows(limitRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<String> getEndFragments(QueryWrapper queryWrapper) {
|
||||||
|
return queryWrapper.getEndFragments();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setEndFragments(QueryWrapper queryWrapper,List<String> endFragments) {
|
||||||
|
queryWrapper.setEndFragments(endFragments);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Map<String, Object> getContext(QueryWrapper queryWrapper) {
|
public static Map<String, Object> getContext(QueryWrapper queryWrapper) {
|
||||||
return queryWrapper.getContext();
|
return queryWrapper.getContext();
|
||||||
}
|
}
|
||||||
@ -175,4 +199,12 @@ public class CPI {
|
|||||||
queryWrapper.from(tableName);
|
queryWrapper.from(tableName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean containsTable(QueryCondition condition, String... tables) {
|
||||||
|
return condition != null && condition.containsTable(tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static QueryWrapper getQueryWrapper(SelectQueryColumn selectQueryColumn) {
|
||||||
|
return selectQueryColumn.getQueryWrapper();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,135 @@
|
|||||||
|
/**
|
||||||
|
* 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.query;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.dialect.IDialect;
|
||||||
|
import com.mybatisflex.core.util.ArrayUtil;
|
||||||
|
import com.mybatisflex.core.util.LambdaGetter;
|
||||||
|
import com.mybatisflex.core.util.LambdaUtil;
|
||||||
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CaseQueryColumn extends QueryColumn implements HasParamsColumn{
|
||||||
|
|
||||||
|
private List<When> whens;
|
||||||
|
private Object elseValue;
|
||||||
|
|
||||||
|
void addWhen(When when) {
|
||||||
|
if (whens == null) {
|
||||||
|
whens = new ArrayList<>();
|
||||||
|
}
|
||||||
|
whens.add(when);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||||
|
StringBuilder sql = new StringBuilder("CASE");
|
||||||
|
for (When when : whens) {
|
||||||
|
sql.append(" WHEN ").append(when.whenCondition.toSql(queryTables, dialect));
|
||||||
|
sql.append(" THEN ").append(buildValue(when.thenValue));
|
||||||
|
}
|
||||||
|
if (elseValue != null) {
|
||||||
|
sql.append(" ELSE ").append(buildValue(elseValue));
|
||||||
|
}
|
||||||
|
sql.append(" END");
|
||||||
|
if (StringUtil.isNotBlank(alias)) {
|
||||||
|
return "(" + sql + ") AS " + dialect.wrap(alias);
|
||||||
|
}
|
||||||
|
return sql.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String toConditionSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||||
|
StringBuilder sql = new StringBuilder("CASE");
|
||||||
|
for (When when : whens) {
|
||||||
|
sql.append(" WHEN ").append(when.whenCondition.toSql(queryTables, dialect));
|
||||||
|
sql.append(" THEN ").append(buildValue(when.thenValue));
|
||||||
|
}
|
||||||
|
if (elseValue != null) {
|
||||||
|
sql.append(" ELSE ").append(buildValue(elseValue));
|
||||||
|
}
|
||||||
|
sql.append(" END");
|
||||||
|
return "(" + sql + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryColumn as(String alias) {
|
||||||
|
this.alias = alias;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> QueryColumn as(LambdaGetter<T> fn) {
|
||||||
|
return as(LambdaUtil.getFieldName(fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildValue(Object value) {
|
||||||
|
if (value instanceof Number || value instanceof Boolean) {
|
||||||
|
return String.valueOf(value);
|
||||||
|
} else {
|
||||||
|
return "'" + value + "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] getParamValues() {
|
||||||
|
Object[] values = new Object[0];
|
||||||
|
for (When when : whens) {
|
||||||
|
values = ArrayUtil.concat(values, WrapperUtil.getValues(when.whenCondition));
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class When {
|
||||||
|
private Builder builder;
|
||||||
|
private QueryCondition whenCondition;
|
||||||
|
private Object thenValue;
|
||||||
|
|
||||||
|
public When(Builder builder, QueryCondition whenCondition) {
|
||||||
|
this.builder = builder;
|
||||||
|
this.whenCondition = whenCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder then(Object thenValue) {
|
||||||
|
this.thenValue = thenValue;
|
||||||
|
this.builder.caseQueryColumn.addWhen(this);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private CaseQueryColumn caseQueryColumn = new CaseQueryColumn();
|
||||||
|
|
||||||
|
public When when(QueryCondition condition) {
|
||||||
|
return new When(this, condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder else_(Object elseValue) {
|
||||||
|
caseQueryColumn.elseValue = elseValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CaseQueryColumn end() {
|
||||||
|
return caseQueryColumn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* 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.query;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.dialect.IDialect;
|
||||||
|
import com.mybatisflex.core.util.LambdaGetter;
|
||||||
|
import com.mybatisflex.core.util.LambdaUtil;
|
||||||
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CaseSearchQueryColumn extends QueryColumn {
|
||||||
|
|
||||||
|
private QueryColumn queryColumn;
|
||||||
|
private List<When> whens;
|
||||||
|
private Object elseValue;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||||
|
StringBuilder sql = new StringBuilder("CASE ");
|
||||||
|
sql.append(queryColumn.toSelectSql(queryTables, dialect));
|
||||||
|
for (When when : whens) {
|
||||||
|
sql.append(" WHEN ").append(buildValue(when.searchValue)).append(" THEN ").append(buildValue(when.thenValue));
|
||||||
|
}
|
||||||
|
if (elseValue != null) {
|
||||||
|
sql.append(" ELSE ").append(buildValue(elseValue));
|
||||||
|
}
|
||||||
|
sql.append(" END");
|
||||||
|
if (StringUtil.isNotBlank(alias)) {
|
||||||
|
return "(" + sql + ") AS " + dialect.wrap(alias);
|
||||||
|
}
|
||||||
|
return sql.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String toConditionSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||||
|
StringBuilder sql = new StringBuilder("CASE ");
|
||||||
|
sql.append(queryColumn.toSelectSql(queryTables, dialect));
|
||||||
|
for (When when : whens) {
|
||||||
|
sql.append(" WHEN ").append(buildValue(when.searchValue)).append(" THEN ").append(buildValue(when.thenValue));
|
||||||
|
}
|
||||||
|
if (elseValue != null) {
|
||||||
|
sql.append(" ELSE ").append(buildValue(elseValue));
|
||||||
|
}
|
||||||
|
sql.append(" END");
|
||||||
|
return "(" + sql + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String buildValue(Object value) {
|
||||||
|
if (value instanceof Number || value instanceof Boolean) {
|
||||||
|
return String.valueOf(value);
|
||||||
|
} else {
|
||||||
|
return "'" + value + "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void addWhen(When when) {
|
||||||
|
if (whens == null) {
|
||||||
|
whens = new ArrayList<>();
|
||||||
|
}
|
||||||
|
whens.add(when);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryColumn as(String alias) {
|
||||||
|
this.alias = alias;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> QueryColumn as(LambdaGetter<T> fn) {
|
||||||
|
return as(LambdaUtil.getFieldName(fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class When {
|
||||||
|
private Builder builder;
|
||||||
|
private Object searchValue;
|
||||||
|
private Object thenValue;
|
||||||
|
|
||||||
|
public When(Builder builder, Object searchValue) {
|
||||||
|
this.builder = builder;
|
||||||
|
this.searchValue = searchValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder then(Object thenValue) {
|
||||||
|
this.thenValue = thenValue;
|
||||||
|
this.builder.caseQueryColumn.addWhen(this);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private CaseSearchQueryColumn caseQueryColumn = new CaseSearchQueryColumn();
|
||||||
|
|
||||||
|
public Builder(QueryColumn queryColumn) {
|
||||||
|
this.caseQueryColumn.queryColumn = queryColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public When when(Object searchValue) {
|
||||||
|
return new When(this, searchValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder else_(Object elseValue) {
|
||||||
|
caseQueryColumn.elseValue = elseValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CaseSearchQueryColumn end() {
|
||||||
|
return caseQueryColumn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package com.mybatisflex.core.query;
|
||||||
|
|
||||||
|
public interface HasParamsColumn {
|
||||||
|
|
||||||
|
Object[] getParamValues();
|
||||||
|
}
|
||||||
@ -30,17 +30,18 @@ public class Join implements Serializable {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
static final String TYPE_LEFT = " LEFT JOIN ";
|
public static final String TYPE_JOIN = " JOIN ";
|
||||||
static final String TYPE_RIGHT = " RIGHT JOIN ";
|
public static final String TYPE_LEFT = " LEFT JOIN ";
|
||||||
static final String TYPE_INNER = " INNER JOIN ";
|
public static final String TYPE_RIGHT = " RIGHT JOIN ";
|
||||||
static final String TYPE_FULL = " FULL JOIN ";
|
public static final String TYPE_INNER = " INNER JOIN ";
|
||||||
static final String TYPE_CROSS = " CROSS JOIN ";
|
public static final String TYPE_FULL = " FULL JOIN ";
|
||||||
|
public static final String TYPE_CROSS = " CROSS JOIN ";
|
||||||
|
|
||||||
|
|
||||||
private final String type;
|
protected final String type;
|
||||||
private final QueryTable queryTable;
|
protected final QueryTable queryTable;
|
||||||
private QueryCondition on;
|
protected QueryCondition on;
|
||||||
private boolean effective;
|
protected boolean effective;
|
||||||
|
|
||||||
public Join(String type, String table, boolean when) {
|
public Join(String type, String table, boolean when) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@ -55,7 +56,6 @@ public class Join implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
QueryTable getQueryTable() {
|
QueryTable getQueryTable() {
|
||||||
return queryTable;
|
return queryTable;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,4 +61,9 @@ public class OperatorQueryCondition extends QueryCondition {
|
|||||||
public Object getValue() {
|
public Object getValue() {
|
||||||
return WrapperUtil.getValues(child);
|
return WrapperUtil.getValues(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean containsTable(String... tables) {
|
||||||
|
return child != null && child.containsTable(tables);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,4 +63,10 @@ public class OperatorSelectCondition extends QueryCondition {
|
|||||||
public Object getValue() {
|
public Object getValue() {
|
||||||
return queryWrapper.getValueArray();
|
return queryWrapper.getValueArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean containsTable(String... tables) {
|
||||||
|
QueryCondition condition = queryWrapper.getWhereQueryCondition();
|
||||||
|
return condition != null && condition.containsTable(tables);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,8 @@ package com.mybatisflex.core.query;
|
|||||||
|
|
||||||
import com.mybatisflex.core.dialect.IDialect;
|
import com.mybatisflex.core.dialect.IDialect;
|
||||||
import com.mybatisflex.core.table.TableDef;
|
import com.mybatisflex.core.table.TableDef;
|
||||||
|
import com.mybatisflex.core.util.LambdaGetter;
|
||||||
|
import com.mybatisflex.core.util.LambdaUtil;
|
||||||
import com.mybatisflex.core.util.SqlUtil;
|
import com.mybatisflex.core.util.SqlUtil;
|
||||||
import com.mybatisflex.core.util.StringUtil;
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
|
|
||||||
@ -80,6 +82,10 @@ public class QueryColumn implements Serializable {
|
|||||||
this.alias = alias;
|
this.alias = alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> QueryColumn as(LambdaGetter<T> fn) {
|
||||||
|
return as(LambdaUtil.getFieldName(fn));
|
||||||
|
}
|
||||||
|
|
||||||
public QueryColumn as(String alias) {
|
public QueryColumn as(String alias) {
|
||||||
SqlUtil.keepColumnSafely(alias);
|
SqlUtil.keepColumnSafely(alias);
|
||||||
QueryColumn newColumn = new QueryColumn();
|
QueryColumn newColumn = new QueryColumn();
|
||||||
|
|||||||
@ -120,7 +120,7 @@ public class QueryCondition implements Serializable {
|
|||||||
this.effective = (effective != null && effective);
|
this.effective = (effective != null && effective);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> QueryCondition when(Predicate<T> fn){
|
public <T> QueryCondition when(Predicate<T> fn) {
|
||||||
Object val = this.value;
|
Object val = this.value;
|
||||||
if (LOGIC_LIKE.equals(logic) && val instanceof String) {
|
if (LOGIC_LIKE.equals(logic) && val instanceof String) {
|
||||||
String valStr = (String) val;
|
String valStr = (String) val;
|
||||||
@ -183,8 +183,12 @@ public class QueryCondition implements Serializable {
|
|||||||
if (effectiveBefore != null) {
|
if (effectiveBefore != null) {
|
||||||
sql.append(effectiveBefore.connector);
|
sql.append(effectiveBefore.connector);
|
||||||
}
|
}
|
||||||
|
//列
|
||||||
sql.append(getColumn().toConditionSql(queryTables, dialect));
|
sql.append(getColumn().toConditionSql(queryTables, dialect));
|
||||||
|
//逻辑符号
|
||||||
sql.append(" ").append(logic).append(" ");
|
sql.append(" ").append(logic).append(" ");
|
||||||
|
|
||||||
|
//值(或者问号)
|
||||||
if (value instanceof QueryColumn) {
|
if (value instanceof QueryColumn) {
|
||||||
sql.append(((QueryColumn) value).toConditionSql(queryTables, dialect));
|
sql.append(((QueryColumn) value).toConditionSql(queryTables, dialect));
|
||||||
}
|
}
|
||||||
@ -264,6 +268,19 @@ public class QueryCondition implements Serializable {
|
|||||||
return paramsCount;
|
return paramsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
boolean containsTable(String... tables){
|
||||||
|
if (column == null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (String table : tables) {
|
||||||
|
if (column.table != null && table.equals(column.table.name)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "QueryCondition{" +
|
return "QueryCondition{" +
|
||||||
|
|||||||
@ -65,11 +65,27 @@ public class QueryMethods {
|
|||||||
return new DistinctQueryColumn(columns);
|
return new DistinctQueryColumn(columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CaseQueryColumn.Builder case_() {
|
||||||
|
return new CaseQueryColumn.Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CaseSearchQueryColumn.Builder case_(QueryColumn queryColumn) {
|
||||||
|
return new CaseSearchQueryColumn.Builder(queryColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
//CONVERT ( data_type [ ( length ) ] , expression [ , style ] )
|
||||||
|
public static StringFunctionQueryColumn convert(String... params) {
|
||||||
|
return new StringFunctionQueryColumn("CONVERT", params);
|
||||||
|
}
|
||||||
|
|
||||||
public static StringQueryColumn column(String column) {
|
public static StringQueryColumn column(String column) {
|
||||||
return new StringQueryColumn(column);
|
return new StringQueryColumn(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SelectQueryColumn column(QueryWrapper queryWrapper) {
|
||||||
|
return new SelectQueryColumn(queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
public static QueryCondition exists(QueryWrapper queryWrapper) {
|
public static QueryCondition exists(QueryWrapper queryWrapper) {
|
||||||
return new OperatorSelectCondition(" EXISTS ", queryWrapper);
|
return new OperatorSelectCondition(" EXISTS ", queryWrapper);
|
||||||
}
|
}
|
||||||
@ -82,7 +98,7 @@ public class QueryMethods {
|
|||||||
return new OperatorQueryCondition(" NOT ", childCondition);
|
return new OperatorQueryCondition(" NOT ", childCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static QueryCondition noCondition(){
|
public static QueryCondition noCondition() {
|
||||||
return QueryCondition.createEmpty();
|
return QueryCondition.createEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,12 +111,19 @@ public class QueryMethods {
|
|||||||
return newWrapper().select(queryColumns);
|
return newWrapper().select(queryColumns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static QueryWrapper selectOne() {
|
public static QueryWrapper selectOne() {
|
||||||
return select(column("1"));
|
return select(column("1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RawValue raw(String raw){
|
public static QueryWrapper selectCount() {
|
||||||
|
return select(count());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static QueryWrapper selectCountOne(String countP) {
|
||||||
|
return select(count("1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RawValue raw(String raw) {
|
||||||
return new RawValue(raw);
|
return new RawValue(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -155,8 +155,8 @@ public class QueryWrapper extends BaseQueryWrapper<QueryWrapper> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Joiner<QueryWrapper> leftJoinIf(String table, boolean condition) {
|
public Joiner<QueryWrapper> leftJoinIf(String table, boolean when) {
|
||||||
return joining(Join.TYPE_LEFT, table, condition);
|
return joining(Join.TYPE_LEFT, table, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> leftJoin(TableDef table) {
|
public Joiner<QueryWrapper> leftJoin(TableDef table) {
|
||||||
@ -164,88 +164,104 @@ public class QueryWrapper extends BaseQueryWrapper<QueryWrapper> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Joiner<QueryWrapper> leftJoinIf(TableDef table, boolean condition) {
|
public Joiner<QueryWrapper> leftJoinIf(TableDef table, boolean when) {
|
||||||
return joining(Join.TYPE_LEFT, table.getTableName(), condition);
|
return joining(Join.TYPE_LEFT, table.getTableName(), when);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> leftJoin(QueryWrapper table) {
|
public Joiner<QueryWrapper> leftJoin(QueryWrapper table) {
|
||||||
return joining(Join.TYPE_LEFT, table, true);
|
return joining(Join.TYPE_LEFT, table, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> leftJoinIf(QueryWrapper table, boolean condition) {
|
public Joiner<QueryWrapper> leftJoinIf(QueryWrapper table, boolean when) {
|
||||||
return joining(Join.TYPE_LEFT, table, condition);
|
return joining(Join.TYPE_LEFT, table, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> rightJoin(String table) {
|
public Joiner<QueryWrapper> rightJoin(String table) {
|
||||||
return joining(Join.TYPE_RIGHT, table, true);
|
return joining(Join.TYPE_RIGHT, table, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> rightJoinIf(String table, boolean condition) {
|
public Joiner<QueryWrapper> rightJoinIf(String table, boolean when) {
|
||||||
return joining(Join.TYPE_RIGHT, table, condition);
|
return joining(Join.TYPE_RIGHT, table, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> rightJoin(QueryWrapper table) {
|
public Joiner<QueryWrapper> rightJoin(QueryWrapper table) {
|
||||||
return joining(Join.TYPE_RIGHT, table, true);
|
return joining(Join.TYPE_RIGHT, table, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> rightJoinIf(QueryWrapper table, boolean condition) {
|
public Joiner<QueryWrapper> rightJoinIf(QueryWrapper table, boolean when) {
|
||||||
return joining(Join.TYPE_RIGHT, table, condition);
|
return joining(Join.TYPE_RIGHT, table, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> innerJoin(String table) {
|
public Joiner<QueryWrapper> innerJoin(String table) {
|
||||||
return joining(Join.TYPE_INNER, table, true);
|
return joining(Join.TYPE_INNER, table, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> innerJoinIf(String table, boolean condition) {
|
public Joiner<QueryWrapper> innerJoinIf(String table, boolean when) {
|
||||||
return joining(Join.TYPE_INNER, table, condition);
|
return joining(Join.TYPE_INNER, table, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> innerJoin(TableDef table) {
|
public Joiner<QueryWrapper> innerJoin(TableDef table) {
|
||||||
return innerJoinIf(table, true);
|
return innerJoinIf(table, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> innerJoinIf(TableDef table, boolean condition) {
|
public Joiner<QueryWrapper> innerJoinIf(TableDef table, boolean when) {
|
||||||
return joining(Join.TYPE_INNER, table.getTableName(), condition);
|
return joining(Join.TYPE_INNER, table.getTableName(), when);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> innerJoin(QueryWrapper table) {
|
public Joiner<QueryWrapper> innerJoin(QueryWrapper table) {
|
||||||
return joining(Join.TYPE_INNER, table, true);
|
return joining(Join.TYPE_INNER, table, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> innerJoinIf(QueryWrapper table, boolean condition) {
|
public Joiner<QueryWrapper> innerJoinIf(QueryWrapper table, boolean when) {
|
||||||
return joining(Join.TYPE_INNER, table, condition);
|
return joining(Join.TYPE_INNER, table, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> fullJoin(String table) {
|
public Joiner<QueryWrapper> fullJoin(String table) {
|
||||||
return joining(Join.TYPE_FULL, table, true);
|
return joining(Join.TYPE_FULL, table, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> fullJoinIf(String table, boolean condition) {
|
public Joiner<QueryWrapper> fullJoinIf(String table, boolean when) {
|
||||||
return joining(Join.TYPE_FULL, table, condition);
|
return joining(Join.TYPE_FULL, table, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> fullJoin(QueryWrapper table) {
|
public Joiner<QueryWrapper> fullJoin(QueryWrapper table) {
|
||||||
return joining(Join.TYPE_FULL, table, true);
|
return joining(Join.TYPE_FULL, table, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> fullJoinIf(QueryWrapper table, boolean condition) {
|
public Joiner<QueryWrapper> fullJoinIf(QueryWrapper table, boolean when) {
|
||||||
return joining(Join.TYPE_FULL, table, condition);
|
return joining(Join.TYPE_FULL, table, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> crossJoin(String table) {
|
public Joiner<QueryWrapper> crossJoin(String table) {
|
||||||
return joining(Join.TYPE_CROSS, table, true);
|
return joining(Join.TYPE_CROSS, table, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> crossJoinIf(String table, boolean condition) {
|
public Joiner<QueryWrapper> crossJoinIf(String table, boolean when) {
|
||||||
return joining(Join.TYPE_CROSS, table, condition);
|
return joining(Join.TYPE_CROSS, table, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> crossJoin(QueryWrapper table) {
|
public Joiner<QueryWrapper> crossJoin(QueryWrapper table) {
|
||||||
return joining(Join.TYPE_CROSS, table, true);
|
return joining(Join.TYPE_CROSS, table, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Joiner<QueryWrapper> crossJoinIf(QueryWrapper table, boolean condition) {
|
public Joiner<QueryWrapper> crossJoinIf(QueryWrapper table, boolean when) {
|
||||||
return joining(Join.TYPE_CROSS, table, condition);
|
return joining(Join.TYPE_CROSS, table, when);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Joiner<QueryWrapper> join(String table) {
|
||||||
|
return joining(Join.TYPE_JOIN, table, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Joiner<QueryWrapper> join(String table, boolean when) {
|
||||||
|
return joining(Join.TYPE_JOIN, table, when);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Joiner<QueryWrapper> join(QueryWrapper table) {
|
||||||
|
return joining(Join.TYPE_JOIN, table, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Joiner<QueryWrapper> join(QueryWrapper table, boolean when) {
|
||||||
|
return joining(Join.TYPE_JOIN, table, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryWrapper union(QueryWrapper unionQuery) {
|
public QueryWrapper union(QueryWrapper unionQuery) {
|
||||||
@ -264,16 +280,33 @@ public class QueryWrapper extends BaseQueryWrapper<QueryWrapper> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public QueryWrapper forUpdate() {
|
||||||
|
addEndFragment("FOR UPDATE");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryWrapper forUpdateNoWait() {
|
||||||
|
addEndFragment("FOR UPDATE NOWAIT");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public QueryWrapper end(String sqlPart){
|
||||||
|
// addEndFragment(sqlPart);
|
||||||
|
// return this;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
protected Joiner<QueryWrapper> joining(String type, String table, boolean condition) {
|
protected Joiner<QueryWrapper> joining(String type, String table, boolean condition) {
|
||||||
Join join = new Join(type, table, condition);
|
Join join = new Join(type, table, condition);
|
||||||
addJoinTable(join.getQueryTable());
|
addJoinTable(join.getQueryTable());
|
||||||
return new Joiner<>(AddJoin(join), join);
|
return new Joiner<>(addJoin(join), join);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Joiner<QueryWrapper> joining(String type, QueryWrapper queryWrapper, boolean condition) {
|
protected Joiner<QueryWrapper> joining(String type, QueryWrapper queryWrapper, boolean condition) {
|
||||||
Join join = new Join(type, queryWrapper, condition);
|
Join join = new Join(type, queryWrapper, condition);
|
||||||
addJoinTable(join.getQueryTable());
|
addJoinTable(join.getQueryTable());
|
||||||
return new Joiner<>(AddJoin(join), join);
|
return new Joiner<>(addJoin(join), join);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -315,8 +348,14 @@ public class QueryWrapper extends BaseQueryWrapper<QueryWrapper> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public QueryWrapper orderBy(String... orderBys) {
|
public QueryWrapper orderBy(String... orderBys) {
|
||||||
|
if (orderBys == null || orderBys.length == 0) {
|
||||||
|
//ignore
|
||||||
|
return this;
|
||||||
|
}
|
||||||
for (String queryOrderBy : orderBys) {
|
for (String queryOrderBy : orderBys) {
|
||||||
addOrderBy(new StringQueryOrderBy(queryOrderBy));
|
if (StringUtil.isNotBlank(queryOrderBy)) {
|
||||||
|
addOrderBy(new StringQueryOrderBy(queryOrderBy));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -343,12 +382,33 @@ public class QueryWrapper extends BaseQueryWrapper<QueryWrapper> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public QueryWrapper hint(String hint) {
|
||||||
|
setHint(hint);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 queryWrapper 的参数
|
* 获取 queryWrapper 的参数
|
||||||
* 在构建 sql 的时候,需要保证 where 在 having 的前面
|
* 在构建 sql 的时候,需要保证 where 在 having 的前面
|
||||||
*/
|
*/
|
||||||
Object[] getValueArray() {
|
Object[] getValueArray() {
|
||||||
|
|
||||||
|
List<Object> columnValues = null;
|
||||||
|
List<QueryColumn> selectColumns = getSelectColumns();
|
||||||
|
if (CollectionUtil.isNotEmpty(selectColumns)) {
|
||||||
|
for (QueryColumn selectColumn : selectColumns) {
|
||||||
|
if (selectColumn instanceof HasParamsColumn) {
|
||||||
|
Object[] paramValues = ((HasParamsColumn) selectColumn).getParamValues();
|
||||||
|
if (ArrayUtil.isNotEmpty(paramValues)) {
|
||||||
|
if (columnValues == null) {
|
||||||
|
columnValues = new ArrayList<>();
|
||||||
|
}
|
||||||
|
columnValues.addAll(Arrays.asList(paramValues));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//select 子查询的参数:select * from (select ....)
|
//select 子查询的参数:select * from (select ....)
|
||||||
List<Object> tableValues = null;
|
List<Object> tableValues = null;
|
||||||
List<QueryTable> queryTables = getQueryTables();
|
List<QueryTable> queryTables = getQueryTables();
|
||||||
@ -404,7 +464,8 @@ public class QueryWrapper extends BaseQueryWrapper<QueryWrapper> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Object[] returnValues = tableValues == null ? WrapperUtil.NULL_PARA_ARRAY : tableValues.toArray();
|
Object[] returnValues = columnValues == null ? WrapperUtil.NULL_PARA_ARRAY : columnValues.toArray();
|
||||||
|
returnValues = tableValues != null ? ArrayUtil.concat(returnValues, tableValues.toArray()) : returnValues;
|
||||||
returnValues = joinValues != null ? ArrayUtil.concat(returnValues, joinValues.toArray()) : returnValues;
|
returnValues = joinValues != null ? ArrayUtil.concat(returnValues, joinValues.toArray()) : returnValues;
|
||||||
returnValues = ArrayUtil.concat(returnValues, paramValues);
|
returnValues = ArrayUtil.concat(returnValues, paramValues);
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* 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.query;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.dialect.IDialect;
|
||||||
|
import com.mybatisflex.core.util.SqlUtil;
|
||||||
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SelectQueryColumn extends QueryColumn implements HasParamsColumn {
|
||||||
|
|
||||||
|
private QueryWrapper queryWrapper;
|
||||||
|
|
||||||
|
public SelectQueryColumn(QueryWrapper queryWrapper) {
|
||||||
|
this.queryWrapper = queryWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SelectQueryColumn as(String alias) {
|
||||||
|
SqlUtil.keepColumnSafely(alias);
|
||||||
|
this.alias = alias;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryWrapper getQueryWrapper() {
|
||||||
|
return queryWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||||
|
String selectSql = dialect.forSelectByQuery(queryWrapper);
|
||||||
|
if (StringUtil.isNotBlank(selectSql) && StringUtil.isNotBlank(alias)) {
|
||||||
|
selectSql = "(" + selectSql + ") AS " + dialect.wrap(alias);
|
||||||
|
}
|
||||||
|
return selectSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String toConditionSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||||
|
return super.toConditionSql(queryTables, dialect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] getParamValues() {
|
||||||
|
return queryWrapper.getValueArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* 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.query;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.dialect.IDialect;
|
||||||
|
import com.mybatisflex.core.util.SqlUtil;
|
||||||
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库 聚合函数,例如 CONVERT(NVARCHAR(30), GETDATE(), 126) 等等
|
||||||
|
*/
|
||||||
|
public class StringFunctionQueryColumn extends QueryColumn {
|
||||||
|
|
||||||
|
protected String fnName;
|
||||||
|
protected List<String> params;
|
||||||
|
|
||||||
|
public StringFunctionQueryColumn(String fnName, String ...params) {
|
||||||
|
SqlUtil.keepColumnSafely(fnName);
|
||||||
|
this.fnName = fnName;
|
||||||
|
this.params = Arrays.asList(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getFnName() {
|
||||||
|
return fnName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFnName(String fnName) {
|
||||||
|
this.fnName = fnName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<String> getParams() {
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParams(List<String> params) {
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||||
|
String sql = StringUtil.join(", ",params);
|
||||||
|
return StringUtil.isBlank(sql) ? "" : fnName + "(" + sql + ")" + WrapperUtil.buildAsAlias(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String toConditionSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||||
|
String sql = StringUtil.join(", ",params);
|
||||||
|
return StringUtil.isBlank(sql) ? "" : fnName + "(" + sql + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryColumn as(String alias) {
|
||||||
|
SqlUtil.keepColumnSafely(alias);
|
||||||
|
this.alias = alias;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "StringFunctionQueryColumn{" +
|
||||||
|
"fnName='" + fnName + '\'' +
|
||||||
|
", params=" + params +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,6 +18,7 @@ package com.mybatisflex.core.query;
|
|||||||
|
|
||||||
import com.mybatisflex.core.util.ClassUtil;
|
import com.mybatisflex.core.util.ClassUtil;
|
||||||
import com.mybatisflex.core.util.CollectionUtil;
|
import com.mybatisflex.core.util.CollectionUtil;
|
||||||
|
import com.mybatisflex.core.util.EnumWrapper;
|
||||||
import com.mybatisflex.core.util.StringUtil;
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
@ -40,7 +41,7 @@ class WrapperUtil {
|
|||||||
while (condition != null) {
|
while (condition != null) {
|
||||||
if (condition.checkEffective()) {
|
if (condition.checkEffective()) {
|
||||||
if (condition instanceof Brackets) {
|
if (condition instanceof Brackets) {
|
||||||
List<QueryWrapper> childQueryWrapper = getChildSelect(((Brackets) condition).getChildCondition());
|
List<QueryWrapper> childQueryWrapper = getChildSelect(((Brackets) condition).getChild());
|
||||||
if (!childQueryWrapper.isEmpty()) {
|
if (!childQueryWrapper.isEmpty()) {
|
||||||
if (list == null) {
|
if (list == null) {
|
||||||
list = new ArrayList<>();
|
list = new ArrayList<>();
|
||||||
@ -82,14 +83,14 @@ class WrapperUtil {
|
|||||||
return NULL_PARA_ARRAY;
|
return NULL_PARA_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Object> paras = new ArrayList<>();
|
List<Object> params = new ArrayList<>();
|
||||||
getValues(condition, paras);
|
getValues(condition, params);
|
||||||
|
|
||||||
return paras.isEmpty() ? NULL_PARA_ARRAY : paras.toArray();
|
return params.isEmpty() ? NULL_PARA_ARRAY : params.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void getValues(QueryCondition condition, List<Object> paras) {
|
private static void getValues(QueryCondition condition, List<Object> params) {
|
||||||
if (condition == null) {
|
if (condition == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -98,29 +99,35 @@ class WrapperUtil {
|
|||||||
if (value == null
|
if (value == null
|
||||||
|| value instanceof QueryColumn
|
|| value instanceof QueryColumn
|
||||||
|| value instanceof RawValue) {
|
|| value instanceof RawValue) {
|
||||||
getValues(condition.next, paras);
|
getValues(condition.next, params);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.getClass().isArray()) {
|
addParam(params, value);
|
||||||
Object[] values = (Object[]) value;
|
getValues(condition.next, params);
|
||||||
for (Object object : values) {
|
}
|
||||||
if (object != null && ClassUtil.isArray(object.getClass())) {
|
|
||||||
for (int i = 0; i < Array.getLength(object); i++) {
|
private static void addParam(List<Object> paras, Object value) {
|
||||||
paras.add(Array.get(object, i));
|
if (value == null) {
|
||||||
}
|
paras.add(null);
|
||||||
} else {
|
} else if (ClassUtil.isArray(value.getClass())) {
|
||||||
paras.add(object);
|
for (int i = 0; i < Array.getLength(value); i++) {
|
||||||
}
|
addParam(paras, Array.get(value, i));
|
||||||
}
|
}
|
||||||
} else if (value instanceof QueryWrapper) {
|
} else if (value instanceof QueryWrapper) {
|
||||||
Object[] valueArray = ((QueryWrapper) value).getValueArray();
|
Object[] valueArray = ((QueryWrapper) value).getValueArray();
|
||||||
paras.addAll(Arrays.asList(valueArray));
|
paras.addAll(Arrays.asList(valueArray));
|
||||||
|
} else if (value.getClass().isEnum()) {
|
||||||
|
EnumWrapper enumWrapper = EnumWrapper.of(value.getClass());
|
||||||
|
if (enumWrapper.hasEnumValueAnnotation()) {
|
||||||
|
paras.add(enumWrapper.getEnumValue((Enum) value));
|
||||||
|
} else {
|
||||||
|
paras.add(value);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
paras.add(value);
|
paras.add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
getValues(condition.next, paras);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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.row;
|
||||||
|
|
||||||
|
public interface BatchArgsSetter {
|
||||||
|
|
||||||
|
Object[] NONE_ARGS = new Object[0];
|
||||||
|
|
||||||
|
int getBatchSize();
|
||||||
|
|
||||||
|
Object[] getSqlArgs(int index);
|
||||||
|
}
|
||||||
@ -24,13 +24,16 @@ import com.mybatisflex.core.query.QueryTable;
|
|||||||
import com.mybatisflex.core.query.QueryWrapper;
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
import com.mybatisflex.core.transaction.Propagation;
|
import com.mybatisflex.core.transaction.Propagation;
|
||||||
import com.mybatisflex.core.transaction.TransactionalManager;
|
import com.mybatisflex.core.transaction.TransactionalManager;
|
||||||
|
import com.mybatisflex.core.util.CollectionUtil;
|
||||||
import org.apache.ibatis.session.SqlSessionFactory;
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
import org.apache.ibatis.util.MapUtil;
|
import org.apache.ibatis.util.MapUtil;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,7 +101,11 @@ public class Db {
|
|||||||
* @param batchSize 每次提交的数据量
|
* @param batchSize 每次提交的数据量
|
||||||
*/
|
*/
|
||||||
public static int[] insertBatch(String tableName, Collection<Row> rows, int batchSize) {
|
public static int[] insertBatch(String tableName, Collection<Row> rows, int batchSize) {
|
||||||
return invoker().insertBatch(tableName, rows, batchSize);
|
List<Row> list = CollectionUtil.toList(rows);
|
||||||
|
return executeBatch(rows.size(), batchSize, RowMapper.class, (mapper, index) -> {
|
||||||
|
Row row = list.get(index);
|
||||||
|
mapper.insert(tableName, row);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,6 +203,18 @@ public class Db {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param sql
|
||||||
|
* @param batchArgsSetter
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int[] updateBatch(String sql, BatchArgsSetter batchArgsSetter) {
|
||||||
|
int batchSize = batchArgsSetter.getBatchSize();
|
||||||
|
return executeBatch(batchSize, batchSize, RowMapper.class
|
||||||
|
, (mapper, index) -> mapper.updateBySql(sql, batchArgsSetter.getSqlArgs(index)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 id 来更新数据
|
* 根据 id 来更新数据
|
||||||
*
|
*
|
||||||
@ -252,6 +271,46 @@ public class Db {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主键来批量更新数据
|
||||||
|
*
|
||||||
|
* @param entities 实体
|
||||||
|
* @param batchSize 批次大小
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static <T> int updateEntitiesBatch(Collection<T> entities, int batchSize) {
|
||||||
|
List<T> list = CollectionUtil.toList(entities);
|
||||||
|
return Arrays.stream(executeBatch(list.size(), batchSize, RowMapper.class, (mapper, index) -> {
|
||||||
|
T entity = list.get(index);
|
||||||
|
mapper.updateEntity(entity);
|
||||||
|
})).sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主键来批量更新数据
|
||||||
|
*
|
||||||
|
* @param entities 实体
|
||||||
|
* @return int 影响行数
|
||||||
|
*/
|
||||||
|
public static <T> int updateEntitiesBatch(Collection<T> entities) {
|
||||||
|
return updateEntitiesBatch(entities, RowMapper.DEFAULT_BATCH_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量执行工具方法
|
||||||
|
*
|
||||||
|
* @param totalSize 执行总量
|
||||||
|
* @param batchSize 每一批次的数据量
|
||||||
|
* @param mapperClass 通过那个 Mapper 来执行
|
||||||
|
* @param consumer 执行内容
|
||||||
|
* @param <M> Mapper
|
||||||
|
* @return 执行影响的行数
|
||||||
|
*/
|
||||||
|
public static <M> int[] executeBatch(int totalSize, int batchSize, Class<M> mapperClass, BiConsumer<M, Integer> consumer) {
|
||||||
|
return invoker().executeBatch(totalSize, batchSize, mapperClass, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 sql 来查询 1 条数据
|
* 根据 sql 来查询 1 条数据
|
||||||
*
|
*
|
||||||
@ -431,6 +490,29 @@ public class Db {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 queryWrapper 查询内容,数据返回的应该只有 1 行 1 列
|
||||||
|
*
|
||||||
|
* @param tableName 表名
|
||||||
|
* @param queryWrapper query 封装
|
||||||
|
* @return 数据内容
|
||||||
|
*/
|
||||||
|
public static Object selectObject(String tableName, QueryWrapper queryWrapper) {
|
||||||
|
return invoker().selectObjectByQuery(tableName, queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 queryWrapper 查询内容,数据返回的应该只有 1 行 1 列
|
||||||
|
*
|
||||||
|
* @param queryWrapper query 封装
|
||||||
|
* @return 数据内容
|
||||||
|
*/
|
||||||
|
public static Object selectObject(QueryWrapper queryWrapper) {
|
||||||
|
return invoker().selectObjectByQuery(null, queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询某列内容,数据返回应该有 多行 1 列
|
* 查询某列内容,数据返回应该有 多行 1 列
|
||||||
*
|
*
|
||||||
@ -442,6 +524,29 @@ public class Db {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 queryWrapper 查询内容,数据返回的应该只有 1 行 1 列
|
||||||
|
*
|
||||||
|
* @param tableName 表名
|
||||||
|
* @param queryWrapper query 封装
|
||||||
|
* @return 数据内容
|
||||||
|
*/
|
||||||
|
public static Object selectObjectList(String tableName, QueryWrapper queryWrapper) {
|
||||||
|
return invoker().selectObjectListByQuery(tableName, queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 queryWrapper 查询内容,数据返回的应该只有 1 行 1 列
|
||||||
|
*
|
||||||
|
* @param queryWrapper query 封装
|
||||||
|
* @return 数据内容
|
||||||
|
*/
|
||||||
|
public static Object selectObjectList(QueryWrapper queryWrapper) {
|
||||||
|
return invoker().selectObjectListByQuery(null, queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查收 count 数据,一般用于 select count(*)...
|
* 查收 count 数据,一般用于 select count(*)...
|
||||||
* 或者返回的内容是一行1列,且是数值类型的也可以用此方法
|
* 或者返回的内容是一行1列,且是数值类型的也可以用此方法
|
||||||
@ -485,7 +590,7 @@ public class Db {
|
|||||||
public static long selectCountByQuery(QueryWrapper queryWrapper) {
|
public static long selectCountByQuery(QueryWrapper queryWrapper) {
|
||||||
List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
|
List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
|
||||||
if (queryTables == null || queryTables.isEmpty()) {
|
if (queryTables == null || queryTables.isEmpty()) {
|
||||||
throw FlexExceptions.wrap("table must not be null or empty in Db.selectCountByQuery");
|
throw FlexExceptions.wrap("Query tables must not be null or empty in Db.selectCountByQuery");
|
||||||
}
|
}
|
||||||
return invoker().selectCountByQuery(null, queryWrapper);
|
return invoker().selectCountByQuery(null, queryWrapper);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,20 +19,21 @@ import com.mybatisflex.core.FlexConsts;
|
|||||||
import com.mybatisflex.core.exception.FlexExceptions;
|
import com.mybatisflex.core.exception.FlexExceptions;
|
||||||
import com.mybatisflex.core.paginate.Page;
|
import com.mybatisflex.core.paginate.Page;
|
||||||
import com.mybatisflex.core.provider.RowSqlProvider;
|
import com.mybatisflex.core.provider.RowSqlProvider;
|
||||||
import com.mybatisflex.core.query.CPI;
|
import com.mybatisflex.core.query.*;
|
||||||
import com.mybatisflex.core.query.QueryColumn;
|
import com.mybatisflex.core.util.CollectionUtil;
|
||||||
import com.mybatisflex.core.query.QueryWrapper;
|
|
||||||
import com.mybatisflex.core.util.StringUtil;
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
import org.apache.ibatis.annotations.*;
|
import org.apache.ibatis.annotations.*;
|
||||||
import org.apache.ibatis.exceptions.TooManyResultsException;
|
import org.apache.ibatis.exceptions.TooManyResultsException;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import static com.mybatisflex.core.query.QueryMethods.count;
|
||||||
|
|
||||||
|
|
||||||
public interface RowMapper {
|
public interface RowMapper {
|
||||||
|
|
||||||
|
int DEFAULT_BATCH_SIZE = 1000;
|
||||||
|
|
||||||
//////insert //////
|
//////insert //////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -92,7 +93,7 @@ public interface RowMapper {
|
|||||||
* @param row id 和 值的数据,可以通过 {@link Row#ofKey(String, Object)} 来创建
|
* @param row id 和 值的数据,可以通过 {@link Row#ofKey(String, Object)} 来创建
|
||||||
* @return 执行影响的行数
|
* @return 执行影响的行数
|
||||||
*/
|
*/
|
||||||
default int deleteById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROW) Row row) {
|
default int deleteById(String tableName, Row row) {
|
||||||
return deleteById(tableName, StringUtil.join(",", row.obtainsPrimaryKeyStrings()), row.obtainsPrimaryValues());
|
return deleteById(tableName, StringUtil.join(",", row.obtainsPrimaryKeyStrings()), row.obtainsPrimaryValues());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,6 +187,17 @@ public interface RowMapper {
|
|||||||
@UpdateProvider(value = RowSqlProvider.class, method = "updateBatchById")
|
@UpdateProvider(value = RowSqlProvider.class, method = "updateBatchById")
|
||||||
int updateBatchById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROWS) List<Row> rows);
|
int updateBatchById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROWS) List<Row> rows);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新 entity,主要用于进行批量更新的场景
|
||||||
|
*
|
||||||
|
* @param entity 实体类
|
||||||
|
* @see RowSqlProvider#updateEntity(Map)
|
||||||
|
* @see Db#updateEntitiesBatch(Collection, int)
|
||||||
|
*/
|
||||||
|
@UpdateProvider(value = RowSqlProvider.class, method = "updateEntity")
|
||||||
|
int updateEntity(@Param(FlexConsts.ENTITY) Object entity);
|
||||||
|
|
||||||
///////select /////
|
///////select /////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -278,7 +290,7 @@ public interface RowMapper {
|
|||||||
* @param tableName 表名
|
* @param tableName 表名
|
||||||
* @return row 列表
|
* @return row 列表
|
||||||
*/
|
*/
|
||||||
default List<Row> selectAll(@Param(FlexConsts.TABLE_NAME) String tableName) {
|
default List<Row> selectAll(String tableName) {
|
||||||
return selectListByQuery(tableName, QueryWrapper.create());
|
return selectListByQuery(tableName, QueryWrapper.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,15 +337,58 @@ public interface RowMapper {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 queryWrapper 来查询数量
|
* 根据 queryWrapper 1 条数据
|
||||||
|
* queryWrapper 执行的结果应该只有 1 列,例如 QueryWrapper.create().select(ACCOUNT.id).where...
|
||||||
*
|
*
|
||||||
* @param tableName 表名
|
* @param tableName 表名
|
||||||
* @param queryWrapper queryWrapper
|
* @param queryWrapper queryWrapper
|
||||||
* @return 数量
|
* @return 数据
|
||||||
* @see RowSqlProvider#selectCountByQuery(Map)
|
|
||||||
*/
|
*/
|
||||||
@SelectProvider(value = RowSqlProvider.class, method = "selectCountByQuery")
|
default Object selectObjectByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||||
long selectCountByQuery(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
queryWrapper.limit(1);
|
||||||
|
List<Object> objects = selectObjectListByQuery(tableName, queryWrapper);
|
||||||
|
if (objects == null || objects.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return objects.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 queryWrapper 来查询数据列表
|
||||||
|
* queryWrapper 执行的结果应该只有 1 列,例如 QueryWrapper.create().select(ACCOUNT.id).where...
|
||||||
|
*
|
||||||
|
* @param queryWrapper 查询包装器
|
||||||
|
* @return 数据列表
|
||||||
|
* @see RowSqlProvider#selectObjectByQuery(Map)
|
||||||
|
*/
|
||||||
|
@SelectProvider(type = RowSqlProvider.class, method = "selectObjectByQuery")
|
||||||
|
List<Object> selectObjectListByQuery(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询数据量
|
||||||
|
*
|
||||||
|
* @param tableName 表名
|
||||||
|
* @param queryWrapper 查询包装器
|
||||||
|
* @return 数据量
|
||||||
|
*/
|
||||||
|
default long selectCountByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||||
|
List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
|
||||||
|
if (CollectionUtil.isEmpty(selectColumns)) {
|
||||||
|
queryWrapper.select(count());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Object> objects = selectObjectListByQuery(tableName, queryWrapper);
|
||||||
|
Object object = objects == null || objects.isEmpty() ? null : objects.get(0);
|
||||||
|
if (object == null) {
|
||||||
|
return 0;
|
||||||
|
} else if (object instanceof Number) {
|
||||||
|
return ((Number) object).longValue();
|
||||||
|
} else {
|
||||||
|
throw FlexExceptions.wrap("selectCountByQuery error, Can not get number value for queryWrapper: %s", queryWrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -346,15 +401,59 @@ public interface RowMapper {
|
|||||||
*/
|
*/
|
||||||
default Page<Row> paginate(String tableName, Page<Row> page, QueryWrapper queryWrapper) {
|
default Page<Row> paginate(String tableName, Page<Row> page, QueryWrapper queryWrapper) {
|
||||||
|
|
||||||
List<QueryColumn> groupByColumns = CPI.getGroupByColumns(queryWrapper);
|
CPI.setFromIfNecessary(queryWrapper, tableName);
|
||||||
|
|
||||||
|
List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
|
||||||
|
|
||||||
|
List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
|
||||||
|
|
||||||
|
List<Join> joins = CPI.getJoins(queryWrapper);
|
||||||
|
boolean removedJoins = true;
|
||||||
|
|
||||||
// 只有 totalRow 小于 0 的时候才会去查询总量
|
// 只有 totalRow 小于 0 的时候才会去查询总量
|
||||||
// 这样方便用户做总数缓存,而非每次都要去查询总量
|
// 这样方便用户做总数缓存,而非每次都要去查询总量
|
||||||
// 一般的分页场景中,只有第一页的时候有必要去查询总量,第二页以后是不需要的
|
// 一般的分页场景中,只有第一页的时候有必要去查询总量,第二页以后是不需要的
|
||||||
if (page.getTotalRow() < 0) {
|
if (page.getTotalRow() < 0) {
|
||||||
|
|
||||||
//清除group by 去查询数据
|
//移除 seelct
|
||||||
CPI.setGroupByColumns(queryWrapper, null);
|
CPI.setSelectColumns(queryWrapper, Collections.singletonList(count().as("total")));
|
||||||
|
|
||||||
|
//移除 OrderBy
|
||||||
|
if (CollectionUtil.isNotEmpty(orderBys)) {
|
||||||
|
CPI.setOrderBys(queryWrapper, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
//移除 left join
|
||||||
|
if (joins != null && !joins.isEmpty()) {
|
||||||
|
for (Join join : joins) {
|
||||||
|
if (!Join.TYPE_LEFT.equals(CPI.getJoinType(join))) {
|
||||||
|
removedJoins = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
removedJoins = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedJoins) {
|
||||||
|
List<String> joinTables = new ArrayList<>();
|
||||||
|
joins.forEach(join -> {
|
||||||
|
QueryTable joinQueryTable = CPI.getJoinQueryTable(join);
|
||||||
|
if (joinQueryTable != null && StringUtil.isNotBlank(joinQueryTable.getName())) {
|
||||||
|
joinTables.add(joinQueryTable.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QueryCondition where = CPI.getWhereQueryCondition(queryWrapper);
|
||||||
|
if (CPI.containsTable(where, CollectionUtil.toArrayString(joinTables))) {
|
||||||
|
removedJoins = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedJoins) {
|
||||||
|
CPI.setJoins(queryWrapper, null);
|
||||||
|
}
|
||||||
|
|
||||||
long count = selectCountByQuery(tableName, queryWrapper);
|
long count = selectCountByQuery(tableName, queryWrapper);
|
||||||
page.setTotalRow(count);
|
page.setTotalRow(count);
|
||||||
}
|
}
|
||||||
@ -363,14 +462,26 @@ public interface RowMapper {
|
|||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
//恢复数量查询清除的 groupBy
|
//重置 selectColumns
|
||||||
CPI.setGroupByColumns(queryWrapper, groupByColumns);
|
CPI.setSelectColumns(queryWrapper, selectColumns);
|
||||||
|
|
||||||
|
//重置 orderBys
|
||||||
|
if (CollectionUtil.isNotEmpty(orderBys)) {
|
||||||
|
CPI.setOrderBys(queryWrapper, orderBys);
|
||||||
|
}
|
||||||
|
|
||||||
|
//重置 join
|
||||||
|
if (removedJoins) {
|
||||||
|
CPI.setJoins(queryWrapper, joins);
|
||||||
|
}
|
||||||
|
|
||||||
int offset = page.getPageSize() * (page.getPageNumber() - 1);
|
int offset = page.getPageSize() * (page.getPageNumber() - 1);
|
||||||
queryWrapper.limit(offset, page.getPageSize());
|
queryWrapper.limit(offset, page.getPageSize());
|
||||||
|
|
||||||
List<Row> records = selectListByQuery(tableName, queryWrapper);
|
List<Row> records = selectListByQuery(tableName, queryWrapper);
|
||||||
page.setRecords(records);
|
page.setRecords(records);
|
||||||
return page;
|
return page;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import org.apache.ibatis.session.SqlSessionFactory;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class RowMapperInvoker {
|
public class RowMapperInvoker {
|
||||||
@ -50,41 +51,6 @@ public class RowMapperInvoker {
|
|||||||
return execute(mapper -> mapper.insertBySql(sql, args));
|
return execute(mapper -> mapper.insertBySql(sql, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int[] insertBatch(String tableName, Collection<Row> rows, int batchSize) {
|
|
||||||
int[] results = new int[rows.size()];
|
|
||||||
try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, true)) {
|
|
||||||
RowMapper mapper = sqlSession.getMapper(RowMapper.class);
|
|
||||||
int counter = 0;
|
|
||||||
int resultsPos = 0;
|
|
||||||
for (Row row : rows) {
|
|
||||||
if (++counter > batchSize) {
|
|
||||||
counter = 0;
|
|
||||||
List<BatchResult> batchResults = sqlSession.flushStatements();
|
|
||||||
for (BatchResult batchResult : batchResults) {
|
|
||||||
int[] updateCounts = batchResult.getUpdateCounts();
|
|
||||||
for (int updateCount : updateCounts) {
|
|
||||||
results[resultsPos++] = updateCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mapper.insert(tableName, row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counter != 0) {
|
|
||||||
List<BatchResult> batchResults = sqlSession.flushStatements();
|
|
||||||
for (BatchResult batchResult : batchResults) {
|
|
||||||
int[] updateCounts = batchResult.getUpdateCounts();
|
|
||||||
for (int updateCount : updateCounts) {
|
|
||||||
results[resultsPos++] = updateCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int insertBatchWithFirstRowColumns(String tableName, List<Row> rows) {
|
public int insertBatchWithFirstRowColumns(String tableName, List<Row> rows) {
|
||||||
return execute(mapper -> mapper.insertBatchWithFirstRowColumns(tableName, rows));
|
return execute(mapper -> mapper.insertBatchWithFirstRowColumns(tableName, rows));
|
||||||
}
|
}
|
||||||
@ -114,6 +80,41 @@ public class RowMapperInvoker {
|
|||||||
return execute(mapper -> mapper.updateBySql(sql, args));
|
return execute(mapper -> mapper.updateBySql(sql, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public <M> int[] executeBatch(int totalSize, int batchSize, Class<M> mapperClass, BiConsumer<M, Integer> consumer) {
|
||||||
|
int[] results = new int[totalSize];
|
||||||
|
try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, true)) {
|
||||||
|
M mapper = sqlSession.getMapper(mapperClass);
|
||||||
|
int counter = 0;
|
||||||
|
int resultsPos = 0;
|
||||||
|
for (int i = 0; i < totalSize; i++) {
|
||||||
|
consumer.accept(mapper, i);
|
||||||
|
if (++counter == batchSize) {
|
||||||
|
counter = 0;
|
||||||
|
List<BatchResult> batchResults = sqlSession.flushStatements();
|
||||||
|
for (BatchResult batchResult : batchResults) {
|
||||||
|
int[] updateCounts = batchResult.getUpdateCounts();
|
||||||
|
for (int updateCount : updateCounts) {
|
||||||
|
results[resultsPos++] = updateCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter != 0) {
|
||||||
|
List<BatchResult> batchResults = sqlSession.flushStatements();
|
||||||
|
for (BatchResult batchResult : batchResults) {
|
||||||
|
int[] updateCounts = batchResult.getUpdateCounts();
|
||||||
|
for (int updateCount : updateCounts) {
|
||||||
|
results[resultsPos++] = updateCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
public int updateById(String tableName, Row row) {
|
public int updateById(String tableName, Row row) {
|
||||||
return execute(mapper -> mapper.updateById(tableName, row));
|
return execute(mapper -> mapper.updateById(tableName, row));
|
||||||
}
|
}
|
||||||
@ -154,6 +155,14 @@ public class RowMapperInvoker {
|
|||||||
return execute(mapper -> mapper.selectAll(tableName));
|
return execute(mapper -> mapper.selectAll(tableName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object selectObjectByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||||
|
return execute(mapper -> mapper.selectObjectByQuery(tableName, queryWrapper));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> selectObjectListByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||||
|
return execute(mapper -> mapper.selectObjectListByQuery(tableName, queryWrapper));
|
||||||
|
}
|
||||||
|
|
||||||
public Object selectObject(String sql, Object... args) {
|
public Object selectObject(String sql, Object... args) {
|
||||||
return execute(mapper -> mapper.selectObject(sql, args));
|
return execute(mapper -> mapper.selectObject(sql, args));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,38 +13,40 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.mybatisflex.spring.service;
|
package com.mybatisflex.core.service;
|
||||||
|
|
||||||
import com.mybatisflex.core.BaseMapper;
|
import com.mybatisflex.core.BaseMapper;
|
||||||
import com.mybatisflex.core.paginate.Page;
|
import com.mybatisflex.core.paginate.Page;
|
||||||
import com.mybatisflex.core.query.QueryCondition;
|
import com.mybatisflex.core.query.QueryCondition;
|
||||||
import com.mybatisflex.core.query.QueryWrapper;
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
|
import com.mybatisflex.core.row.Db;
|
||||||
|
import com.mybatisflex.core.row.RowMapper;
|
||||||
|
import com.mybatisflex.core.util.ClassUtil;
|
||||||
import com.mybatisflex.core.util.CollectionUtil;
|
import com.mybatisflex.core.util.CollectionUtil;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import com.mybatisflex.core.util.SqlUtil;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static com.mybatisflex.core.util.SqlUtil.retBool;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 由 Mybatis-Flex 提供的顶级增强 Service 接口。
|
* 由 Mybatis-Flex 提供的顶级增强 Service 接口。
|
||||||
*
|
*
|
||||||
|
* @param <T> 实体类(Entity)类型
|
||||||
* @author 王帅
|
* @author 王帅
|
||||||
* @since 2023-05-01
|
* @since 2023-05-01
|
||||||
* @param <T> 实体类(Entity)类型
|
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public interface IService<T> {
|
public interface IService<T> {
|
||||||
|
|
||||||
|
// ===== 保存(增)操作 =====
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取对应实体类(Entity)的基础映射类(BaseMapper)。
|
* 获取对应实体类(Entity)的基础映射类(BaseMapper)。
|
||||||
*
|
*
|
||||||
* @return 基础映射类(BaseMapper)
|
* @return 基础映射类(BaseMapper)
|
||||||
*/
|
*/
|
||||||
BaseMapper<T> getBaseMapper();
|
BaseMapper<T> getMapper();
|
||||||
|
|
||||||
// ===== 保存(增)操作 =====
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存实体类对象数据。
|
* 保存实体类对象数据。
|
||||||
@ -55,7 +57,7 @@ public interface IService<T> {
|
|||||||
* {@code null} 字段的数据,使数据库配置的默认值生效。
|
* {@code null} 字段的数据,使数据库配置的默认值生效。
|
||||||
*/
|
*/
|
||||||
default boolean save(T entity) {
|
default boolean save(T entity) {
|
||||||
return retBool(getBaseMapper().insertSelective(entity));
|
return SqlUtil.toBool(getMapper().insertSelective(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,7 +68,7 @@ public interface IService<T> {
|
|||||||
* @apiNote 如果实体类对象主键有值,则更新数据,若没有值,则保存数据。
|
* @apiNote 如果实体类对象主键有值,则更新数据,若没有值,则保存数据。
|
||||||
*/
|
*/
|
||||||
default boolean saveOrUpdate(T entity) {
|
default boolean saveOrUpdate(T entity) {
|
||||||
return retBool(getBaseMapper().insertOrUpdate(entity));
|
return SqlUtil.toBool(getMapper().insertOrUpdate(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,25 +77,23 @@ public interface IService<T> {
|
|||||||
* @param entities 实体类对象
|
* @param entities 实体类对象
|
||||||
* @return {@code true} 保存成功,{@code false} 保存失败。
|
* @return {@code true} 保存成功,{@code false} 保存失败。
|
||||||
*/
|
*/
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
default boolean saveBatch(Collection<T> entities) {
|
default boolean saveBatch(Collection<T> entities) {
|
||||||
return retBool(getBaseMapper().insertBatch(new ArrayList<>(entities)));
|
return SqlUtil.toBool(getMapper().insertBatch(new ArrayList<>(entities)));
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量保存实体类对象数据。
|
|
||||||
*
|
|
||||||
* @param entities 实体类对象
|
|
||||||
* @param size 每次保存切分的数量
|
|
||||||
* @return {@code true} 保存成功,{@code false} 保存失败。
|
|
||||||
*/
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
default boolean saveBatch(Collection<T> entities, int size) {
|
|
||||||
return retBool(getBaseMapper().insertBatch(new ArrayList<>(entities), size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 删除(删)操作 =====
|
// ===== 删除(删)操作 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存实体类对象数据。
|
||||||
|
*
|
||||||
|
* @param entities 实体类对象
|
||||||
|
* @param size 每次保存切分的数量
|
||||||
|
* @return {@code true} 保存成功,{@code false} 保存失败。
|
||||||
|
*/
|
||||||
|
default boolean saveBatch(Collection<T> entities, int size) {
|
||||||
|
return SqlUtil.toBool(getMapper().insertBatch(CollectionUtil.toList(entities), size));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据查询条件删除数据。
|
* 根据查询条件删除数据。
|
||||||
*
|
*
|
||||||
@ -101,17 +101,17 @@ public interface IService<T> {
|
|||||||
* @return {@code true} 删除成功,{@code false} 删除失败。
|
* @return {@code true} 删除成功,{@code false} 删除失败。
|
||||||
*/
|
*/
|
||||||
default boolean remove(QueryWrapper query) {
|
default boolean remove(QueryWrapper query) {
|
||||||
return retBool(getBaseMapper().deleteByQuery(query));
|
return SqlUtil.toBool(getMapper().deleteByQuery(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据查询条件删除数据。
|
* 根据查询条件删除数据。
|
||||||
*
|
*
|
||||||
* @param query 查询条件
|
* @param condition 查询条件
|
||||||
* @return {@code true} 删除成功,{@code false} 删除失败。
|
* @return {@code true} 删除成功,{@code false} 删除失败。
|
||||||
*/
|
*/
|
||||||
default boolean remove(QueryCondition query) {
|
default boolean remove(QueryCondition condition) {
|
||||||
return retBool(getBaseMapper().deleteByCondition(query));
|
return SqlUtil.toBool(getMapper().deleteByCondition(condition));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,7 +121,7 @@ public interface IService<T> {
|
|||||||
* @return {@code true} 删除成功,{@code false} 删除失败。
|
* @return {@code true} 删除成功,{@code false} 删除失败。
|
||||||
*/
|
*/
|
||||||
default boolean removeById(Serializable id) {
|
default boolean removeById(Serializable id) {
|
||||||
return retBool(getBaseMapper().deleteById(id));
|
return SqlUtil.toBool(getMapper().deleteById(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,14 +130,15 @@ public interface IService<T> {
|
|||||||
* @param ids 数据主键
|
* @param ids 数据主键
|
||||||
* @return {@code true} 删除成功,{@code false} 删除失败。
|
* @return {@code true} 删除成功,{@code false} 删除失败。
|
||||||
*/
|
*/
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
default boolean removeByIds(Collection<? extends Serializable> ids) {
|
default boolean removeByIds(Collection<? extends Serializable> ids) {
|
||||||
if (CollectionUtil.isEmpty(ids)) {
|
if (CollectionUtil.isEmpty(ids)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return retBool(getBaseMapper().deleteBatchByIds(ids));
|
return SqlUtil.toBool(getMapper().deleteBatchByIds(ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== 更新(改)操作 =====
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 {@link Map} 构建查询条件删除数据。
|
* 根据 {@link Map} 构建查询条件删除数据。
|
||||||
*
|
*
|
||||||
@ -145,31 +146,57 @@ public interface IService<T> {
|
|||||||
* @return {@code true} 删除成功,{@code false} 删除失败。
|
* @return {@code true} 删除成功,{@code false} 删除失败。
|
||||||
*/
|
*/
|
||||||
default boolean removeByMap(Map<String, Object> query) {
|
default boolean removeByMap(Map<String, Object> query) {
|
||||||
return retBool(getBaseMapper().deleteByMap(query));
|
return SqlUtil.toBool(getMapper().deleteByMap(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 更新(改)操作 =====
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据查询条件更新数据。
|
* 根据查询条件更新数据。
|
||||||
*
|
*
|
||||||
* @param entity 实体类对象
|
* @param entity 实体类对象
|
||||||
* @param query 查询条件
|
* @param query 查询条件
|
||||||
* @return {@code true} 更新成功,{@code false} 更新失败。
|
* @return {@code true} 更新成功,{@code false} 更新失败。
|
||||||
*/
|
*/
|
||||||
default boolean update(T entity, QueryWrapper query) {
|
default boolean update(T entity, QueryWrapper query) {
|
||||||
return retBool(getBaseMapper().updateByQuery(entity, query));
|
return SqlUtil.toBool(getMapper().updateByQuery(entity, query));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据查询条件更新数据。
|
* 根据查询条件更新数据。
|
||||||
*
|
*
|
||||||
* @param entity 实体类对象
|
* @param entity 实体类对象
|
||||||
* @param query 查询条件
|
* @param condition 查询条件
|
||||||
* @return {@code true} 更新成功,{@code false} 更新失败。
|
* @return {@code true} 更新成功,{@code false} 更新失败。
|
||||||
*/
|
*/
|
||||||
default boolean update(T entity, QueryCondition query) {
|
default boolean update(T entity, QueryCondition condition) {
|
||||||
return retBool(getBaseMapper().updateByCondition(entity, query));
|
return SqlUtil.toBool(getMapper().updateByCondition(entity, condition));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 id 批量更新数据
|
||||||
|
*
|
||||||
|
* @param entities 实体类对象集合
|
||||||
|
* @return boolean {@code true} 更新成功,{@code false} 更新失败。
|
||||||
|
*/
|
||||||
|
default boolean updateBatch(Collection<T> entities) {
|
||||||
|
return updateBatch(entities, RowMapper.DEFAULT_BATCH_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 id 批量更新数据
|
||||||
|
*
|
||||||
|
* @param entities 实体类对象集合
|
||||||
|
* @param batchSize 每批次更新数量
|
||||||
|
* @return boolean {@code true} 更新成功,{@code false} 更新失败。
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
default boolean updateBatch(Collection<T> entities, int batchSize) {
|
||||||
|
return Db.tx(() -> {
|
||||||
|
final List<T> entityList = CollectionUtil.toList(entities);
|
||||||
|
|
||||||
|
// BaseMapper 是经过 Mybatis 动态代理处理过的对象,需要获取原始 BaseMapper 类型
|
||||||
|
final Class<BaseMapper<T>> usefulClass = (Class<BaseMapper<T>>) ClassUtil.getUsefulClass(getMapper().getClass());
|
||||||
|
return SqlUtil.toBool(Arrays.stream(Db.executeBatch(entityList.size(), batchSize, usefulClass, (mapper, index) -> mapper.update(entityList.get(index)))).sum());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,18 +206,18 @@ public interface IService<T> {
|
|||||||
* @return {@code true} 更新成功,{@code false} 更新失败。
|
* @return {@code true} 更新成功,{@code false} 更新失败。
|
||||||
*/
|
*/
|
||||||
default boolean updateById(T entity) {
|
default boolean updateById(T entity) {
|
||||||
return retBool(getBaseMapper().update(entity));
|
return SqlUtil.toBool(getMapper().update(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 {@link Map} 构建查询条件更新数据。
|
* 根据 {@link Map} 构建查询条件更新数据。
|
||||||
*
|
*
|
||||||
* @param entity 实体类对象
|
* @param entity 实体类对象
|
||||||
* @param query 查询条件
|
* @param query 查询条件
|
||||||
* @return {@code true} 更新成功,{@code false} 更新失败。
|
* @return {@code true} 更新成功,{@code false} 更新失败。
|
||||||
*/
|
*/
|
||||||
default boolean updateByMap(T entity, Map<String, Object> query) {
|
default boolean updateByMap(T entity, Map<String, Object> query) {
|
||||||
return retBool(getBaseMapper().updateByMap(entity, query));
|
return SqlUtil.toBool(getMapper().updateByMap(entity, query));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 查询(查)操作 =====
|
// ===== 查询(查)操作 =====
|
||||||
@ -202,7 +229,7 @@ public interface IService<T> {
|
|||||||
* @return 查询结果数据
|
* @return 查询结果数据
|
||||||
*/
|
*/
|
||||||
default T getById(Serializable id) {
|
default T getById(Serializable id) {
|
||||||
return getBaseMapper().selectOneById(id);
|
return getMapper().selectOneById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,7 +250,19 @@ public interface IService<T> {
|
|||||||
* @return 查询结果数据
|
* @return 查询结果数据
|
||||||
*/
|
*/
|
||||||
default T getOne(QueryWrapper query) {
|
default T getOne(QueryWrapper query) {
|
||||||
return getBaseMapper().selectOneByQuery(query);
|
return getMapper().selectOneByQuery(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据查询条件查询一条数据,并通过 asType 进行接收
|
||||||
|
*
|
||||||
|
* @param query 查询条件
|
||||||
|
* @param asType 接收的数据类型
|
||||||
|
* @return 查询结果数据
|
||||||
|
*/
|
||||||
|
default <R> R getOneAs(QueryWrapper query, Class<R> asType) {
|
||||||
|
return getMapper().selectOneByQueryAs(query, asType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -237,25 +276,38 @@ public interface IService<T> {
|
|||||||
return Optional.ofNullable(getOne(query));
|
return Optional.ofNullable(getOne(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据查询条件查询一条数据。
|
* 根据查询条件查询一条数据。
|
||||||
*
|
*
|
||||||
* @param query 查询条件
|
* @param query 查询条件
|
||||||
|
* @param asType 接收的数据类型
|
||||||
* @return 查询结果数据
|
* @return 查询结果数据
|
||||||
|
* @apiNote 该方法会将查询结果封装为 {@link Optional} 类进行返回,方便链式操作。
|
||||||
*/
|
*/
|
||||||
default T getOne(QueryCondition query) {
|
default <R> Optional<R> getOneOptAs(QueryWrapper query, Class<R> asType) {
|
||||||
return getBaseMapper().selectOneByCondition(query);
|
return Optional.ofNullable(getOneAs(query, asType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据查询条件查询一条数据。
|
* 根据查询条件查询一条数据。
|
||||||
*
|
*
|
||||||
* @param query 查询条件
|
* @param condition 查询条件
|
||||||
|
* @return 查询结果数据
|
||||||
|
*/
|
||||||
|
default T getOne(QueryCondition condition) {
|
||||||
|
return getMapper().selectOneByCondition(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据查询条件查询一条数据。
|
||||||
|
*
|
||||||
|
* @param condition 查询条件
|
||||||
* @return 查询结果数据
|
* @return 查询结果数据
|
||||||
* @apiNote 该方法会将查询结果封装为 {@link Optional} 类进行返回,方便链式操作。
|
* @apiNote 该方法会将查询结果封装为 {@link Optional} 类进行返回,方便链式操作。
|
||||||
*/
|
*/
|
||||||
default Optional<T> getOneOpt(QueryCondition query) {
|
default Optional<T> getOneOpt(QueryCondition condition) {
|
||||||
return Optional.ofNullable(getOne(query));
|
return Optional.ofNullable(getOne(condition));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -264,9 +316,30 @@ public interface IService<T> {
|
|||||||
* @return 所有数据
|
* @return 所有数据
|
||||||
*/
|
*/
|
||||||
default List<T> list() {
|
default List<T> list() {
|
||||||
return getBaseMapper().selectAll();
|
return getMapper().selectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据查询条件查询数据集合。
|
||||||
|
*
|
||||||
|
* @param condition 查询条件
|
||||||
|
* @return 数据集合
|
||||||
|
*/
|
||||||
|
default List<T> list(QueryCondition condition) {
|
||||||
|
return getMapper().selectListByCondition(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据查询条件查询数据集合。
|
||||||
|
*
|
||||||
|
* @param condition 查询条件
|
||||||
|
* @return 数据集合
|
||||||
|
*/
|
||||||
|
default List<T> list(QueryCondition condition, int count) {
|
||||||
|
return getMapper().selectListByCondition(condition, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据查询条件查询数据集合。
|
* 根据查询条件查询数据集合。
|
||||||
*
|
*
|
||||||
@ -274,19 +347,21 @@ public interface IService<T> {
|
|||||||
* @return 数据集合
|
* @return 数据集合
|
||||||
*/
|
*/
|
||||||
default List<T> list(QueryWrapper query) {
|
default List<T> list(QueryWrapper query) {
|
||||||
return getBaseMapper().selectListByQuery(query);
|
return getMapper().selectListByQuery(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据查询条件查询数据集合。
|
* 根据查询条件查询数据集合,并通过 asType 进行接收
|
||||||
*
|
*
|
||||||
* @param query 查询条件
|
* @param query 查询条件
|
||||||
|
* @param asType 接收的数据类型
|
||||||
* @return 数据集合
|
* @return 数据集合
|
||||||
*/
|
*/
|
||||||
default List<T> list(QueryCondition query) {
|
default <R> List<R> listAs(QueryWrapper query, Class<R> asType) {
|
||||||
return getBaseMapper().selectListByCondition(query);
|
return getMapper().selectListByQueryAs(query, asType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据数据主键查询数据集合。
|
* 根据数据主键查询数据集合。
|
||||||
*
|
*
|
||||||
@ -294,7 +369,7 @@ public interface IService<T> {
|
|||||||
* @return 数据集合
|
* @return 数据集合
|
||||||
*/
|
*/
|
||||||
default List<T> listByIds(Collection<? extends Serializable> ids) {
|
default List<T> listByIds(Collection<? extends Serializable> ids) {
|
||||||
return getBaseMapper().selectListByIds(ids);
|
return getMapper().selectListByIds(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -304,7 +379,7 @@ public interface IService<T> {
|
|||||||
* @return 数据集合
|
* @return 数据集合
|
||||||
*/
|
*/
|
||||||
default List<T> listByMap(Map<String, Object> query) {
|
default List<T> listByMap(Map<String, Object> query) {
|
||||||
return getBaseMapper().selectListByMap(query);
|
return getMapper().selectListByMap(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 数量查询操作 =====
|
// ===== 数量查询操作 =====
|
||||||
@ -316,17 +391,17 @@ public interface IService<T> {
|
|||||||
* @return {@code true} 数据存在,{@code false} 数据不存在。
|
* @return {@code true} 数据存在,{@code false} 数据不存在。
|
||||||
*/
|
*/
|
||||||
default boolean exists(QueryWrapper query) {
|
default boolean exists(QueryWrapper query) {
|
||||||
return retBool(count(query));
|
return SqlUtil.toBool(count(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据查询条件判断数据是否存在。
|
* 根据查询条件判断数据是否存在。
|
||||||
*
|
*
|
||||||
* @param query 查询条件
|
* @param condition 查询条件
|
||||||
* @return {@code true} 数据存在,{@code false} 数据不存在。
|
* @return {@code true} 数据存在,{@code false} 数据不存在。
|
||||||
*/
|
*/
|
||||||
default boolean exists(QueryCondition query) {
|
default boolean exists(QueryCondition condition) {
|
||||||
return retBool(count(query));
|
return SqlUtil.toBool(count(condition));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -335,7 +410,7 @@ public interface IService<T> {
|
|||||||
* @return 所有数据数量
|
* @return 所有数据数量
|
||||||
*/
|
*/
|
||||||
default long count() {
|
default long count() {
|
||||||
return getBaseMapper().selectCountByQuery(QueryWrapper.create());
|
return getMapper().selectCountByQuery(QueryWrapper.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -345,17 +420,17 @@ public interface IService<T> {
|
|||||||
* @return 数据数量
|
* @return 数据数量
|
||||||
*/
|
*/
|
||||||
default long count(QueryWrapper query) {
|
default long count(QueryWrapper query) {
|
||||||
return getBaseMapper().selectCountByQuery(query);
|
return getMapper().selectCountByQuery(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据查询条件查询数据数量。
|
* 根据查询条件查询数据数量。
|
||||||
*
|
*
|
||||||
* @param query 查询条件
|
* @param condition 查询条件
|
||||||
* @return 数据数量
|
* @return 数据数量
|
||||||
*/
|
*/
|
||||||
default long count(QueryCondition query) {
|
default long count(QueryCondition condition) {
|
||||||
return getBaseMapper().selectCountByCondition(query);
|
return getMapper().selectCountByCondition(condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 分页查询操作 =====
|
// ===== 分页查询操作 =====
|
||||||
@ -367,29 +442,29 @@ public interface IService<T> {
|
|||||||
* @return 分页对象
|
* @return 分页对象
|
||||||
*/
|
*/
|
||||||
default Page<T> page(Page<T> page) {
|
default Page<T> page(Page<T> page) {
|
||||||
return getBaseMapper().paginate(page, QueryWrapper.create());
|
return getMapper().paginate(page, QueryWrapper.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据查询条件分页查询数据。
|
* 根据查询条件分页查询数据。
|
||||||
*
|
*
|
||||||
* @param page 分页对象
|
* @param page 分页对象
|
||||||
* @param query 查询条件
|
* @param query 查询条件
|
||||||
* @return 分页对象
|
* @return 分页对象
|
||||||
*/
|
*/
|
||||||
default Page<T> page(Page<T> page, QueryWrapper query) {
|
default Page<T> page(Page<T> page, QueryWrapper query) {
|
||||||
return getBaseMapper().paginate(page, query);
|
return getMapper().paginate(page, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据查询条件分页查询数据。
|
* 根据查询条件分页查询数据。
|
||||||
*
|
*
|
||||||
* @param page 分页对象
|
* @param page 分页对象
|
||||||
* @param query 查询条件
|
* @param condition 查询条件
|
||||||
* @return 分页对象
|
* @return 分页对象
|
||||||
*/
|
*/
|
||||||
default Page<T> page(Page<T> page, QueryCondition query) {
|
default Page<T> page(Page<T> page, QueryCondition condition) {
|
||||||
return getBaseMapper().paginate(page, QueryWrapper.create().where(query));
|
return getMapper().paginate(page, QueryWrapper.create().where(condition));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -456,8 +456,7 @@ public class TableInfo {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object value = getPropertyValue(metaObject, property);
|
Object value = buildColumnSqlArg(metaObject, column);
|
||||||
|
|
||||||
// ModifyAttrsRecord 忽略 ignoreNulls 的设置,
|
// ModifyAttrsRecord 忽略 ignoreNulls 的设置,
|
||||||
// 当使用 ModifyAttrsRecord 时,可以理解为要对字段进行 null 值进行更新,否则没必要使用 ModifyAttrsRecord
|
// 当使用 ModifyAttrsRecord 时,可以理解为要对字段进行 null 值进行更新,否则没必要使用 ModifyAttrsRecord
|
||||||
// if (ignoreNulls && value == null) {
|
// if (ignoreNulls && value == null) {
|
||||||
@ -530,21 +529,24 @@ public class TableInfo {
|
|||||||
CPI.putContext(queryWrapper, APPEND_CONDITIONS_FLAG, Boolean.TRUE);
|
CPI.putContext(queryWrapper, APPEND_CONDITIONS_FLAG, Boolean.TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//select xxx.id,(select..) from xxx
|
||||||
|
List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
|
||||||
|
if (selectColumns != null && !selectColumns.isEmpty()) {
|
||||||
|
for (QueryColumn queryColumn : selectColumns) {
|
||||||
|
if (queryColumn instanceof SelectQueryColumn) {
|
||||||
|
QueryWrapper selectColumnQueryWrapper = CPI.getQueryWrapper((SelectQueryColumn) queryColumn);
|
||||||
|
doAppendConditions(entity, selectColumnQueryWrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//select * from (select ... from ) 中的子查询处理
|
//select * from (select ... from ) 中的子查询处理
|
||||||
List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
|
List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
|
||||||
if (queryTables != null && !queryTables.isEmpty()) {
|
if (queryTables != null && !queryTables.isEmpty()) {
|
||||||
for (QueryTable queryTable : queryTables) {
|
for (QueryTable queryTable : queryTables) {
|
||||||
if (queryTable instanceof SelectQueryTable) {
|
if (queryTable instanceof SelectQueryTable) {
|
||||||
QueryWrapper selectQueryWrapper = ((SelectQueryTable) queryTable).getQueryWrapper();
|
QueryWrapper selectQueryWrapper = ((SelectQueryTable) queryTable).getQueryWrapper();
|
||||||
List<QueryTable> selectQueryTables = CPI.getQueryTables(selectQueryWrapper);
|
doAppendConditions(entity, selectQueryWrapper);
|
||||||
if (selectQueryTables != null && !selectQueryTables.isEmpty()) {
|
|
||||||
for (QueryTable selectQueryTable : selectQueryTables) {
|
|
||||||
TableInfo tableInfo = TableInfoFactory.ofTableName(selectQueryTable.getName());
|
|
||||||
if (tableInfo != null) {
|
|
||||||
tableInfo.appendConditions(entity, selectQueryWrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -578,13 +580,7 @@ public class TableInfo {
|
|||||||
List<QueryWrapper> childSelects = CPI.getChildSelect(queryWrapper);
|
List<QueryWrapper> childSelects = CPI.getChildSelect(queryWrapper);
|
||||||
if (CollectionUtil.isNotEmpty(childSelects)) {
|
if (CollectionUtil.isNotEmpty(childSelects)) {
|
||||||
for (QueryWrapper childQueryWrapper : childSelects) {
|
for (QueryWrapper childQueryWrapper : childSelects) {
|
||||||
List<QueryTable> childQueryTables = CPI.getQueryTables(childQueryWrapper);
|
doAppendConditions(entity, childQueryWrapper);
|
||||||
for (QueryTable queryTable : childQueryTables) {
|
|
||||||
TableInfo tableInfo = TableInfoFactory.ofTableName(queryTable.getName());
|
|
||||||
if (tableInfo != null) {
|
|
||||||
tableInfo.appendConditions(entity, childQueryWrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,16 +589,22 @@ public class TableInfo {
|
|||||||
if (CollectionUtil.isNotEmpty(unions)) {
|
if (CollectionUtil.isNotEmpty(unions)) {
|
||||||
for (UnionWrapper union : unions) {
|
for (UnionWrapper union : unions) {
|
||||||
QueryWrapper unionQueryWrapper = union.getQueryWrapper();
|
QueryWrapper unionQueryWrapper = union.getQueryWrapper();
|
||||||
List<QueryTable> unionQueryTables = CPI.getQueryTables(unionQueryWrapper);
|
doAppendConditions(entity, unionQueryWrapper);
|
||||||
for (QueryTable queryTable : unionQueryTables) {
|
}
|
||||||
TableInfo tableInfo = TableInfoFactory.ofTableName(queryTable.getName());
|
}
|
||||||
if (tableInfo != null) {
|
}
|
||||||
tableInfo.appendConditions(entity, unionQueryWrapper);
|
|
||||||
}
|
|
||||||
|
private void doAppendConditions(Object entity, QueryWrapper queryWrapper) {
|
||||||
|
List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
|
||||||
|
if (queryTables != null && !queryTables.isEmpty()) {
|
||||||
|
for (QueryTable queryTable : queryTables) {
|
||||||
|
TableInfo tableInfo = TableInfoFactory.ofTableName(queryTable.getName());
|
||||||
|
if (tableInfo != null) {
|
||||||
|
tableInfo.appendConditions(entity, queryWrapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -640,6 +642,16 @@ public class TableInfo {
|
|||||||
.typeHandler(columnInfo.buildTypeHandler())
|
.typeHandler(columnInfo.buildTypeHandler())
|
||||||
.build();
|
.build();
|
||||||
resultMappings.add(mapping);
|
resultMappings.add(mapping);
|
||||||
|
|
||||||
|
//add property mapper for sql as ...
|
||||||
|
if (!Objects.equals(columnInfo.getColumn(), columnInfo.getProperty())) {
|
||||||
|
ResultMapping propertyMapping = new ResultMapping.Builder(configuration, columnInfo.getProperty(),
|
||||||
|
columnInfo.getProperty(), columnInfo.getPropertyType())
|
||||||
|
.jdbcType(columnInfo.getJdbcType())
|
||||||
|
.typeHandler(columnInfo.buildTypeHandler())
|
||||||
|
.build();
|
||||||
|
resultMappings.add(propertyMapping);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (IdInfo idInfo : primaryKeyList) {
|
for (IdInfo idInfo : primaryKeyList) {
|
||||||
@ -660,9 +672,11 @@ public class TableInfo {
|
|||||||
ColumnInfo columnInfo = columnInfoMapping.get(column);
|
ColumnInfo columnInfo = columnInfoMapping.get(column);
|
||||||
Object value = getPropertyValue(metaObject, columnInfo.property);
|
Object value = getPropertyValue(metaObject, columnInfo.property);
|
||||||
|
|
||||||
TypeHandler typeHandler = columnInfo.buildTypeHandler();
|
if (value != null) {
|
||||||
if (value != null && typeHandler != null) {
|
TypeHandler typeHandler = columnInfo.buildTypeHandler();
|
||||||
return new TypeHandlerObject(typeHandler, value, columnInfo.getJdbcType());
|
if (typeHandler != null) {
|
||||||
|
return new TypeHandlerObject(typeHandler, value, columnInfo.getJdbcType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
@ -783,7 +797,9 @@ public class TableInfo {
|
|||||||
//默认使用第一个作为插入的租户ID
|
//默认使用第一个作为插入的租户ID
|
||||||
Object tenantId = tenantIds[0];
|
Object tenantId = tenantIds[0];
|
||||||
if (tenantId != null) {
|
if (tenantId != null) {
|
||||||
metaObject.setValue(columnInfoMapping.get(tenantIdColumn).property, tenantId);
|
String property = columnInfoMapping.get(tenantIdColumn).property;
|
||||||
|
Class<?> setterType = metaObject.getSetterType(property);
|
||||||
|
metaObject.setValue(property, ConvertUtil.convert(tenantId, setterType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,7 +818,6 @@ public class TableInfo {
|
|||||||
if (columnValue == null) {
|
if (columnValue == null) {
|
||||||
String property = columnInfoMapping.get(logicDeleteColumn).property;
|
String property = columnInfoMapping.get(logicDeleteColumn).property;
|
||||||
Class<?> setterType = metaObject.getSetterType(property);
|
Class<?> setterType = metaObject.getSetterType(property);
|
||||||
|
|
||||||
Object normalValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete();
|
Object normalValueOfLogicDelete = FlexGlobalConfig.getDefaultConfig().getNormalValueOfLogicDelete();
|
||||||
metaObject.setValue(property, ConvertUtil.convert(normalValueOfLogicDelete, setterType));
|
metaObject.setValue(property, ConvertUtil.convert(normalValueOfLogicDelete, setterType));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,12 +16,14 @@
|
|||||||
package com.mybatisflex.core.table;
|
package com.mybatisflex.core.table;
|
||||||
|
|
||||||
import com.mybatisflex.annotation.*;
|
import com.mybatisflex.annotation.*;
|
||||||
|
import com.mybatisflex.core.BaseMapper;
|
||||||
import com.mybatisflex.core.FlexConsts;
|
import com.mybatisflex.core.FlexConsts;
|
||||||
import com.mybatisflex.core.FlexGlobalConfig;
|
import com.mybatisflex.core.FlexGlobalConfig;
|
||||||
import com.mybatisflex.core.exception.FlexExceptions;
|
import com.mybatisflex.core.exception.FlexExceptions;
|
||||||
import com.mybatisflex.core.util.ClassUtil;
|
import com.mybatisflex.core.util.ClassUtil;
|
||||||
import com.mybatisflex.core.util.CollectionUtil;
|
import com.mybatisflex.core.util.CollectionUtil;
|
||||||
import com.mybatisflex.core.util.StringUtil;
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
|
import org.apache.ibatis.io.ResolverUtil;
|
||||||
import org.apache.ibatis.reflection.Reflector;
|
import org.apache.ibatis.reflection.Reflector;
|
||||||
import org.apache.ibatis.session.Configuration;
|
import org.apache.ibatis.session.Configuration;
|
||||||
import org.apache.ibatis.type.JdbcType;
|
import org.apache.ibatis.type.JdbcType;
|
||||||
@ -57,7 +59,7 @@ public class TableInfoFactory {
|
|||||||
Date.class, java.sql.Date.class, Time.class, Timestamp.class,
|
Date.class, java.sql.Date.class, Time.class, Timestamp.class,
|
||||||
Instant.class, LocalDate.class, LocalDateTime.class, LocalTime.class, OffsetDateTime.class, OffsetTime.class, ZonedDateTime.class,
|
Instant.class, LocalDate.class, LocalDateTime.class, LocalTime.class, OffsetDateTime.class, OffsetTime.class, ZonedDateTime.class,
|
||||||
Year.class, Month.class, YearMonth.class, JapaneseDate.class,
|
Year.class, Month.class, YearMonth.class, JapaneseDate.class,
|
||||||
byte[].class, Byte[].class,
|
byte[].class, Byte[].class, Byte.class,
|
||||||
BigInteger.class, BigDecimal.class,
|
BigInteger.class, BigDecimal.class,
|
||||||
char.class, String.class, Character.class
|
char.class, String.class, Character.class
|
||||||
);
|
);
|
||||||
@ -66,12 +68,26 @@ public class TableInfoFactory {
|
|||||||
private static final Map<Class<?>, TableInfo> mapperTableInfoMap = new ConcurrentHashMap<>();
|
private static final Map<Class<?>, TableInfo> mapperTableInfoMap = new ConcurrentHashMap<>();
|
||||||
private static final Map<Class<?>, TableInfo> entityTableMap = new ConcurrentHashMap<>();
|
private static final Map<Class<?>, TableInfo> entityTableMap = new ConcurrentHashMap<>();
|
||||||
private static final Map<String, TableInfo> tableInfoMap = new ConcurrentHashMap<>();
|
private static final Map<String, TableInfo> tableInfoMap = new ConcurrentHashMap<>();
|
||||||
|
private static final Set<String> initedPackageNames = new HashSet<>();
|
||||||
|
|
||||||
|
|
||||||
|
public synchronized static void init(String mapperPackageName){
|
||||||
|
if (!initedPackageNames.contains(mapperPackageName)) {
|
||||||
|
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
|
||||||
|
resolverUtil.find(new ResolverUtil.IsA(BaseMapper.class), mapperPackageName);
|
||||||
|
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
|
||||||
|
for (Class<? extends Class<?>> mapperClass : mapperSet) {
|
||||||
|
ofMapperClass(mapperClass);
|
||||||
|
}
|
||||||
|
initedPackageNames.add(mapperPackageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static TableInfo ofMapperClass(Class<?> mapperClass) {
|
public static TableInfo ofMapperClass(Class<?> mapperClass) {
|
||||||
return MapUtil.computeIfAbsent(mapperTableInfoMap, mapperClass, key -> {
|
return MapUtil.computeIfAbsent(mapperTableInfoMap, mapperClass, key -> {
|
||||||
Class<?> entityClass = getEntityClass(mapperClass);
|
Class<?> entityClass = getEntityClass(mapperClass);
|
||||||
if (entityClass == null) {
|
if (entityClass == null){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return ofEntityClass(entityClass);
|
return ofEntityClass(entityClass);
|
||||||
@ -117,7 +133,6 @@ public class TableInfoFactory {
|
|||||||
tableInfo.setEntityClass(entityClass);
|
tableInfo.setEntityClass(entityClass);
|
||||||
tableInfo.setReflector(new Reflector(entityClass));
|
tableInfo.setReflector(new Reflector(entityClass));
|
||||||
|
|
||||||
|
|
||||||
//初始化表名
|
//初始化表名
|
||||||
Table table = entityClass.getAnnotation(Table.class);
|
Table table = entityClass.getAnnotation(Table.class);
|
||||||
if (table != null) {
|
if (table != null) {
|
||||||
|
|||||||
@ -36,6 +36,8 @@ public class ClassUtil {
|
|||||||
// javassist
|
// javassist
|
||||||
, "javassist.util.proxy.ProxyObject"
|
, "javassist.util.proxy.ProxyObject"
|
||||||
, "org.apache.ibatis.javassist.util.proxy.ProxyObject");
|
, "org.apache.ibatis.javassist.util.proxy.ProxyObject");
|
||||||
|
private static final String ENHANCER_BY = "$$EnhancerBy";
|
||||||
|
private static final String JAVASSIST_BY = "_$$_";
|
||||||
|
|
||||||
public static boolean isProxy(Class<?> clazz) {
|
public static boolean isProxy(Class<?> clazz) {
|
||||||
for (Class<?> cls : clazz.getInterfaces()) {
|
for (Class<?> cls : clazz.getInterfaces()) {
|
||||||
@ -47,12 +49,9 @@ public class ClassUtil {
|
|||||||
return Proxy.isProxyClass(clazz);
|
return Proxy.isProxyClass(clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String ENHANCER_BY = "$$EnhancerBy";
|
|
||||||
private static final String JAVASSIST_BY = "_$$_";
|
|
||||||
|
|
||||||
public static <T> Class<T> getUsefulClass(Class<T> clazz) {
|
public static <T> Class<T> getUsefulClass(Class<T> clazz) {
|
||||||
if (isProxy(clazz)) {
|
if (isProxy(clazz)) {
|
||||||
return (Class<T>) clazz.getSuperclass();
|
return getJdkProxySuperClass(clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
//ControllerTest$ServiceTest$$EnhancerByGuice$$40471411#hello -------> Guice
|
//ControllerTest$ServiceTest$$EnhancerByGuice$$40471411#hello -------> Guice
|
||||||
@ -235,4 +234,9 @@ public class ClassUtil {
|
|||||||
doGetMethods(cl.getSuperclass(), methods, predicate);
|
doGetMethods(cl.getSuperclass(), methods, predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> Class<T> getJdkProxySuperClass(Class<T> clazz) {
|
||||||
|
final Class<?> proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), clazz.getInterfaces());
|
||||||
|
return (Class<T>) proxyClass.getInterfaces()[0];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,4 +83,24 @@ public class CollectionUtil {
|
|||||||
return new ArrayList<>(Arrays.asList(elements));
|
return new ArrayList<>(Arrays.asList(elements));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> toList(Collection<T> collection) {
|
||||||
|
if (collection instanceof List) {
|
||||||
|
return (List<T>) collection;
|
||||||
|
} else {
|
||||||
|
return new ArrayList<>(collection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] toArrayString(Collection<?> collection) {
|
||||||
|
if (isEmpty(collection)) {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
String[] results = new String[collection.size()];
|
||||||
|
int index = 0;
|
||||||
|
for (Object o : collection) {
|
||||||
|
results[index++] = String.valueOf(o);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,11 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.mybatisflex.core.util;
|
package com.mybatisflex.core.util;
|
||||||
|
|
||||||
import com.mybatisflex.annotation.EnumValue;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
@ -27,8 +22,6 @@ import java.time.LocalDateTime;
|
|||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.temporal.Temporal;
|
import java.time.temporal.Temporal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class ConvertUtil {
|
public class ConvertUtil {
|
||||||
|
|
||||||
@ -99,40 +92,9 @@ public class ConvertUtil {
|
|||||||
}
|
}
|
||||||
return Short.parseShort(value.toString());
|
return Short.parseShort(value.toString());
|
||||||
} else if (targetClass.isEnum()) {
|
} else if (targetClass.isEnum()) {
|
||||||
Object[] enumConstants = targetClass.getEnumConstants();
|
EnumWrapper enumWrapper = EnumWrapper.of(targetClass);
|
||||||
List<Field> allFields = ClassUtil.getAllFields(targetClass, field -> field.getAnnotation(EnumValue.class) != null);
|
if (enumWrapper.hasEnumValueAnnotation()) {
|
||||||
if (allFields.size() == 1) {
|
return enumWrapper.toEnum(value);
|
||||||
Field field = allFields.get(0);
|
|
||||||
|
|
||||||
String fieldGetterName = "get" + StringUtil.firstCharToUpperCase(field.getName());
|
|
||||||
List<Method> allMethods = ClassUtil.getAllMethods(targetClass, method -> {
|
|
||||||
String methodName = method.getName();
|
|
||||||
return methodName.equals(fieldGetterName);
|
|
||||||
});
|
|
||||||
|
|
||||||
//getter
|
|
||||||
if (allMethods.size() == 1) {
|
|
||||||
Method getter = allMethods.get(0);
|
|
||||||
for (Object enumConstant : enumConstants) {
|
|
||||||
try {
|
|
||||||
Object enumValue = getter.invoke(enumConstant);
|
|
||||||
if (Objects.equals(enumValue, value)) {
|
|
||||||
return enumConstant;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//public field
|
|
||||||
else if (Modifier.isPublic(field.getModifiers())) {
|
|
||||||
for (Object enumConstant : enumConstants) {
|
|
||||||
if (Objects.equals(readPublicField(field, enumConstant), value)) {
|
|
||||||
return enumConstant;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (value instanceof String) {
|
} else if (value instanceof String) {
|
||||||
return Enum.valueOf(targetClass, value.toString());
|
return Enum.valueOf(targetClass, value.toString());
|
||||||
}
|
}
|
||||||
@ -146,15 +108,6 @@ public class ConvertUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static Object readPublicField(Field field, Object target) {
|
|
||||||
try {
|
|
||||||
return field.get(target);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE
|
//Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE
|
||||||
public static Object getPrimitiveDefaultValue(Class<?> paraClass) {
|
public static Object getPrimitiveDefaultValue(Class<?> paraClass) {
|
||||||
if (paraClass == int.class || paraClass == long.class || paraClass == float.class || paraClass == double.class) {
|
if (paraClass == int.class || paraClass == long.class || paraClass == float.class || paraClass == double.class) {
|
||||||
|
|||||||
@ -0,0 +1,121 @@
|
|||||||
|
/**
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import com.mybatisflex.annotation.EnumValue;
|
||||||
|
import com.mybatisflex.core.exception.FlexExceptions;
|
||||||
|
import org.apache.ibatis.util.MapUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class EnumWrapper<E extends Enum<E>> {
|
||||||
|
|
||||||
|
private static final Map<Class, EnumWrapper> cache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private Class<?> enumClass;
|
||||||
|
|
||||||
|
private Class<?> enumPropertyType;
|
||||||
|
private E[] enums;
|
||||||
|
private Field property;
|
||||||
|
private Method getter;
|
||||||
|
private boolean hasEnumValueAnnotation = false;
|
||||||
|
|
||||||
|
public static <R extends Enum<R>> EnumWrapper<R> of(Class<?> enumClass) {
|
||||||
|
return MapUtil.computeIfAbsent(cache, enumClass, EnumWrapper::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumWrapper(Class<E> enumClass) {
|
||||||
|
this.enumClass = enumClass;
|
||||||
|
|
||||||
|
List<Field> allFields = ClassUtil.getAllFields(enumClass, field -> field.getAnnotation(EnumValue.class) != null);
|
||||||
|
if (!allFields.isEmpty()) {
|
||||||
|
hasEnumValueAnnotation = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasEnumValueAnnotation) {
|
||||||
|
Field field = allFields.get(0);
|
||||||
|
|
||||||
|
String fieldGetterName = "get" + StringUtil.firstCharToUpperCase(field.getName());
|
||||||
|
List<Method> allMethods = ClassUtil.getAllMethods(enumClass, method -> {
|
||||||
|
String methodName = method.getName();
|
||||||
|
return methodName.equals(fieldGetterName);
|
||||||
|
});
|
||||||
|
|
||||||
|
enumPropertyType = ClassUtil.wrap(field.getType());
|
||||||
|
enums = enumClass.getEnumConstants();
|
||||||
|
|
||||||
|
if (allMethods.isEmpty()) {
|
||||||
|
if (Modifier.isPublic(field.getModifiers())) {
|
||||||
|
property = field;
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Can not find \"" + fieldGetterName + "()\" method in enum: " + enumClass.getName());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
getter = allMethods.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Object getEnumValue(E object) {
|
||||||
|
try {
|
||||||
|
return getter != null
|
||||||
|
? getter.invoke(object)
|
||||||
|
: property.get(object);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw FlexExceptions.wrap(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public E toEnum(Object value) {
|
||||||
|
for (E e : enums) {
|
||||||
|
if (value.equals(getEnumValue(e))) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getEnumClass() {
|
||||||
|
return enumClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getEnumPropertyType() {
|
||||||
|
return enumPropertyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public E[] getEnums() {
|
||||||
|
return enums;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Field getProperty() {
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Method getGetter() {
|
||||||
|
return getter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasEnumValueAnnotation() {
|
||||||
|
return hasEnumValueAnnotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface LambdaGetter<T> extends Serializable {
|
||||||
|
Object get(T source);
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import org.apache.ibatis.reflection.property.PropertyNamer;
|
||||||
|
import org.apache.ibatis.util.MapUtil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.invoke.SerializedLambda;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class LambdaUtil {
|
||||||
|
|
||||||
|
private static final Map<Class<?>, SerializedLambda> lambdaMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static <T> String getFieldName(LambdaGetter<T> getter) {
|
||||||
|
SerializedLambda lambda = getSerializedLambda(getter);
|
||||||
|
String methodName = lambda.getImplMethodName();
|
||||||
|
return PropertyNamer.methodToProperty(methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static SerializedLambda getSerializedLambda(Serializable getter) {
|
||||||
|
return MapUtil.computeIfAbsent(lambdaMap, getter.getClass(), aClass -> {
|
||||||
|
try {
|
||||||
|
Method method = getter.getClass().getDeclaredMethod("writeReplace");
|
||||||
|
method.setAccessible(Boolean.TRUE);
|
||||||
|
return (SerializedLambda) method.invoke(getter);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -50,7 +50,7 @@ public class SqlUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static final char[] UN_SAFE_CHARS = "'`\"<>&*+=#-;".toCharArray();
|
private static final char[] UN_SAFE_CHARS = "'`\"<>&+=#-;".toCharArray();
|
||||||
|
|
||||||
private static boolean isUnSafeChar(char ch) {
|
private static boolean isUnSafeChar(char ch) {
|
||||||
for (char c : UN_SAFE_CHARS) {
|
for (char c : UN_SAFE_CHARS) {
|
||||||
@ -68,19 +68,8 @@ public class SqlUtil {
|
|||||||
* @param result 数据库操作返回影响条数
|
* @param result 数据库操作返回影响条数
|
||||||
* @return {@code true} 操作成功,{@code false} 操作失败。
|
* @return {@code true} 操作成功,{@code false} 操作失败。
|
||||||
*/
|
*/
|
||||||
public static boolean retBool(int result) {
|
public static boolean toBool(Number result) {
|
||||||
return result >= 1;
|
return result != null && result.longValue() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据数据库响应结果判断数据库操作是否成功。
|
|
||||||
*
|
|
||||||
* @param result 数据库操作返回影响条数
|
|
||||||
* @return {@code true} 操作成功,{@code false} 操作失败。
|
|
||||||
*/
|
|
||||||
public static boolean retBool(long result) {
|
|
||||||
return result >= 1L;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -253,7 +253,6 @@ public class StringUtil {
|
|||||||
* @param objs
|
* @param objs
|
||||||
* @param function
|
* @param function
|
||||||
* @param <T>
|
* @param <T>
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
public static <T> String join(String delimiter, Collection<T> objs, Function<T, String> function) {
|
public static <T> String join(String delimiter, Collection<T> objs, Function<T, String> function) {
|
||||||
if (CollectionUtil.isEmpty(objs)) {
|
if (CollectionUtil.isEmpty(objs)) {
|
||||||
|
|||||||
@ -13,8 +13,8 @@ import org.junit.Test;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static com.mybatisflex.core.query.QueryMethods.*;
|
import static com.mybatisflex.core.query.QueryMethods.*;
|
||||||
import static com.mybatisflex.coretest.table.Tables.ACCOUNT;
|
import static com.mybatisflex.coretest.table.AccountTableDef.ACCOUNT;
|
||||||
import static com.mybatisflex.coretest.table.Tables.ARTICLE;
|
import static com.mybatisflex.coretest.table.ArticleTableDef.ARTICLE;
|
||||||
|
|
||||||
public class AccountSqlTester {
|
public class AccountSqlTester {
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ public class AccountSqlTester {
|
|||||||
.from(ACCOUNT);
|
.from(ACCOUNT);
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(query);
|
String sql = dialect.forSelectByQuery(query);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ public class AccountSqlTester {
|
|||||||
.from(ACCOUNT);
|
.from(ACCOUNT);
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(query);
|
String sql = dialect.forSelectByQuery(query);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ public class AccountSqlTester {
|
|||||||
.where(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID));
|
.where(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID));
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl(KeywordWrap.NONE, LimitOffsetProcessor.MYSQL);
|
IDialect dialect = new CommonsDialectImpl(KeywordWrap.NONE, LimitOffsetProcessor.MYSQL);
|
||||||
String sql = dialect.forSelectListByQuery(query);
|
String sql = dialect.forSelectByQuery(query);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ public class AccountSqlTester {
|
|||||||
.from(ACCOUNT);
|
.from(ACCOUNT);
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(query);
|
String sql = dialect.forSelectByQuery(query);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ public class AccountSqlTester {
|
|||||||
.from(ACCOUNT);
|
.from(ACCOUNT);
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(query);
|
String sql = dialect.forSelectByQuery(query);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,21 +88,7 @@ public class AccountSqlTester {
|
|||||||
.unionAll(select(ARTICLE.ID).from(ARTICLE));
|
.unionAll(select(ARTICLE.ID).from(ARTICLE));
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(query);
|
String sql = dialect.forSelectByQuery(query);
|
||||||
System.out.println(sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSelectCountSql() {
|
|
||||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
|
||||||
.select()
|
|
||||||
.from(ACCOUNT)
|
|
||||||
.where(ACCOUNT.ID.ge(100))
|
|
||||||
.and(ACCOUNT.USER_NAME.like("michael"));
|
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
|
||||||
String sql = dialect.forSelectCountByQuery(queryWrapper);
|
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +102,7 @@ public class AccountSqlTester {
|
|||||||
.and(ACCOUNT.USER_NAME.like("michael"));
|
.and(ACCOUNT.USER_NAME.like("michael"));
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +115,7 @@ public class AccountSqlTester {
|
|||||||
.and(column("aaa").in("michael", "aaa"));
|
.and(column("aaa").in("michael", "aaa"));
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +130,7 @@ public class AccountSqlTester {
|
|||||||
.and(ACCOUNT.USER_NAME.like("michael"));
|
.and(ACCOUNT.USER_NAME.like("michael"));
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
|
|
||||||
Object[] valueArray = CPI.getValueArray(queryWrapper);
|
Object[] valueArray = CPI.getValueArray(queryWrapper);
|
||||||
@ -162,7 +148,7 @@ public class AccountSqlTester {
|
|||||||
.and(ACCOUNT.USER_NAME.like("michael"));
|
.and(ACCOUNT.USER_NAME.like("michael"));
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
|
|
||||||
Object[] valueArray = CPI.getValueArray(queryWrapper);
|
Object[] valueArray = CPI.getValueArray(queryWrapper);
|
||||||
@ -183,7 +169,7 @@ public class AccountSqlTester {
|
|||||||
);
|
);
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +184,7 @@ public class AccountSqlTester {
|
|||||||
.or(ACCOUNT.AGE.in(18, 19, 20).or(ACCOUNT.USER_NAME.like("michael")));
|
.or(ACCOUNT.AGE.in(18, 19, 20).or(ACCOUNT.USER_NAME.like("michael")));
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +198,7 @@ public class AccountSqlTester {
|
|||||||
));
|
));
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +210,7 @@ public class AccountSqlTester {
|
|||||||
.groupBy(ACCOUNT.USER_NAME);
|
.groupBy(ACCOUNT.USER_NAME);
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,7 +223,7 @@ public class AccountSqlTester {
|
|||||||
.having(ACCOUNT.AGE.between(18, 25));
|
.having(ACCOUNT.AGE.between(18, 25));
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +236,7 @@ public class AccountSqlTester {
|
|||||||
.where(ACCOUNT.AGE.ge(10));
|
.where(ACCOUNT.AGE.ge(10));
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +251,7 @@ public class AccountSqlTester {
|
|||||||
.where(ACCOUNT.AGE.ge(10));
|
.where(ACCOUNT.AGE.ge(10));
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +268,7 @@ public class AccountSqlTester {
|
|||||||
.where(ACCOUNT.AGE.ge(10));
|
.where(ACCOUNT.AGE.ge(10));
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +280,7 @@ public class AccountSqlTester {
|
|||||||
.orderBy(ACCOUNT.AGE.asc(), ACCOUNT.USER_NAME.desc().nullsLast());
|
.orderBy(ACCOUNT.AGE.asc(), ACCOUNT.USER_NAME.desc().nullsLast());
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(queryWrapper);
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,6 +292,73 @@ public class AccountSqlTester {
|
|||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testForUpdate() {
|
||||||
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
|
|
||||||
|
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||||
|
.select()
|
||||||
|
.from(ACCOUNT)
|
||||||
|
.and(ACCOUNT.USER_NAME.like("michael"))
|
||||||
|
.forUpdate();
|
||||||
|
|
||||||
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
|
System.out.println(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConvert() {
|
||||||
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
|
|
||||||
|
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||||
|
.select(ACCOUNT.ALL_COLUMNS, convert("NVARCHAR(30)", "GETDATE()", "126").as("result"))
|
||||||
|
.from(ACCOUNT)
|
||||||
|
.and(ACCOUNT.USER_NAME.like("michael"))
|
||||||
|
.and(convert("NVARCHAR(30)", "GETDATE()", "126").in(
|
||||||
|
select(ACCOUNT.ID).from(ACCOUNT).where(ACCOUNT.ID.ge(100)))
|
||||||
|
);
|
||||||
|
|
||||||
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
|
System.out.println(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCase1() {
|
||||||
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
|
|
||||||
|
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||||
|
.select(ACCOUNT.ALL_COLUMNS,
|
||||||
|
case_()
|
||||||
|
.when(ACCOUNT.ID.eq(100)).then(100)
|
||||||
|
.when(ACCOUNT.ID.ge(200)).then(200)
|
||||||
|
.else_(300)
|
||||||
|
.end().as("result"))
|
||||||
|
.from(ACCOUNT)
|
||||||
|
.and(ACCOUNT.USER_NAME.like("michael"));
|
||||||
|
|
||||||
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
|
System.out.println(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCase2() {
|
||||||
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
|
|
||||||
|
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||||
|
.select(ACCOUNT.ALL_COLUMNS,
|
||||||
|
case_(ACCOUNT.ID)
|
||||||
|
.when(100).then(100)
|
||||||
|
.when(200).then(200)
|
||||||
|
.else_(300)
|
||||||
|
.end().as("result"))
|
||||||
|
.from(ACCOUNT)
|
||||||
|
.and(ACCOUNT.USER_NAME.like("michael"));
|
||||||
|
|
||||||
|
String sql = dialect.forSelectByQuery(queryWrapper);
|
||||||
|
System.out.println(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLimitOffset() {
|
public void testLimitOffset() {
|
||||||
@ -325,9 +378,9 @@ public class AccountSqlTester {
|
|||||||
String sql2 = dialect2.buildSelectSql(queryWrapper);
|
String sql2 = dialect2.buildSelectSql(queryWrapper);
|
||||||
System.out.println(sql2);
|
System.out.println(sql2);
|
||||||
|
|
||||||
IDialect dialect3 = new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.DB2);
|
// IDialect dialect3 = new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.DB2);
|
||||||
String sql3 = dialect3.buildSelectSql(queryWrapper);
|
// String sql3 = dialect3.buildSelectSql(queryWrapper);
|
||||||
System.out.println(sql3);
|
// System.out.println(sql3);
|
||||||
|
|
||||||
IDialect dialect4 = new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.POSTGRESQL);
|
IDialect dialect4 = new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcessor.POSTGRESQL);
|
||||||
String sql4 = dialect4.buildSelectSql(queryWrapper);
|
String sql4 = dialect4.buildSelectSql(queryWrapper);
|
||||||
@ -374,7 +427,7 @@ public class AccountSqlTester {
|
|||||||
.orderBy(ACCOUNT.ID.desc())
|
.orderBy(ACCOUNT.ID.desc())
|
||||||
.limit(10, 10);
|
.limit(10, 10);
|
||||||
|
|
||||||
String mysqlSql = new CommonsDialectImpl().forSelectListByQuery(queryWrapper);
|
String mysqlSql = new CommonsDialectImpl().forSelectByQuery(queryWrapper);
|
||||||
System.out.println(">>>>> mysql: \n" + mysqlSql);
|
System.out.println(">>>>> mysql: \n" + mysqlSql);
|
||||||
System.out.println(">>>>> mysql: \n" + Arrays.toString(CPI.getValueArray(queryWrapper)));
|
System.out.println(">>>>> mysql: \n" + Arrays.toString(CPI.getValueArray(queryWrapper)));
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,8 @@ import com.mybatisflex.core.table.TableInfoFactory;
|
|||||||
import com.mybatisflex.core.util.CollectionUtil;
|
import com.mybatisflex.core.util.CollectionUtil;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static com.mybatisflex.coretest.table.Tables.ARTICLE;
|
import static com.mybatisflex.coretest.table.ArticleTableDef.ARTICLE;
|
||||||
|
|
||||||
|
|
||||||
public class ArticleSqlTester {
|
public class ArticleSqlTester {
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ public class ArticleSqlTester {
|
|||||||
.from(ARTICLE);
|
.from(ARTICLE);
|
||||||
|
|
||||||
IDialect dialect = new CommonsDialectImpl();
|
IDialect dialect = new CommonsDialectImpl();
|
||||||
String sql = dialect.forSelectListByQuery(query);
|
String sql = dialect.forSelectByQuery(query);
|
||||||
System.out.println(sql);
|
System.out.println(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
package com.mybatisflex.coretest;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.dialect.DbType;
|
||||||
|
import com.mybatisflex.core.dialect.DbTypeUtil;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class DbTypeUtilTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseUrl(){
|
||||||
|
String url01 = "jdbc:sqlserver://127.0.0.1";
|
||||||
|
DbType dbType01 = DbTypeUtil.parseDbType(url01);
|
||||||
|
System.out.println(dbType01);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>parent</artifactId>
|
<artifactId>parent</artifactId>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@ -20,7 +20,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mybatis-flex</groupId>
|
<groupId>com.mybatis-flex</groupId>
|
||||||
<artifactId>mybatis-flex-annotation</artifactId>
|
<artifactId>mybatis-flex-annotation</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user