mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-07 09:08:24 +08:00
first commit
This commit is contained in:
commit
83e9a89c6d
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
target
|
||||
logs
|
||||
.idea
|
||||
*.iml
|
||||
*.log
|
||||
.logs
|
||||
*.iws
|
||||
*.report
|
||||
*.classpath
|
||||
.DS_Store
|
||||
.ossutil_checkpoint
|
||||
*.patch
|
||||
~$*
|
||||
node_modules
|
||||
yarn.lock
|
||||
dist
|
||||
package-lock.json
|
||||
remarks.md
|
||||
201
LICENSE
Normal file
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
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.
|
||||
2
changes.txt
Normal file
2
changes.txt
Normal file
@ -0,0 +1,2 @@
|
||||
mybatis-flex v1.0.0-beta.1:
|
||||
init mybatis-flex
|
||||
0
docs/readme.md
Normal file
0
docs/readme.md
Normal file
48
mybatis-flex-annotation/pom.xml
Normal file
48
mybatis-flex-annotation/pom.xml
Normal file
@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.0.0-beta.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>mybatis-flex-annotation</artifactId>
|
||||
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>
|
||||
*.properties
|
||||
</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<proc>none</proc>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@ -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.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD})
|
||||
public @interface Column {
|
||||
|
||||
/**
|
||||
* 字段名称
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* update 的时候自动赋值,这个值会直接被拼接到 sql 而不通过参数设置
|
||||
*/
|
||||
String update() default "";
|
||||
|
||||
/**
|
||||
* insert 的时候默认值,这个值会直接被拼接到 sql 而不通过参数设置
|
||||
*/
|
||||
String insert() default "";
|
||||
|
||||
/**
|
||||
* 是否忽略该字段,可能只是业务字段,而非数据库对应字段
|
||||
*/
|
||||
boolean ignore() default false;
|
||||
|
||||
/**
|
||||
* 是否是大字段,大字默认不会对齐进行查询,除非指定查询
|
||||
*/
|
||||
boolean isLarge() default false;
|
||||
|
||||
/**
|
||||
* 是否是逻辑删除字段,一张表中只能存在 1 一个逻辑删除字段
|
||||
*/
|
||||
boolean isLogicDelete() default false;
|
||||
|
||||
/**
|
||||
* 是否为乐观锁字段,如果是的话更新的时候会去检测当前版本号,更新成功的话会设置当前版本号 +1
|
||||
* 只能用于数值的字段
|
||||
*/
|
||||
boolean version() default false;
|
||||
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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.annotation;
|
||||
|
||||
import com.mybatisflex.core.enums.KeyType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD})
|
||||
public @interface Id {
|
||||
|
||||
/**
|
||||
* ID 生成策略,默认为 auto
|
||||
*
|
||||
* @return 生成策略
|
||||
*/
|
||||
KeyType keyType() default KeyType.Auto;
|
||||
|
||||
/**
|
||||
* 若 keyType 类型是 sequence, value 则代表的是
|
||||
* sequence 序列的 sql 内容
|
||||
* 例如:select SEQ_USER_ID.nextval as id from dual
|
||||
*
|
||||
* 若 keyType 是 Generator,value 则代表的是使用的那个 keyGenerator 的名称
|
||||
*
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
|
||||
/**
|
||||
* sequence 序列执行顺序
|
||||
* 是在 entity 数据插入之前执行,还是之后执行,之后执行的一般是数据主动生成的 id
|
||||
*
|
||||
* @return 执行之前还是之后
|
||||
*/
|
||||
boolean before() default true;
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface Table {
|
||||
|
||||
/**
|
||||
* 显式指定表名称
|
||||
*/
|
||||
String value();
|
||||
|
||||
/**
|
||||
* 数据库的 schema
|
||||
*/
|
||||
String schema() default "";
|
||||
|
||||
/**
|
||||
* 是否使用 mybatis 缓存
|
||||
*/
|
||||
boolean useCached() default false;
|
||||
|
||||
/**
|
||||
* 默认为 驼峰属性 转换为 下划线字段
|
||||
*/
|
||||
boolean camelToUnderline() default true;
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.enums;
|
||||
|
||||
/**
|
||||
* ID 生成策略
|
||||
*/
|
||||
public enum KeyType {
|
||||
|
||||
/**
|
||||
* 自增的方式
|
||||
*/
|
||||
Auto,
|
||||
|
||||
/**
|
||||
* 通过执行数据库 sql 生成
|
||||
* 例如:select SEQ_USER_ID.nextval as id from dual
|
||||
*/
|
||||
Sequence,
|
||||
|
||||
/**
|
||||
* 通过 IKeyGenerator 生成器生成
|
||||
*/
|
||||
Generator,
|
||||
|
||||
/**
|
||||
* 其他方式,比如说在代码层用户手动设置
|
||||
*/
|
||||
None,
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 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.processer;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
class MyBatisFlexProps {
|
||||
protected Properties properties = null;
|
||||
private static final String DEFAULT_ENCODING = "UTF-8";
|
||||
|
||||
public MyBatisFlexProps(String fileName) {
|
||||
this(fileName, DEFAULT_ENCODING);
|
||||
}
|
||||
|
||||
public MyBatisFlexProps(String fileName, String encoding) {
|
||||
properties = new Properties();
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = getResourceAsStreamByCurrentThread(fileName);
|
||||
|
||||
if (inputStream == null) {
|
||||
inputStream = getResourceAsStreamByClassloader(fileName);
|
||||
}
|
||||
|
||||
// 当系统未编译的时候,开发环境下的 resources 目录下的 properties 文件不会自动被 copy 到 target/classes 目录下
|
||||
// 此时,需要主动去探测 resources 目录的文件
|
||||
if (inputStream == null) {
|
||||
URL resourceURL = MyBatisFlexProps.class.getResource("/");
|
||||
if (resourceURL != null) {
|
||||
String classPath = resourceURL.toURI().getPath();
|
||||
|
||||
if (removeSlashEnd(classPath).endsWith("classes")) {
|
||||
|
||||
//from classes path
|
||||
File propFile = new File(classPath, fileName);
|
||||
if (propFile.exists() && propFile.isFile()) {
|
||||
inputStream = new FileInputStream(propFile);
|
||||
}
|
||||
|
||||
//from resources path
|
||||
else {
|
||||
File resourcesDir = new File(classPath, "../../src/main/resources");
|
||||
propFile = new File(resourcesDir, fileName);
|
||||
if (propFile.exists() && propFile.isFile()) {
|
||||
inputStream = new FileInputStream(propFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inputStream != null) {
|
||||
properties.load(new InputStreamReader(inputStream, encoding));
|
||||
} else if (!fileName.contains("-")) {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream getResourceAsStreamByCurrentThread(String fileName) {
|
||||
ClassLoader ret = Thread.currentThread().getContextClassLoader();
|
||||
return ret != null ? ret.getResourceAsStream(fileName) : null;
|
||||
}
|
||||
|
||||
|
||||
private InputStream getResourceAsStreamByClassloader(String fileName) {
|
||||
ClassLoader ret = MyBatisFlexProps.class.getClassLoader();
|
||||
return ret != null ? ret.getResourceAsStream(fileName) : null;
|
||||
}
|
||||
|
||||
|
||||
private static String removeSlashEnd(String path) {
|
||||
if (path != null && (path.endsWith("/") || path.endsWith("\\"))) {
|
||||
return path.substring(0, path.length() - 1);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,360 @@
|
||||
/**
|
||||
* 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.processer;
|
||||
|
||||
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Table;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.Filer;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.JavaFileObject;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class QueryEntityProcesser extends AbstractProcessor {
|
||||
|
||||
private static final String classTableTemplate = "package @package;\n" +
|
||||
"\n" +
|
||||
"import com.mybatisflex.core.querywrapper.QueryColumn;\n" +
|
||||
"import com.mybatisflex.core.table.TableDef;\n" +
|
||||
"\n" +
|
||||
"// Auto generate by mybatis-flex, do not modify it.\n" +
|
||||
"public class Tables {\n" +
|
||||
"@classesInfo" +
|
||||
"}\n";
|
||||
|
||||
private static final String tableDefTemplate = "\n\n public static final @entityClassTableDef @tableField = new @entityClassTableDef(\"@tableName\");\n";
|
||||
|
||||
|
||||
private static final String classTemplate = "\n" +
|
||||
" public static class @entityClassTableDef extends TableDef {\n" +
|
||||
"\n" +
|
||||
"@queryColumns" +
|
||||
"@allColumns" +
|
||||
"\n" +
|
||||
" public @entityClassTableDef(String tableName) {\n" +
|
||||
" super(tableName);\n" +
|
||||
" }\n" +
|
||||
" }\n";
|
||||
|
||||
|
||||
private static final String columnsTemplate = " public QueryColumn @property = new QueryColumn(this, \"@columnName\");\n";
|
||||
private static final String allColumnsTemplate = "\n public QueryColumn[] ALL_COLUMNS = new QueryColumn[]{@allColumns};\n\n";
|
||||
|
||||
protected Filer filer;
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnvironment) {
|
||||
super.init(processingEnvironment);
|
||||
this.filer = processingEnvironment.getFiler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
|
||||
if (!roundEnv.processingOver()) {
|
||||
|
||||
MyBatisFlexProps props = new MyBatisFlexProps("mybatis-flex.properties");
|
||||
|
||||
String enable = props.getProperties().getProperty("processer.enable", "");
|
||||
if ("false".equalsIgnoreCase(enable)) {
|
||||
return true;
|
||||
}
|
||||
String genPath = props.getProperties().getProperty("processer.genPath", "");
|
||||
final String genPackage = props.getProperties().getProperty("processer.package");
|
||||
String className = props.getProperties().getProperty("processer.className", "Tables");
|
||||
|
||||
StringBuilder guessPackage = new StringBuilder();
|
||||
|
||||
|
||||
StringBuilder tablesContent = new StringBuilder();
|
||||
roundEnv.getElementsAnnotatedWith(Table.class).forEach((Consumer<Element>) entityClassElement -> {
|
||||
|
||||
Table table = entityClassElement.getAnnotation(Table.class);
|
||||
|
||||
//init genPackage
|
||||
if ((genPackage == null || genPackage.trim().length() == 0)
|
||||
&& guessPackage.length() == 0) {
|
||||
String entityClassName = entityClassElement.toString();
|
||||
if (!entityClassName.contains(".")) {
|
||||
guessPackage.append("table");// = "table";
|
||||
} else {
|
||||
guessPackage.append(entityClassName.substring(0, entityClassName.lastIndexOf(".")) + ".table");
|
||||
}
|
||||
}
|
||||
|
||||
String tableName = table != null && table.value().trim().length() != 0
|
||||
? table.value()
|
||||
: firstCharToLowerCase(entityClassElement.getSimpleName().toString());
|
||||
|
||||
|
||||
Map<String, String> propertyAndColumns = new LinkedHashMap<>();
|
||||
|
||||
TypeElement classElement = (TypeElement) entityClassElement;
|
||||
for (Element fieldElement : classElement.getEnclosedElements()) {
|
||||
|
||||
//all fields
|
||||
if (ElementKind.FIELD == fieldElement.getKind()) {
|
||||
Column column = fieldElement.getAnnotation(Column.class);
|
||||
if (column != null && column.ignore()) {
|
||||
continue;
|
||||
}
|
||||
String columnName = column != null && column.value().trim().length() > 0 ? column.value() : camelToUnderline(fieldElement.toString());
|
||||
propertyAndColumns.put(fieldElement.toString(), columnName);
|
||||
}
|
||||
}
|
||||
|
||||
String entityClassName = entityClassElement.getSimpleName().toString();
|
||||
tablesContent.append(buildClass(entityClassName, tableName, propertyAndColumns));
|
||||
});
|
||||
|
||||
if (tablesContent.length() > 0) {
|
||||
String realGenPackage = genPackage == null || genPackage.trim().length() == 0 ? guessPackage.toString() : genPackage;
|
||||
genClass(genPath, realGenPackage, className, tablesContent.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String buildClass(String entityClass, String tableName, Map<String, String> propertyAndColumns) {
|
||||
|
||||
// tableDefTemplate = " public static final @entityClassTableDef @tableField = new @entityClassTableDef(\"@tableName\");\n";
|
||||
|
||||
String tableDef = tableDefTemplate.replace("@entityClass", entityClass)
|
||||
.replace("@tableField", entityClass.toUpperCase())
|
||||
.replace("@tableName", tableName);
|
||||
|
||||
|
||||
//columnsTemplate = " public QueryColumn @property = new QueryColumn(this, \"@columnName\");\n";
|
||||
StringBuilder queryColumns = new StringBuilder();
|
||||
propertyAndColumns.forEach((property, column) ->
|
||||
queryColumns.append(columnsTemplate
|
||||
.replace("@property", camelToUnderline(property).toUpperCase())
|
||||
.replace("@columnName", column)
|
||||
));
|
||||
|
||||
|
||||
// public QueryColumn[] ALL_COLUMNS = new QueryColumn[]{@allColumns};
|
||||
StringJoiner allColumns = new StringJoiner(", ");
|
||||
propertyAndColumns.forEach((property, column) -> allColumns.add(camelToUnderline(property).toUpperCase()));
|
||||
String allColumnsString = allColumnsTemplate.replace("@allColumns", allColumns.toString());
|
||||
|
||||
|
||||
// classTemplate = "\n" +
|
||||
// " public static class @entityClassTableDef extends TableDef {\n" +
|
||||
// "\n" +
|
||||
// "@queryColumns" +
|
||||
// "@allColumns" +
|
||||
// "\n" +
|
||||
// " public @entityClassTableDef(String tableName) {\n" +
|
||||
// " super(tableName);\n" +
|
||||
// " }\n" +
|
||||
// " }\n";
|
||||
|
||||
String tableClass = classTemplate.replace("@entityClass", entityClass)
|
||||
.replace("@queryColumns", queryColumns)
|
||||
.replace("@allColumns", allColumnsString);
|
||||
|
||||
return tableDef + tableClass;
|
||||
}
|
||||
|
||||
|
||||
private void genClass(String genBasePath, String genPackageName, String className, String classContent) {
|
||||
String genContent = classTableTemplate.replace("@package", genPackageName)
|
||||
.replace("@classesInfo", classContent);
|
||||
|
||||
Writer writer = null;
|
||||
try {
|
||||
JavaFileObject sourceFile = filer.createSourceFile(genPackageName + "." + className);
|
||||
if (genBasePath == null || genBasePath.trim().length() == 0) {
|
||||
writer = sourceFile.openWriter();
|
||||
writer.write(genContent);
|
||||
writer.flush();
|
||||
|
||||
printMessage(">>>>> mybatis-flex success generate tables class: \n" + sourceFile.toUri());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
String defaultGenPath = sourceFile.toUri().getPath();
|
||||
|
||||
//真实的生成代码的目录
|
||||
String realPath;
|
||||
|
||||
//用户配置的路径为绝对路径
|
||||
if (isAbsolutePath(genBasePath)) {
|
||||
realPath = genBasePath;
|
||||
}
|
||||
//配置的是相对路径,那么则以项目根目录为相对路径
|
||||
else {
|
||||
String projectRootPath = getProjectRootPath(defaultGenPath);
|
||||
realPath = new File(projectRootPath, genBasePath).getAbsolutePath();
|
||||
}
|
||||
|
||||
//通过在 test/java 目录下执行编译生成的
|
||||
boolean fromTestSource = isFromTestSource(defaultGenPath);
|
||||
if (fromTestSource) {
|
||||
realPath = new File(realPath, "src/test/java").getAbsolutePath();
|
||||
} else {
|
||||
realPath = new File(realPath, "src/main/java").getAbsolutePath();
|
||||
}
|
||||
|
||||
File genJavaFile = new File(realPath, (genPackageName + "." + className).replace(".", "/") + ".java");
|
||||
if (!genJavaFile.getParentFile().exists() && !genJavaFile.getParentFile().mkdirs()) {
|
||||
System.out.println(">>>>>ERROR: can not mkdirs by mybatis-flex processer for: " + genJavaFile.getParentFile());
|
||||
return;
|
||||
}
|
||||
|
||||
writer = new PrintWriter(new FileOutputStream(genJavaFile));
|
||||
writer.write(classContent);
|
||||
writer.flush();
|
||||
|
||||
printMessage(">>>>> mybatis-flex success generate tables class: \n" + genJavaFile.toURI());
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void printMessage(String message) {
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, message);
|
||||
System.out.println(message);
|
||||
}
|
||||
|
||||
public static String firstCharToLowerCase(String str) {
|
||||
char firstChar = str.charAt(0);
|
||||
if (firstChar >= 'A' && firstChar <= 'Z') {
|
||||
char[] arr = str.toCharArray();
|
||||
arr[0] += ('a' - 'A');
|
||||
return new String(arr);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedAnnotationTypes() {
|
||||
Set<String> supportedAnnotationTypes = new HashSet<>();
|
||||
supportedAnnotationTypes.add(Table.class.getCanonicalName());
|
||||
return supportedAnnotationTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceVersion getSupportedSourceVersion() {
|
||||
return SourceVersion.latestSupported();
|
||||
}
|
||||
|
||||
|
||||
private boolean isFromTestSource(String path) {
|
||||
return path.contains("test-sources") || path.contains("test-annotations");
|
||||
}
|
||||
|
||||
|
||||
public static boolean isAbsolutePath(String path) {
|
||||
return path != null && (path.startsWith("/") || path.indexOf(":") > 0);
|
||||
}
|
||||
|
||||
public static String camelToUnderline(String string) {
|
||||
if (string == null || string.trim().length() == 0) {
|
||||
return "";
|
||||
}
|
||||
int len = string.length();
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = string.charAt(i);
|
||||
if (Character.isUpperCase(c) && i > 0) {
|
||||
sb.append('_');
|
||||
}
|
||||
sb.append(Character.toLowerCase(c));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取项目的根目录,也就是根节点 pom.xml 所在的目录
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String getProjectRootPath(String genFilePath) {
|
||||
File file = new File(genFilePath);
|
||||
int count = 20;
|
||||
return getProjectRootPath(file, count);
|
||||
}
|
||||
|
||||
|
||||
private String getProjectRootPath(File file, int count) {
|
||||
if (count <= 0) {
|
||||
return null;
|
||||
}
|
||||
if (file.isFile()) {
|
||||
return getProjectRootPath(file.getParentFile(), --count);
|
||||
} else {
|
||||
if (new File(file, "pom.xml").exists() && !new File(file.getParentFile(), "pom.xml").exists()) {
|
||||
return file.getAbsolutePath();
|
||||
} else {
|
||||
return getProjectRootPath(file.getParentFile(), --count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前 Maven 模块所在所在的目录
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String getModuleRootPath(String genFilePath) {
|
||||
File file = new File(genFilePath);
|
||||
int count = 20;
|
||||
return getModuleRootPath(file, count);
|
||||
}
|
||||
|
||||
|
||||
private String getModuleRootPath(File file, int count) {
|
||||
if (count <= 0) {
|
||||
return null;
|
||||
}
|
||||
if (file.isFile()) {
|
||||
return getModuleRootPath(file.getParentFile(), --count);
|
||||
} else {
|
||||
if (new File(file, "pom.xml").exists()) {
|
||||
return file.getAbsolutePath();
|
||||
} else {
|
||||
return getModuleRootPath(file.getParentFile(), --count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
com.mybatisflex.processer.QueryEntityProcesser
|
||||
19
mybatis-flex-codegen/pom.xml
Normal file
19
mybatis-flex-codegen/pom.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.0.0-beta.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>mybatis-flex-codegen</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
53
mybatis-flex-core/pom.xml
Normal file
53
mybatis-flex-core/pom.xml
Normal file
@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.0.0-beta.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>mybatis-flex-core</artifactId>
|
||||
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-annotation</artifactId>
|
||||
<version>1.0.0-beta.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,284 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.provider.EntitySqlProvider;
|
||||
import com.mybatisflex.core.querywrapper.QueryColumn;
|
||||
import com.mybatisflex.core.querywrapper.QueryWrapper;
|
||||
import com.mybatisflex.core.querywrapper.CPI;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
import org.apache.ibatis.builder.annotation.ProviderContext;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface BaseMapper<T> {
|
||||
|
||||
/**
|
||||
* 插入 entity 数据
|
||||
*
|
||||
* @param entity
|
||||
* @return 返回影响的行数
|
||||
* @see com.mybatisflex.core.provider.EntitySqlProvider#insert(Map, ProviderContext)
|
||||
*/
|
||||
@InsertProvider(type = EntitySqlProvider.class, method = "insert")
|
||||
int insert(@Param(FlexConsts.ENTITY) T entity);
|
||||
|
||||
|
||||
/**
|
||||
* 批量插入 entity 数据,只会根据第一条数据来构建插入的字段内容
|
||||
*
|
||||
* @param entities 插入的数据列表
|
||||
* @return 返回影响的行数
|
||||
* @see com.mybatisflex.core.provider.EntitySqlProvider#insertBatchWithFirstEntityColumns(Map, ProviderContext)
|
||||
*/
|
||||
@InsertProvider(type = EntitySqlProvider.class, method = "insertBatchWithFirstEntityColumns")
|
||||
int insertBatchWithFirstEntityColumns(@Param(FlexConsts.ENTITIES) List<T> entities);
|
||||
|
||||
/**
|
||||
* 根据 id 删除数据
|
||||
* 如果是多个主键的情况下,需要传入数组 new Object[]{100,101}
|
||||
*
|
||||
* @param id 主键数据
|
||||
* @return 返回影响的行数
|
||||
* @see com.mybatisflex.core.provider.EntitySqlProvider#deleteById(Map, ProviderContext)
|
||||
*/
|
||||
@DeleteProvider(type = EntitySqlProvider.class, method = "deleteById")
|
||||
int deleteById(@Param(FlexConsts.PRIMARY_VALUE) Serializable id);
|
||||
|
||||
|
||||
/**
|
||||
* 根据多个 id 批量删除数据
|
||||
*
|
||||
* @param ids ids 列表
|
||||
* @return 返回影响的行数
|
||||
* @see com.mybatisflex.core.provider.EntitySqlProvider#deleteBatchByIds(Map, ProviderContext)
|
||||
*/
|
||||
@DeleteProvider(type = EntitySqlProvider.class, method = "deleteBatchByIds")
|
||||
int deleteBatchByIds(@Param(FlexConsts.PRIMARY_VALUE) Collection<? extends Serializable> ids);
|
||||
|
||||
|
||||
/**
|
||||
* 根据 map 构建的条件来删除数据
|
||||
*
|
||||
* @param whereConditions
|
||||
* @return 返回影响的行数
|
||||
*/
|
||||
default int deleteByMap(Map<String, Object> whereConditions) {
|
||||
return deleteByQuery(QueryWrapper.create().where(whereConditions));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 query 构建的条件来数据吗
|
||||
*
|
||||
* @param queryWrapper query 条件
|
||||
* @return 返回影响的行数
|
||||
* @see com.mybatisflex.core.provider.EntitySqlProvider#deleteByQuery(Map, ProviderContext)
|
||||
*/
|
||||
@DeleteProvider(type = EntitySqlProvider.class, method = "deleteByQuery")
|
||||
int deleteByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
/**
|
||||
* 根据主键来更新数据,若数据为 null,也会更新到数据库
|
||||
*
|
||||
* @param entity 数据内容,必须包含有主键
|
||||
* @return 返回影响的行数
|
||||
*/
|
||||
default int update(T entity) {
|
||||
return update(entity, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据主键来更新数据到数据库
|
||||
*
|
||||
* @param entity 数据内容
|
||||
* @param ignoreNulls 是否忽略空内容字段
|
||||
* @return 返回影响的行数
|
||||
* @see com.mybatisflex.core.provider.EntitySqlProvider#update(Map, ProviderContext)
|
||||
*/
|
||||
@UpdateProvider(type = EntitySqlProvider.class, method = "update")
|
||||
int update(@Param(FlexConsts.ENTITY) T entity, @Param(FlexConsts.IGNORE_NULLS) boolean ignoreNulls);
|
||||
|
||||
|
||||
/**
|
||||
* 根据 map 构建的条件来更新数据
|
||||
*
|
||||
* @param entity 数据
|
||||
* @param map where 条件内容
|
||||
* @return 返回影响的行数
|
||||
*/
|
||||
default int updateByMap(T entity, Map<String, Object> map) {
|
||||
return updateByQuery(entity, QueryWrapper.create().where(map));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据 query 构建的条件来更新数据
|
||||
*
|
||||
* @param entity 数据内容
|
||||
* @param queryWrapper query 条件
|
||||
* @return 返回影响的行数
|
||||
*/
|
||||
|
||||
default int updateByQuery(@Param(FlexConsts.ENTITY) T entity, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper) {
|
||||
return updateByQuery(entity, true, queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 query 构建的条件来更新数据
|
||||
*
|
||||
* @param entity 数据内容
|
||||
* @param ignoreNulls 是否忽略空值
|
||||
* @param queryWrapper query 条件
|
||||
* @return
|
||||
* @see com.mybatisflex.core.provider.EntitySqlProvider#updateByQuery(Map, ProviderContext)
|
||||
*/
|
||||
@UpdateProvider(type = EntitySqlProvider.class, method = "updateByQuery")
|
||||
int updateByQuery(@Param(FlexConsts.ENTITY) T entity, @Param("ignoreNulls") boolean ignoreNulls, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
/**
|
||||
* 根据主键来选择数据
|
||||
*
|
||||
* @param id 多个主键
|
||||
* @return entity
|
||||
* @see com.mybatisflex.core.provider.EntitySqlProvider#selectOneById(Map, ProviderContext)
|
||||
*/
|
||||
@SelectProvider(type = EntitySqlProvider.class, method = "selectOneById")
|
||||
T selectOneById(@Param(FlexConsts.PRIMARY_VALUE) Serializable id);
|
||||
|
||||
|
||||
/**
|
||||
* 根据 map 构建的条件来查询数据
|
||||
*
|
||||
* @param whereConditions where 条件
|
||||
* @return entity 数据
|
||||
*/
|
||||
default T selectOneByMap(Map<String, Object> whereConditions) {
|
||||
return selectOneByQuery(QueryWrapper.create().where(whereConditions));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据 queryWrapper 构建的条件来查询 1 条数据
|
||||
*
|
||||
* @param queryWrapper query 条件
|
||||
* @return entity 数据
|
||||
*/
|
||||
default T selectOneByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper) {
|
||||
List<T> entities = selectListByQuery(queryWrapper.limit(1));
|
||||
if (entities == null || entities.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return entities.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据多个主键来查询多条数据
|
||||
*
|
||||
* @param ids 主键列表
|
||||
* @return 数据列表
|
||||
* @see com.mybatisflex.core.provider.EntitySqlProvider#selectListByIds(Map, ProviderContext)
|
||||
*/
|
||||
@SelectProvider(type = EntitySqlProvider.class, method = "selectListByIds")
|
||||
List<T> selectListByIds(@Param(FlexConsts.PRIMARY_VALUE) Collection<? extends Serializable> ids);
|
||||
|
||||
|
||||
/**
|
||||
* 根据 map 来构建查询条件,查询多条数据
|
||||
*
|
||||
* @param whereConditions 条件列表
|
||||
* @return 数据列表
|
||||
*/
|
||||
default List<T> selectListByMap(Map<String, Object> whereConditions) {
|
||||
return selectListByQuery(QueryWrapper.create().where(whereConditions));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据 query 来构建条件查询数据列表
|
||||
*
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 数据列表
|
||||
* @see com.mybatisflex.core.provider.EntitySqlProvider#selectListByQuery(Map, ProviderContext)
|
||||
*/
|
||||
@SelectProvider(type = EntitySqlProvider.class, method = "selectListByQuery")
|
||||
List<T> selectListByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
/**
|
||||
* 根据 queryWrapper 来查询数据量
|
||||
*
|
||||
* @param queryWrapper
|
||||
* @return 数据量
|
||||
* @see EntitySqlProvider#selectCountByQuery(Map, ProviderContext)
|
||||
*/
|
||||
@SelectProvider(type = EntitySqlProvider.class, method = "selectCountByQuery")
|
||||
long selectCountByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param pageNumber 当前页码
|
||||
* @param pageSize 每页的数据量
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 返回 Page 数据
|
||||
*/
|
||||
default Page<T> paginate(int pageNumber, int pageSize, QueryWrapper queryWrapper) {
|
||||
Page<T> page = new Page<>(pageNumber, pageSize);
|
||||
return paginate(page, queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param page page,其包含了页码、每页的数据量,可能包含数据总量
|
||||
* @param queryWrapper 查询条件
|
||||
* @return page 数据
|
||||
*/
|
||||
default Page<T> paginate(@Param("page") Page<T> page, @Param("query") QueryWrapper queryWrapper) {
|
||||
List<QueryColumn> groupByColumns = CPI.getGroupByColumns(queryWrapper);
|
||||
|
||||
// 只有 totalRow 小于 0 的时候才会去查询总量
|
||||
// 这样方便用户做总数缓存,而非每次都要去查询总量
|
||||
// 一般的分页场景中,只有第一页的时候有必要去查询总量,第二页以后是不需要的
|
||||
if (page.getTotalRow() < 0) {
|
||||
//清除group by 去查询数据
|
||||
CPI.setGroupByColumns(queryWrapper, null);
|
||||
long count = selectCountByQuery(queryWrapper);
|
||||
page.setTotalRow(count);
|
||||
}
|
||||
|
||||
if (page.getTotalRow() == 0 || page.getPageNumber() > page.getTotalPage()) {
|
||||
return page;
|
||||
}
|
||||
|
||||
//恢复数量查询清除的 groupBy
|
||||
CPI.setGroupByColumns(queryWrapper, groupByColumns);
|
||||
int offset = page.getPageSize() * (page.getPageNumber() - 1);
|
||||
queryWrapper.limit(offset, page.getPageSize());
|
||||
List<T> rows = selectListByQuery(queryWrapper);
|
||||
page.setList(rows);
|
||||
return page;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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-Flex 可能用到的静态常量
|
||||
*/
|
||||
public class FlexConsts {
|
||||
public static final String VERSION = "1.0.0-beta.1";
|
||||
|
||||
public static final String DEFAULT_PRIMARY_FIELD = "id";
|
||||
|
||||
public static final String SQL = "$$sql";
|
||||
public static final String SQL_ARGS = "$$sql_args";
|
||||
public static final String TABLE_NAME = "$$tableName";
|
||||
public static final String PRIMARY_KEY = "$$primaryKey";
|
||||
public static final String PRIMARY_VALUE = "$$primaryValue";
|
||||
|
||||
public static final String QUERY = "$$query";
|
||||
public static final String ROW = "$$row";
|
||||
public static final String ROWS = "$$rows";
|
||||
|
||||
public static final String ENTITY = "$$entity";
|
||||
public static final String ENTITIES = "$$entities";
|
||||
public static final String IGNORE_NULLS = "$$ignoreNulls";
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import com.mybatisflex.core.dialect.DbType;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 全局配置文件
|
||||
*/
|
||||
public class FlexGlobalConfig {
|
||||
|
||||
/**
|
||||
* 默认使用 Mysql 数据库类型
|
||||
*/
|
||||
private DbType dbType = DbType.MYSQL;
|
||||
|
||||
/**
|
||||
* 创建好的 sqlSessionFactory
|
||||
*/
|
||||
private SqlSessionFactory sqlSessionFactory;
|
||||
|
||||
|
||||
|
||||
public DbType getDbType() {
|
||||
return dbType;
|
||||
}
|
||||
|
||||
public void setDbType(DbType dbType) {
|
||||
this.dbType = dbType;
|
||||
}
|
||||
|
||||
public SqlSessionFactory getSqlSessionFactory() {
|
||||
return sqlSessionFactory;
|
||||
}
|
||||
|
||||
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
|
||||
this.sqlSessionFactory = sqlSessionFactory;
|
||||
}
|
||||
|
||||
|
||||
/////static factory methods/////
|
||||
private static ConcurrentHashMap<String, FlexGlobalConfig> globalConfigs = new ConcurrentHashMap();
|
||||
private static FlexGlobalConfig defaultConfig;
|
||||
|
||||
public static FlexGlobalConfig getDefaultConfig() {
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
public static void setDefaultConfig(FlexGlobalConfig config) {
|
||||
defaultConfig = config;
|
||||
}
|
||||
|
||||
public static FlexGlobalConfig getConfig(String environmentId) {
|
||||
return globalConfigs.get(environmentId);
|
||||
}
|
||||
|
||||
public static void setConfig(String id, FlexGlobalConfig config) {
|
||||
if (getDefaultConfig() == null) {
|
||||
setDefaultConfig(config);
|
||||
}
|
||||
globalConfigs.put(id, config);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,160 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import com.mybatisflex.core.dialect.DbType;
|
||||
import com.mybatisflex.core.dialect.DialectFactory;
|
||||
import com.mybatisflex.core.mybatis.FlexConfiguration;
|
||||
import com.mybatisflex.core.mybatis.FlexSqlSessionFactoryBuilder;
|
||||
import org.apache.ibatis.logging.LogFactory;
|
||||
import org.apache.ibatis.mapping.Environment;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.apache.ibatis.transaction.TransactionFactory;
|
||||
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* MybatisFlex 的启动类
|
||||
*
|
||||
* <code>
|
||||
* MybatisFlexBootstrap.getInstance()
|
||||
* .setDatasource(...)
|
||||
* .addMapper(...)
|
||||
* .start();
|
||||
* <p>
|
||||
* <p>
|
||||
* MybatisFlexBootstrap.getInstance()
|
||||
* .execute(...)
|
||||
* </code>
|
||||
*/
|
||||
public class MybatisFlexBootstrap {
|
||||
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
|
||||
private String environmentId = "mybatis-flex";
|
||||
private TransactionFactory transactionFactory;
|
||||
|
||||
private DataSource dataSource;
|
||||
private Configuration configuration;
|
||||
private List<Class<?>> mappers;
|
||||
|
||||
private DbType dbType;
|
||||
private SqlSessionFactory sqlSessionFactory;
|
||||
|
||||
|
||||
private static volatile MybatisFlexBootstrap instance;
|
||||
|
||||
public static MybatisFlexBootstrap getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (MybatisFlexBootstrap.class) {
|
||||
if (instance == null) {
|
||||
instance = new MybatisFlexBootstrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
public MybatisFlexBootstrap setDatasource(DataSource datasource) {
|
||||
this.dataSource = datasource;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MybatisFlexBootstrap setConfiguration(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MybatisFlexBootstrap setTransactionFactory(TransactionFactory transactionFactory) {
|
||||
this.transactionFactory = transactionFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public MybatisFlexBootstrap setEnvironmentId(String environmentId) {
|
||||
this.environmentId = environmentId;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public <T> MybatisFlexBootstrap addMapper(Class<T> type) {
|
||||
if (this.mappers == null) {
|
||||
mappers = new ArrayList<>();
|
||||
}
|
||||
mappers.add(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public MybatisFlexBootstrap start() {
|
||||
if (started.compareAndSet(false, true)) {
|
||||
if (dataSource == null) {
|
||||
throw new IllegalStateException("dataSource can not be null.");
|
||||
}
|
||||
|
||||
//init configuration
|
||||
if (configuration == null) {
|
||||
|
||||
if (transactionFactory == null) {
|
||||
transactionFactory = new JdbcTransactionFactory();
|
||||
}
|
||||
|
||||
Environment environment = new Environment(environmentId, transactionFactory, dataSource);
|
||||
configuration = new FlexConfiguration(environment);
|
||||
}
|
||||
|
||||
//init mappers
|
||||
if (mappers != null) {
|
||||
mappers.forEach(configuration::addMapper);
|
||||
}
|
||||
|
||||
//init sqlSessionFactory
|
||||
this.sqlSessionFactory = new FlexSqlSessionFactoryBuilder().build(configuration);
|
||||
|
||||
//init dbType
|
||||
this.dbType = FlexGlobalConfig.getConfig(environmentId).getDbType();
|
||||
|
||||
LogFactory.getLog(MybatisFlexBootstrap.class).debug("Mybatis-Flex has started.");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public <R, T> R execute(Class<T> mapperClass, Function<T, R> function) {
|
||||
try (SqlSession sqlSession = openSession()) {
|
||||
DialectFactory.setHintDbType(dbType);
|
||||
T mapper = sqlSession.getMapper(mapperClass);
|
||||
return function.apply(mapper);
|
||||
} finally {
|
||||
DialectFactory.clearHintDbType();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private SqlSession openSession() {
|
||||
return sqlSessionFactory.openSession(configuration.getDefaultExecutorType());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,576 @@
|
||||
/**
|
||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.mybatisflex.core.dialect;
|
||||
|
||||
import com.mybatisflex.core.querywrapper.*;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
import com.mybatisflex.core.util.ArrayUtil;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 通用的方言设计,其他方言可以继承于当前 CommonsDialectImpl
|
||||
* 创建或获取方言请参考 {@link com.mybatisflex.core.dialect.DialectFactory}
|
||||
*/
|
||||
public class CommonsDialectImpl implements IDialect {
|
||||
|
||||
protected KeywordWrap keywordWrap = KeywordWrap.NONE;
|
||||
private LimitOffsetProcesser limitOffsetProcesser = LimitOffsetProcesser.MYSQL;
|
||||
|
||||
public CommonsDialectImpl() {
|
||||
}
|
||||
|
||||
public CommonsDialectImpl(LimitOffsetProcesser limitOffsetProcesser) {
|
||||
this.limitOffsetProcesser = limitOffsetProcesser;
|
||||
}
|
||||
|
||||
public CommonsDialectImpl(KeywordWrap keywordWrap, LimitOffsetProcesser limitOffsetProcesser) {
|
||||
this.keywordWrap = keywordWrap;
|
||||
this.limitOffsetProcesser = limitOffsetProcesser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String wrap(String keyword) {
|
||||
return keywordWrap.wrap(keyword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forInsertRow(String tableName, Row row) {
|
||||
StringBuilder fields = new StringBuilder();
|
||||
StringBuilder questions = new StringBuilder();
|
||||
|
||||
Set<String> attrs = row.obtainModifyAttrs();
|
||||
int index = 0;
|
||||
for (String attr : attrs) {
|
||||
fields.append(wrap(attr));
|
||||
questions.append("?");
|
||||
if (index != attrs.size() - 1) {
|
||||
fields.append(", ");
|
||||
questions.append(", ");
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
String sql = "INSERT INTO " + wrap(tableName) +
|
||||
"(" + fields + ") VALUES " +
|
||||
"(" + questions + ")";
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String forInsertBatchWithFirstRowColumns(String tableName, List<Row> rows) {
|
||||
StringBuilder fields = new StringBuilder();
|
||||
StringBuilder questions = new StringBuilder();
|
||||
|
||||
Row firstRow = rows.get(0);
|
||||
Set<String> attrs = firstRow.obtainModifyAttrs();
|
||||
int index = 0;
|
||||
for (String attr : attrs) {
|
||||
fields.append(wrap(attr));
|
||||
if (index != attrs.size() - 1) {
|
||||
fields.append(", ");
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rows.size(); i++) {
|
||||
questions.append(buildQuestion(attrs.size(), true));
|
||||
if (i != rows.size() - 1) {
|
||||
questions.append(",");
|
||||
}
|
||||
}
|
||||
|
||||
String sql = "INSERT INTO " + wrap(tableName) +
|
||||
"(" + fields + ") VALUES " + questions;
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String forDeleteById(String tableName, String[] primaryKeys) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("DELETE FROM ");
|
||||
sql.append(wrap(tableName));
|
||||
sql.append(" WHERE ");
|
||||
for (int i = 0; i < primaryKeys.length; i++) {
|
||||
if (i > 0) {
|
||||
sql.append(" AND ");
|
||||
}
|
||||
sql.append(wrap(primaryKeys[i])).append(" = ?");
|
||||
}
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String forDeleteBatchByIds(String tableName, String[] primaryKeys, Object[] ids) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("DELETE FROM ");
|
||||
sql.append(wrap(tableName));
|
||||
sql.append(" WHERE ");
|
||||
|
||||
//多主键的场景
|
||||
if (primaryKeys.length > 1) {
|
||||
for (int i = 0; i < ids.length / primaryKeys.length; i++) {
|
||||
if (i > 0) {
|
||||
sql.append(" OR ");
|
||||
}
|
||||
sql.append("(");
|
||||
for (int j = 0; j < primaryKeys.length; j++) {
|
||||
if (j > 0) {
|
||||
sql.append(" AND ");
|
||||
}
|
||||
sql.append(wrap(primaryKeys[j])).append(" = ?");
|
||||
}
|
||||
sql.append(")");
|
||||
}
|
||||
}
|
||||
// 单主键
|
||||
else {
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
if (i > 0) {
|
||||
sql.append(" OR ");
|
||||
}
|
||||
sql.append(wrap(primaryKeys[0])).append(" = ?");
|
||||
}
|
||||
}
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forDeleteByQuery(QueryWrapper queryWrapper) {
|
||||
return buildDeleteSql(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forUpdateById(String tableName, Row row) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
Set<String> modifyAttrs = row.obtainModifyAttrs();
|
||||
String[] primaryKeys = row.obtainsPrimaryKeyStrings();
|
||||
|
||||
sql.append("UPDATE ").append(wrap(tableName)).append(" SET ");
|
||||
int index = 0;
|
||||
for (Map.Entry<String, Object> e : row.entrySet()) {
|
||||
String colName = e.getKey();
|
||||
if (modifyAttrs.contains(colName) && !ArrayUtil.contains(primaryKeys, colName)) {
|
||||
if (index > 0) {
|
||||
sql.append(", ");
|
||||
}
|
||||
sql.append(wrap(colName)).append(" = ? ");
|
||||
index++;
|
||||
}
|
||||
}
|
||||
sql.append(" WHERE ");
|
||||
for (int i = 0; i < primaryKeys.length; i++) {
|
||||
if (i > 0) {
|
||||
sql.append(" AND ");
|
||||
}
|
||||
sql.append(wrap(primaryKeys[i])).append(" = ?");
|
||||
}
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forUpdateByQuery(String tableName, Row row, QueryWrapper queryWrapper) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
Set<String> modifyAttrs = row.obtainModifyAttrs();
|
||||
|
||||
sql.append("UPDATE ").append(wrap(tableName)).append(" SET ");
|
||||
int index = 0;
|
||||
for (String modifyAttr : modifyAttrs) {
|
||||
if (index > 0) {
|
||||
sql.append(", ");
|
||||
}
|
||||
sql.append(wrap(modifyAttr)).append(" = ? ");
|
||||
index++;
|
||||
}
|
||||
|
||||
String whereConditionSql = buildWhereConditionSql(queryWrapper);
|
||||
if (StringUtil.isNotBlank(whereConditionSql)) {
|
||||
sql.append(" WHERE ").append(whereConditionSql);
|
||||
}
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forUpdateBatchById(String tableName, List<Row> rows) {
|
||||
if (rows.size() == 1) {
|
||||
return forUpdateById(tableName, rows.get(0));
|
||||
}
|
||||
StringBuilder sql = new StringBuilder();
|
||||
for (Row row : rows) {
|
||||
sql.append(forUpdateById(tableName, row)).append("; ");
|
||||
}
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String forSelectOneById(String tableName, String[] primaryKeys, Object[] primaryValues) {
|
||||
StringBuilder sql = new StringBuilder("SELECT * FROM ");
|
||||
sql.append(wrap(tableName)).append(" WHERE ");
|
||||
for (int i = 0; i < primaryKeys.length; i++) {
|
||||
if (i > 0) {
|
||||
sql.append(" AND ");
|
||||
}
|
||||
sql.append('`').append(primaryKeys[i]).append("` = ?");
|
||||
}
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forSelectListByQuery(QueryWrapper queryWrapper) {
|
||||
return buildSelectSql(queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String forSelectCountByQuery(QueryWrapper queryWrapper) {
|
||||
return buildSelectCountSql(queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
////////////build query sql///////
|
||||
@Override
|
||||
public String buildSelectSql(QueryWrapper queryWrapper) {
|
||||
List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
|
||||
List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
|
||||
List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
|
||||
|
||||
List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
|
||||
|
||||
StringBuilder sqlBuilder = new StringBuilder("SELECT ");
|
||||
if (selectColumns == null || selectColumns.isEmpty()) {
|
||||
sqlBuilder.append("*");
|
||||
} else {
|
||||
int index = 0;
|
||||
|
||||
for (QueryColumn selectColumn : selectColumns) {
|
||||
String selectColumnSql = CPI.toSelectSql(selectColumn, allTables, this);
|
||||
sqlBuilder.append(selectColumnSql);
|
||||
if (index != selectColumns.size() - 1) {
|
||||
sqlBuilder.append(", ");
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
sqlBuilder.append(" FROM ").append(StringUtil.join(",", queryTables, queryTable -> queryTable.toSql(this)));
|
||||
|
||||
buildJoinSql(sqlBuilder, queryWrapper, allTables);
|
||||
buildWhereSql(sqlBuilder, queryWrapper, allTables);
|
||||
buildGroupBySql(sqlBuilder, queryWrapper, allTables);
|
||||
buildHavingSql(sqlBuilder, queryWrapper, allTables);
|
||||
buildOrderBySql(sqlBuilder, queryWrapper, allTables);
|
||||
|
||||
Integer limitRows = CPI.getLimitRows(queryWrapper);
|
||||
Integer limitOffset = CPI.getLimitOffset(queryWrapper);
|
||||
if (limitRows != null || limitOffset != null) {
|
||||
sqlBuilder = buildLimitOffsetSql(sqlBuilder, queryWrapper, limitRows, limitOffset);
|
||||
}
|
||||
|
||||
return sqlBuilder.toString();
|
||||
}
|
||||
|
||||
|
||||
@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);
|
||||
buildGroupBySql(sqlBuilder, queryWrapper, allTables);
|
||||
buildHavingSql(sqlBuilder, queryWrapper, allTables);
|
||||
|
||||
// ignore orderBy and limit
|
||||
// buildOrderBySql(sqlBuilder, queryWrapper);
|
||||
// buildLimitSql(sqlBuilder, queryWrapper);
|
||||
|
||||
return sqlBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildDeleteSql(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("DELETE FROM ");
|
||||
sqlBuilder.append(StringUtil.join(",", queryTables, queryTable -> queryTable.toSql(this)));
|
||||
|
||||
buildJoinSql(sqlBuilder, queryWrapper, allTables);
|
||||
buildWhereSql(sqlBuilder, queryWrapper, allTables);
|
||||
buildGroupBySql(sqlBuilder, queryWrapper, allTables);
|
||||
buildHavingSql(sqlBuilder, queryWrapper, allTables);
|
||||
|
||||
//ignore orderBy and limit
|
||||
//buildOrderBySql(sqlBuilder, queryWrapper);
|
||||
//buildLimitSql(sqlBuilder, queryWrapper);
|
||||
|
||||
return sqlBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildWhereConditionSql(QueryWrapper queryWrapper) {
|
||||
QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(queryWrapper);
|
||||
return whereQueryCondition != null ? whereQueryCondition.toSql(CPI.getQueryTables(queryWrapper), this) : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forInsertEntity(TableInfo tableInfo, Object entity) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("INSERT INTO ").append(wrap(tableInfo.getTableName()));
|
||||
String[] insertColumns = tableInfo.obtainInsertColumns();
|
||||
sql.append("(").append(StringUtil.join(",", insertColumns)).append(")");
|
||||
sql.append(" VALUES ");
|
||||
sql.append(buildQuestion(insertColumns.length, true));
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forInsertBatchWithFirstEntityColumns(TableInfo tableInfo, List<Object> entities) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("INSERT INTO ").append(wrap(tableInfo.getTableName()));
|
||||
String[] insertColumns = tableInfo.obtainInsertColumns();
|
||||
sql.append("(").append(StringUtil.join(",", insertColumns)).append(")");
|
||||
sql.append(" VALUES ");
|
||||
|
||||
for (int i = 0; i < entities.size(); i++) {
|
||||
sql.append(buildQuestion(insertColumns.length, true));
|
||||
if (i != entities.size() - 1) {
|
||||
sql.append(", ");
|
||||
}
|
||||
}
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forDeleteEntityById(TableInfo tableInfo) {
|
||||
return forDeleteById(tableInfo.getTableName(), tableInfo.getPrimaryKeys());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forDeleteEntityBatchById(TableInfo tableInfo, Object[] primaryValues) {
|
||||
return forDeleteBatchByIds(tableInfo.getTableName(), tableInfo.getPrimaryKeys(), primaryValues);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String forUpdateEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
Set<String> modifyAttrs = tableInfo.obtainUpdateColumns(entity, ignoreNulls, false);
|
||||
String[] primaryKeys = tableInfo.getPrimaryKeys();
|
||||
|
||||
sql.append("UPDATE ").append(wrap(tableInfo.getTableName())).append(" SET ");
|
||||
int index = 0;
|
||||
for (String modifyAttr : modifyAttrs) {
|
||||
if (index > 0) {
|
||||
sql.append(", ");
|
||||
}
|
||||
sql.append(wrap(modifyAttr)).append(" = ? ");
|
||||
index++;
|
||||
}
|
||||
sql.append(" WHERE ");
|
||||
for (int i = 0; i < primaryKeys.length; i++) {
|
||||
if (i > 0) {
|
||||
sql.append(" AND ");
|
||||
}
|
||||
sql.append(wrap(primaryKeys[i])).append(" = ?");
|
||||
}
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forUpdateEntityByQuery(TableInfo tableInfo, Object entity, boolean ignoreNulls, QueryWrapper queryWrapper) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
Set<String> modifyAttrs = tableInfo.obtainUpdateColumns(entity, ignoreNulls, true);
|
||||
|
||||
sql.append("UPDATE ").append(wrap(tableInfo.getTableName())).append(" SET ");
|
||||
int index = 0;
|
||||
for (String modifyAttr : modifyAttrs) {
|
||||
if (index > 0) {
|
||||
sql.append(", ");
|
||||
}
|
||||
sql.append(wrap(modifyAttr)).append(" = ? ");
|
||||
index++;
|
||||
}
|
||||
sql.append(" WHERE ");
|
||||
sql.append(buildWhereConditionSql(queryWrapper));
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String forSelectOneEntityById(TableInfo tableInfo) {
|
||||
StringBuilder sql = new StringBuilder("SELECT * FROM ");
|
||||
sql.append(wrap(tableInfo.getTableName()));
|
||||
sql.append(" WHERE ");
|
||||
String[] pKeys = tableInfo.getPrimaryKeys();
|
||||
for (int i = 0; i < pKeys.length; i++) {
|
||||
if (i > 0) {
|
||||
sql.append(" AND ");
|
||||
}
|
||||
sql.append(wrap(pKeys[i])).append(" = ?");
|
||||
}
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String forSelectEntityListByIds(TableInfo tableInfo, Object[] primaryValues) {
|
||||
StringBuilder sql = new StringBuilder("SELECT * FROM ");
|
||||
sql.append(wrap(tableInfo.getTableName()));
|
||||
sql.append(" WHERE ");
|
||||
String[] primaryKeys = tableInfo.getPrimaryKeys();
|
||||
|
||||
//多主键的场景
|
||||
if (primaryKeys.length > 1) {
|
||||
for (int i = 0; i < primaryValues.length / primaryKeys.length; i++) {
|
||||
if (i > 0) {
|
||||
sql.append(" OR ");
|
||||
}
|
||||
sql.append("(");
|
||||
for (int j = 0; j < primaryKeys.length; j++) {
|
||||
if (j > 0) {
|
||||
sql.append(" AND ");
|
||||
}
|
||||
sql.append(wrap(primaryKeys[j])).append(" = ?");
|
||||
}
|
||||
sql.append(")");
|
||||
}
|
||||
}
|
||||
// 单主键
|
||||
else {
|
||||
for (int i = 0; i < primaryValues.length; i++) {
|
||||
if (i > 0) {
|
||||
sql.append(" OR ");
|
||||
}
|
||||
sql.append(wrap(primaryKeys[0])).append(" = ?");
|
||||
}
|
||||
}
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
|
||||
protected void buildJoinSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
|
||||
List<Join> joins = CPI.getJoins(queryWrapper);
|
||||
if (joins != null && !joins.isEmpty()) {
|
||||
for (Join join : joins) {
|
||||
if (!join.checkEffective()) {
|
||||
continue;
|
||||
}
|
||||
sqlBuilder.append(join.toSql(queryTables, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void buildWhereSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
|
||||
QueryCondition whereQueryCondition = CPI.getWhereQueryCondition(queryWrapper);
|
||||
if (whereQueryCondition != null) {
|
||||
String whereSql = whereQueryCondition.toSql(queryTables, this);
|
||||
if (StringUtil.isNotBlank(whereSql)) {
|
||||
sqlBuilder.append(" WHERE ").append(whereSql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void buildGroupBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
|
||||
List<QueryColumn> groupByColumns = CPI.getGroupByColumns(queryWrapper);
|
||||
if (groupByColumns != null && !groupByColumns.isEmpty()) {
|
||||
sqlBuilder.append(" GROUP BY ");
|
||||
int index = 0;
|
||||
for (QueryColumn groupByColumn : groupByColumns) {
|
||||
String groupBy = CPI.toConditionSql(groupByColumn, queryTables, this);
|
||||
sqlBuilder.append(groupBy);
|
||||
if (index != groupByColumns.size() - 1) {
|
||||
sqlBuilder.append(", ");
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void buildHavingSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
|
||||
QueryCondition havingQueryCondition = CPI.getHavingQueryCondition(queryWrapper);
|
||||
if (havingQueryCondition != null) {
|
||||
String havingSql = havingQueryCondition.toSql(queryTables, this);
|
||||
if (StringUtil.isNotBlank(havingSql)) {
|
||||
sqlBuilder.append(" HAVING ").append(havingSql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void buildOrderBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
|
||||
List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
|
||||
if (orderBys != null && !orderBys.isEmpty()) {
|
||||
sqlBuilder.append(" ORDER BY ");
|
||||
int index = 0;
|
||||
for (QueryOrderBy orderBy : orderBys) {
|
||||
sqlBuilder.append(orderBy.toSql(queryTables, this));
|
||||
if (index != orderBys.size() - 1) {
|
||||
sqlBuilder.append(", ");
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建 limit 和 offset 的参数
|
||||
*/
|
||||
protected StringBuilder buildLimitOffsetSql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, Integer limitRows, Integer limitOffset) {
|
||||
return limitOffsetProcesser.process(sqlBuilder, queryWrapper, limitRows, limitOffset);
|
||||
}
|
||||
|
||||
|
||||
protected String buildQuestion(int count, boolean withBrackets) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < count; i++) {
|
||||
sb.append("?");
|
||||
if (i != count - 1) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
return withBrackets ? "(" + sb + ")" : sb.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,196 @@
|
||||
/**
|
||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.mybatisflex.core.dialect;
|
||||
|
||||
|
||||
public enum DbType {
|
||||
|
||||
/**
|
||||
* MYSQL
|
||||
*/
|
||||
MYSQL("mysql", "MySql数据库"),
|
||||
/**
|
||||
* MARIADB
|
||||
*/
|
||||
MARIADB("mariadb", "MariaDB数据库"),
|
||||
/**
|
||||
* ORACLE
|
||||
*/
|
||||
ORACLE("oracle", "Oracle11g及以下数据库"),
|
||||
/**
|
||||
* oracle12c
|
||||
*/
|
||||
ORACLE_12C("oracle12c", "Oracle12c及以上数据库"),
|
||||
/**
|
||||
* DB2
|
||||
*/
|
||||
DB2("db2", "DB2数据库"),
|
||||
/**
|
||||
* H2
|
||||
*/
|
||||
H2("h2", "H2数据库"),
|
||||
/**
|
||||
* HSQL
|
||||
*/
|
||||
HSQL("hsql", "HSQL数据库"),
|
||||
/**
|
||||
* SQLITE
|
||||
*/
|
||||
SQLITE("sqlite", "SQLite数据库"),
|
||||
/**
|
||||
* POSTGRE
|
||||
*/
|
||||
POSTGRE_SQL("postgresql", "Postgre数据库"),
|
||||
/**
|
||||
* SQLSERVER2005
|
||||
*/
|
||||
SQL_SERVER2005("sqlserver2005", "SQLServer2005数据库"),
|
||||
/**
|
||||
* SQLSERVER
|
||||
*/
|
||||
SQL_SERVER("sqlserver", "SQLServer数据库"),
|
||||
/**
|
||||
* DM
|
||||
*/
|
||||
DM("dm", "达梦数据库"),
|
||||
/**
|
||||
* xugu
|
||||
*/
|
||||
XU_GU("xugu", "虚谷数据库"),
|
||||
/**
|
||||
* Kingbase
|
||||
*/
|
||||
KINGBASE_ES("kingbasees", "人大金仓数据库"),
|
||||
/**
|
||||
* Phoenix
|
||||
*/
|
||||
PHOENIX("phoenix", "Phoenix HBase数据库"),
|
||||
/**
|
||||
* Gauss
|
||||
*/
|
||||
GAUSS("gauss", "Gauss 数据库"),
|
||||
/**
|
||||
* ClickHouse
|
||||
*/
|
||||
CLICK_HOUSE("clickhouse", "clickhouse 数据库"),
|
||||
/**
|
||||
* GBase
|
||||
*/
|
||||
GBASE("gbase", "南大通用(华库)数据库"),
|
||||
/**
|
||||
* GBase-8s
|
||||
*/
|
||||
GBASE_8S("gbase-8s", "南大通用数据库 GBase 8s"),
|
||||
/**
|
||||
* Oscar
|
||||
*/
|
||||
OSCAR("oscar", "神通数据库"),
|
||||
/**
|
||||
* Sybase
|
||||
*/
|
||||
SYBASE("sybase", "Sybase ASE 数据库"),
|
||||
/**
|
||||
* OceanBase
|
||||
*/
|
||||
OCEAN_BASE("oceanbase", "OceanBase 数据库"),
|
||||
/**
|
||||
* Firebird
|
||||
*/
|
||||
FIREBIRD("Firebird", "Firebird 数据库"),
|
||||
/**
|
||||
* derby
|
||||
*/
|
||||
DERBY("derby", "derby 数据库"),
|
||||
/**
|
||||
* HighGo
|
||||
*/
|
||||
HIGH_GO("highgo", "瀚高数据库"),
|
||||
/**
|
||||
* CUBRID
|
||||
*/
|
||||
CUBRID("cubrid", "CUBRID数据库"),
|
||||
|
||||
/**
|
||||
* GOLDILOCKS
|
||||
*/
|
||||
GOLDILOCKS("goldilocks", "GOLDILOCKS数据库"),
|
||||
/**
|
||||
* CSIIDB
|
||||
*/
|
||||
CSIIDB("csiidb", "CSIIDB数据库"),
|
||||
/**
|
||||
* CSIIDB
|
||||
*/
|
||||
SAP_HANA("hana", "SAP_HANA数据库"),
|
||||
/**
|
||||
* Impala
|
||||
*/
|
||||
IMPALA("impala", "impala数据库"),
|
||||
/**
|
||||
* Vertica
|
||||
*/
|
||||
VERTICA("vertica", "vertica数据库"),
|
||||
/**
|
||||
* 东方国信 xcloud
|
||||
*/
|
||||
XCloud("xcloud", "行云数据库"),
|
||||
/**
|
||||
* redshift
|
||||
*/
|
||||
REDSHIFT("redshift", "亚马逊redshift数据库"),
|
||||
/**
|
||||
* openGauss
|
||||
*/
|
||||
OPENGAUSS("openGauss", "华为 opengauss 数据库"),
|
||||
/**
|
||||
* TDengine
|
||||
*/
|
||||
TDENGINE("TDengine", "TDengine数据库"),
|
||||
/**
|
||||
* Informix
|
||||
*/
|
||||
INFORMIX("informix", "Informix数据库"),
|
||||
/**
|
||||
* uxdb
|
||||
*/
|
||||
UXDB("uxdb", "优炫数据库"),
|
||||
/**
|
||||
* greenplum
|
||||
*/
|
||||
GREENPLUM("greenplum", "greenplum数据库"),
|
||||
/**
|
||||
* UNKNOWN DB
|
||||
*/
|
||||
OTHER("other", "其他数据库");
|
||||
|
||||
/**
|
||||
* 数据库名称
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String remarks;
|
||||
|
||||
|
||||
DbType(String name, String remarks) {
|
||||
this.name = name;
|
||||
this.remarks = remarks;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.mybatisflex.core.dialect;
|
||||
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* DbType 解析 工具类
|
||||
*/
|
||||
public class DbTypeUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 参考 druid 和 MyBatis-plus 的 JdbcUtils
|
||||
* {@link com.alibaba.druid.util.JdbcUtils#getDbType(String, String)}
|
||||
* {@link com.baomidou.mybatisplus.extension.toolkit.JdbcUtils#getDbType(String)}
|
||||
*
|
||||
* @param jdbcUrl jdbcURL
|
||||
* @return 返回数据库类型
|
||||
*/
|
||||
public static DbType parseDbType(String jdbcUrl) {
|
||||
jdbcUrl = jdbcUrl.toLowerCase();
|
||||
if (jdbcUrl.contains(":mysql:") || jdbcUrl.contains(":cobar:")) {
|
||||
return DbType.MYSQL;
|
||||
} else if (jdbcUrl.contains(":mariadb:")) {
|
||||
return DbType.MARIADB;
|
||||
} else if (jdbcUrl.contains(":oracle:")) {
|
||||
return DbType.ORACLE;
|
||||
} else if (jdbcUrl.contains(":sqlserver:") || jdbcUrl.contains(":microsoft:")) {
|
||||
return DbType.SQL_SERVER2005;
|
||||
} else if (jdbcUrl.contains(":sqlserver2012:")) {
|
||||
return DbType.SQL_SERVER;
|
||||
} else if (jdbcUrl.contains(":postgresql:")) {
|
||||
return DbType.POSTGRE_SQL;
|
||||
} else if (jdbcUrl.contains(":hsqldb:")) {
|
||||
return DbType.HSQL;
|
||||
} else if (jdbcUrl.contains(":db2:")) {
|
||||
return DbType.DB2;
|
||||
} else if (jdbcUrl.contains(":sqlite:")) {
|
||||
return DbType.SQLITE;
|
||||
} else if (jdbcUrl.contains(":h2:")) {
|
||||
return DbType.H2;
|
||||
} else if (isMatchedRegex(":dm\\d*:", jdbcUrl)) {
|
||||
return DbType.DM;
|
||||
} else if (jdbcUrl.contains(":xugu:")) {
|
||||
return DbType.XU_GU;
|
||||
} else if (isMatchedRegex(":kingbase\\d*:", jdbcUrl)) {
|
||||
return DbType.KINGBASE_ES;
|
||||
} else if (jdbcUrl.contains(":phoenix:")) {
|
||||
return DbType.PHOENIX;
|
||||
} else if (jdbcUrl.contains(":zenith:")) {
|
||||
return DbType.GAUSS;
|
||||
} else if (jdbcUrl.contains(":gbase:")) {
|
||||
return DbType.GBASE;
|
||||
} else if (jdbcUrl.contains(":gbasedbt-sqli:") || jdbcUrl.contains(":informix-sqli:")) {
|
||||
return DbType.GBASE_8S;
|
||||
} else if (jdbcUrl.contains(":ch:") || jdbcUrl.contains(":clickhouse:")) {
|
||||
return DbType.CLICK_HOUSE;
|
||||
} else if (jdbcUrl.contains(":oscar:")) {
|
||||
return DbType.OSCAR;
|
||||
} else if (jdbcUrl.contains(":sybase:")) {
|
||||
return DbType.SYBASE;
|
||||
} else if (jdbcUrl.contains(":oceanbase:")) {
|
||||
return DbType.OCEAN_BASE;
|
||||
} else if (jdbcUrl.contains(":highgo:")) {
|
||||
return DbType.HIGH_GO;
|
||||
} else if (jdbcUrl.contains(":cubrid:")) {
|
||||
return DbType.CUBRID;
|
||||
} else if (jdbcUrl.contains(":goldilocks:")) {
|
||||
return DbType.GOLDILOCKS;
|
||||
} else if (jdbcUrl.contains(":csiidb:")) {
|
||||
return DbType.CSIIDB;
|
||||
} else if (jdbcUrl.contains(":sap:")) {
|
||||
return DbType.SAP_HANA;
|
||||
} else if (jdbcUrl.contains(":impala:")) {
|
||||
return DbType.IMPALA;
|
||||
} else if (jdbcUrl.contains(":vertica:")) {
|
||||
return DbType.VERTICA;
|
||||
} else if (jdbcUrl.contains(":xcloud:")) {
|
||||
return DbType.XCloud;
|
||||
} else if (jdbcUrl.contains(":firebirdsql:")) {
|
||||
return DbType.FIREBIRD;
|
||||
} else if (jdbcUrl.contains(":redshift:")) {
|
||||
return DbType.REDSHIFT;
|
||||
} else if (jdbcUrl.contains(":opengauss:")) {
|
||||
return DbType.OPENGAUSS;
|
||||
} else if (jdbcUrl.contains(":taos:") || jdbcUrl.contains(":taos-rs:")) {
|
||||
return DbType.TDENGINE;
|
||||
} else if (jdbcUrl.contains(":informix")) {
|
||||
return DbType.INFORMIX;
|
||||
} else if (jdbcUrl.contains(":uxdb:")) {
|
||||
return DbType.UXDB;
|
||||
} else if (jdbcUrl.contains(":greenplum:")) {
|
||||
return DbType.GREENPLUM;
|
||||
} else {
|
||||
return DbType.OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 正则匹配,验证成功返回 true,验证失败返回 false
|
||||
*/
|
||||
public static boolean isMatchedRegex(String regex, String jdbcUrl) {
|
||||
if (null == jdbcUrl) {
|
||||
return false;
|
||||
}
|
||||
return Pattern.compile(regex).matcher(jdbcUrl).find();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.mybatisflex.core.dialect;
|
||||
|
||||
|
||||
import com.mybatisflex.core.FlexGlobalConfig;
|
||||
import com.mybatisflex.core.util.ObjectUtil;
|
||||
import org.apache.ibatis.util.MapUtil;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 方言工厂类,用于创建方言
|
||||
*/
|
||||
public class DialectFactory {
|
||||
|
||||
/**
|
||||
* 数据库类型和方言的映射关系,可以通过其读取指定的方言,亦可能通过其扩展其他方言
|
||||
* 比如,在 mybatis-flex 实现的方言中有 bug 或者 有自己的独立实现,可以添加自己的方言实现到
|
||||
* 此 map 中,用于覆盖系统的方言实现
|
||||
*/
|
||||
private static Map<DbType, IDialect> dialectMap = new EnumMap<>(DbType.class);
|
||||
|
||||
/**
|
||||
* 通过设置当前线程的数据库类型,以达到在代码执行时随时切换方言的功能
|
||||
*/
|
||||
private static ThreadLocal<DbType> dbTypeThreadLocal = new ThreadLocal<>();
|
||||
|
||||
|
||||
/**
|
||||
* 获取方言
|
||||
*
|
||||
* @return IDialect
|
||||
*/
|
||||
public static IDialect getDialect() {
|
||||
DbType dbType = ObjectUtil.requireNonNullElse(dbTypeThreadLocal.get(), FlexGlobalConfig.getDefaultConfig().getDbType());
|
||||
return MapUtil.computeIfAbsent(dialectMap, dbType, DialectFactory::createDialectByDbType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前线程的 dbType
|
||||
*
|
||||
* @param dbType
|
||||
*/
|
||||
public static void setHintDbType(DbType dbType) {
|
||||
dbTypeThreadLocal.set(dbType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前线程的 dbType
|
||||
*
|
||||
* @return dbType
|
||||
*/
|
||||
public static DbType getHintDbType() {
|
||||
return dbTypeThreadLocal.get();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 清除当前线程的 dbType
|
||||
*/
|
||||
public static void clearHintDbType() {
|
||||
dbTypeThreadLocal.remove();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 可以为某个 dbType 注册(新增或覆盖)自己的方言
|
||||
*
|
||||
* @param dbType 数据库类型
|
||||
* @param dialect 方言的实现
|
||||
*/
|
||||
public static void registerDialect(DbType dbType, IDialect dialect) {
|
||||
dialectMap.put(dbType, dialect);
|
||||
}
|
||||
|
||||
|
||||
private static IDialect createDialectByDbType(DbType dbType) {
|
||||
switch (dbType) {
|
||||
case MYSQL:
|
||||
case H2:
|
||||
case MARIADB:
|
||||
case GBASE:
|
||||
case OSCAR:
|
||||
case XU_GU:
|
||||
case CLICK_HOUSE:
|
||||
case OCEAN_BASE:
|
||||
case CUBRID:
|
||||
case GOLDILOCKS:
|
||||
case CSIIDB:
|
||||
return new CommonsDialectImpl(KeywordWrap.BACKQUOTE, LimitOffsetProcesser.MYSQL);
|
||||
case ORACLE:
|
||||
case DM:
|
||||
case GAUSS:
|
||||
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcesser.ORACLE);
|
||||
case POSTGRE_SQL:
|
||||
case SQLITE:
|
||||
case HSQL:
|
||||
case KINGBASE_ES:
|
||||
case PHOENIX:
|
||||
case SAP_HANA:
|
||||
case IMPALA:
|
||||
case HIGH_GO:
|
||||
case VERTICA:
|
||||
case REDSHIFT:
|
||||
case OPENGAUSS:
|
||||
case TDENGINE:
|
||||
case UXDB:
|
||||
return new CommonsDialectImpl(KeywordWrap.BACKQUOTE, LimitOffsetProcesser.POSTGRESQL);
|
||||
case ORACLE_12C:
|
||||
case FIREBIRD:
|
||||
case SQL_SERVER:
|
||||
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcesser.DERBY);
|
||||
case SQL_SERVER2005:
|
||||
return new CommonsDialectImpl(KeywordWrap.SQUARE_BRACKETS, LimitOffsetProcesser.DB2);
|
||||
case INFORMIX:
|
||||
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcesser.INFORMIX);
|
||||
case DB2:
|
||||
return new CommonsDialectImpl(KeywordWrap.DOUBLE_QUOTATION, LimitOffsetProcesser.DB2);
|
||||
default:
|
||||
return new CommonsDialectImpl();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.mybatisflex.core.dialect;
|
||||
|
||||
import com.mybatisflex.core.querywrapper.QueryWrapper;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IDialect {
|
||||
|
||||
String wrap(String keyword);
|
||||
|
||||
String forInsertRow(String tableName, Row row);
|
||||
|
||||
String forInsertBatchWithFirstRowColumns(String tableName, List<Row> rows);
|
||||
|
||||
String forDeleteById(String tableName, String[] primaryKeys);
|
||||
|
||||
String forDeleteBatchByIds(String tableName, String[] primaryKeys, Object[] ids);
|
||||
|
||||
String forDeleteByQuery(QueryWrapper queryWrapper);
|
||||
|
||||
String forUpdateById(String tableName, Row row);
|
||||
|
||||
String forUpdateByQuery(String tableName, Row data, QueryWrapper queryWrapper);
|
||||
|
||||
String forUpdateBatchById(String tableName, List<Row> rows);
|
||||
|
||||
String forSelectOneById(String tableName, String[] primaryKeys, Object[] primaryValues);
|
||||
|
||||
String forSelectListByQuery(QueryWrapper queryWrapper);
|
||||
|
||||
String forSelectCountByQuery(QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
String buildSelectSql(QueryWrapper queryWrapper);
|
||||
|
||||
String buildSelectCountSql(QueryWrapper queryWrapper);
|
||||
|
||||
String buildDeleteSql(QueryWrapper queryWrapper);
|
||||
|
||||
String buildWhereConditionSql(QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
|
||||
//////for entity /////
|
||||
String forInsertEntity(TableInfo tableInfo, Object entity);
|
||||
|
||||
String forInsertBatchWithFirstEntityColumns(TableInfo tableInfo, List<Object> entities);
|
||||
|
||||
String forDeleteEntityById(TableInfo tableInfo);
|
||||
|
||||
String forDeleteEntityBatchById(TableInfo tableInfo, Object[] primaryValues);
|
||||
|
||||
String forUpdateEntity(TableInfo tableInfo, Object entity, boolean ignoreNulls);
|
||||
|
||||
String forUpdateEntityByQuery(TableInfo tableInfo, Object entity, boolean ignoreNulls, QueryWrapper queryWrapper);
|
||||
|
||||
String forSelectOneEntityById(TableInfo tableInfo);
|
||||
|
||||
String forSelectEntityListByIds(TableInfo tableInfo, Object[] primaryValues);
|
||||
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.mybatisflex.core.dialect;
|
||||
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
/**
|
||||
* 用于对数据库的关键字包装
|
||||
*/
|
||||
public class KeywordWrap {
|
||||
|
||||
/**
|
||||
* 无反义处理, 适用于 db2, informix, clickhouse 等
|
||||
*/
|
||||
public final static KeywordWrap NONE = new KeywordWrap("", "") {
|
||||
@Override
|
||||
public String wrap(String keyword) {
|
||||
return keyword;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 反引号反义处理, 适用于 mysql, h2 等
|
||||
*/
|
||||
public final static KeywordWrap BACKQUOTE = new KeywordWrap("`", "`");
|
||||
|
||||
/**
|
||||
* 双引号反义处理, 适用于 postgresql, sqlite, derby, oracle 等
|
||||
*/
|
||||
public final static KeywordWrap DOUBLE_QUOTATION = new KeywordWrap("\"", "\"");
|
||||
|
||||
/**
|
||||
* 方括号反义处理, 适用于 sqlserver
|
||||
*/
|
||||
public final static KeywordWrap SQUARE_BRACKETS = new KeywordWrap("[", "]");
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
private final String prefix;
|
||||
|
||||
/**
|
||||
* 后缀
|
||||
*/
|
||||
private final String suffix;
|
||||
|
||||
|
||||
public KeywordWrap(String prefix, String suffix) {
|
||||
this.prefix = prefix;
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public String wrap(String keyword) {
|
||||
return StringUtil.isBlank(keyword) ? "" : prefix + keyword + suffix;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
package com.mybatisflex.core.dialect;
|
||||
|
||||
import com.mybatisflex.core.querywrapper.QueryWrapper;
|
||||
|
||||
/**
|
||||
* limit 和 offset 参数的处理器
|
||||
*/
|
||||
public interface LimitOffsetProcesser {
|
||||
|
||||
/**
|
||||
* MySql 的处理器
|
||||
* 适合 {@link DbType#MYSQL,DbType#MARIADB,DbType#H2,DbType#CLICK_HOUSE,DbType#XCloud}
|
||||
*/
|
||||
LimitOffsetProcesser MYSQL = (sql, queryWrapper, limitRows, limitOffset) -> {
|
||||
if (limitRows != null && limitOffset != null) {
|
||||
sql.append(" LIMIT ").append(limitOffset).append(", ").append(limitRows);
|
||||
} else if (limitRows != null) {
|
||||
sql.append(" LIMIT ").append(limitRows);
|
||||
}
|
||||
return sql;
|
||||
};
|
||||
|
||||
/**
|
||||
* Postgresql 的处理器
|
||||
* 适合 {@link DbType#POSTGRE_SQL,DbType#SQLITE,DbType#H2,DbType#HSQL,DbType#KINGBASE_ES,DbType#PHOENIX}
|
||||
* 适合 {@link DbType#SAP_HANA,DbType#IMPALA,DbType#HIGH_GO,DbType#VERTICA,DbType#REDSHIFT}
|
||||
* 适合 {@link DbType#OPENGAUSS,DbType#TDENGINE,DbType#UXDB}
|
||||
*/
|
||||
LimitOffsetProcesser POSTGRESQL = (sql, queryWrapper, limitRows, limitOffset) -> {
|
||||
if (limitRows != null && limitOffset != null) {
|
||||
sql.append(" LIMIT ").append(limitOffset).append(" OFFSET ").append(limitRows);
|
||||
} else if (limitRows != null) {
|
||||
sql.append(" LIMIT ").append(limitRows);
|
||||
}
|
||||
return sql;
|
||||
};
|
||||
|
||||
/**
|
||||
* derby 的处理器
|
||||
* 适合 {@link DbType#DERBY,DbType#ORACLE_12C,DbType#SQL_SERVER,DbType#POSTGRE_SQL}
|
||||
*/
|
||||
LimitOffsetProcesser DERBY = (sql, queryWrapper, limitRows, limitOffset) -> {
|
||||
if (limitRows != null && limitOffset != null) {
|
||||
// OFFSET ** ROWS FETCH NEXT ** ROWS ONLY")
|
||||
sql.append(" OFFSET ").append(limitOffset).append(" ROWS FETCH NEXT ").append(limitRows).append(" ROWS ONLY");
|
||||
} else if (limitRows != null) {
|
||||
// FETCH FIRST 20 ROWS ONLY
|
||||
sql.append(" FETCH FIRST ").append(limitRows).append(" ROWS ONLY");
|
||||
}
|
||||
return sql;
|
||||
};
|
||||
|
||||
/**
|
||||
* db2 的处理器
|
||||
* 适合 {@link DbType#DB2,DbType#SQL_SERVER2005}
|
||||
*/
|
||||
LimitOffsetProcesser DB2 = (sql, queryWrapper, limitRows, limitOffset) -> {
|
||||
if (limitRows != null && limitOffset != null) {
|
||||
// OFFSET ** ROWS FETCH NEXT ** ROWS ONLY")
|
||||
sql.append(" OFFSET ").append(limitOffset).append(" ROWS FETCH NEXT ").append(limitRows).append(" ROWS ONLY");
|
||||
} else if (limitRows != null) {
|
||||
// FETCH FIRST 20 ROWS ONLY
|
||||
sql.append(" FETCH FIRST ").append(limitRows).append(" ROWS ONLY");
|
||||
}
|
||||
return sql;
|
||||
};
|
||||
|
||||
/**
|
||||
* Informix 的处理器
|
||||
* 适合 {@link DbType#INFORMIX}
|
||||
* 文档 {@link <a href="https://www.ibm.com/docs/en/informix-servers/14.10?topic=clause-restricting-return-values-skip-limit-first-options">https://www.ibm.com/docs/en/informix-servers/14.10?topic=clause-restricting-return-values-skip-limit-first-options</a>}
|
||||
*/
|
||||
LimitOffsetProcesser INFORMIX = (sql, queryWrapper, limitRows, limitOffset) -> {
|
||||
if (limitRows != null && limitOffset != null) {
|
||||
// SELECT SKIP 2 FIRST 1 * FROM
|
||||
sql.insert(6, " SKIP " + limitOffset + " FIRST " + limitRows);
|
||||
} else if (limitRows != null) {
|
||||
sql.insert(6, " FIRST " + limitRows);
|
||||
}
|
||||
return sql;
|
||||
};
|
||||
|
||||
/**
|
||||
* Firebird 的处理器
|
||||
* 适合 {@link DbType#FIREBIRD}
|
||||
*/
|
||||
LimitOffsetProcesser FIREBIRD = (sql, queryWrapper, limitRows, limitOffset) -> {
|
||||
if (limitRows != null && limitOffset != null) {
|
||||
// ROWS 2 TO 3
|
||||
sql.append(" ROWS ").append(limitOffset).append(" TO ").append(limitOffset + limitRows);
|
||||
} else if (limitRows != null) {
|
||||
sql.insert(6, " FIRST " + limitRows);
|
||||
}
|
||||
return sql;
|
||||
};
|
||||
|
||||
/**
|
||||
* Oracle11g及以下数据库的处理器
|
||||
* 适合 {@link DbType#ORACLE,DbType#DM,DbType#GAUSS}
|
||||
*/
|
||||
LimitOffsetProcesser ORACLE = (sql, queryWrapper, limitRows, limitOffset) -> {
|
||||
if (limitRows != null) {
|
||||
if (limitOffset == null) {
|
||||
limitOffset = 0;
|
||||
}
|
||||
StringBuilder newSql = new StringBuilder("SELECT * FROM (SELECT TEMP_DATAS.*, ROWNUM RN FROM (");
|
||||
newSql.append(sql);
|
||||
newSql.append(") TEMP_DATAS WHERE ROWNUM <=").append(limitOffset + limitRows).append(") WHERE RN >").append(limitOffset);
|
||||
return newSql;
|
||||
}
|
||||
return sql;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sybase 处理器
|
||||
* 适合 {@link DbType#SYBASE}
|
||||
*/
|
||||
LimitOffsetProcesser SYBASE = (sql, queryWrapper, limitRows, limitOffset) -> {
|
||||
if (limitRows != null && limitOffset != null) {
|
||||
//SELECT TOP 1 START AT 3 * FROM
|
||||
sql.insert(6, " TOP " + limitRows + " START AT " + (limitRows + limitOffset));
|
||||
} else if (limitRows != null) {
|
||||
sql.insert(6, " TOP " + limitRows);
|
||||
}
|
||||
return sql;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 处理构建 limit 和 offset
|
||||
*
|
||||
* @param sql 已经构建的 sql
|
||||
* @param queryWrapper 参数内容
|
||||
* @param limitRows 用户传入的 limit 参数 可能为 null
|
||||
* @param limitOffset 用户传入的 offset 参数,可能为 null
|
||||
*/
|
||||
StringBuilder process(StringBuilder sql, QueryWrapper queryWrapper, Integer limitRows, Integer limitOffset);
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 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.exception;
|
||||
|
||||
/**
|
||||
* MybatisFlexException 异常封装类
|
||||
*/
|
||||
public final class FlexExceptions {
|
||||
|
||||
private FlexExceptions() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 封装 MybatisFlexException 异常
|
||||
*
|
||||
* @param throwable 异常
|
||||
* @return MybatisFlexException
|
||||
*/
|
||||
public static MybatisFlexException wrap(Throwable throwable) {
|
||||
if (throwable instanceof MybatisFlexException){
|
||||
return (MybatisFlexException) throwable;
|
||||
}
|
||||
return new MybatisFlexException(throwable);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 封装 MybatisFlexException 异常
|
||||
*
|
||||
* @param throwable 异常
|
||||
* @param msg 消息
|
||||
* @param params 消息参数
|
||||
* @return MybatisFlexException
|
||||
*/
|
||||
public static MybatisFlexException wrap(Throwable throwable, String msg, Object... params) {
|
||||
return new MybatisFlexException(String.format(msg, params), throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装 MybatisFlexException 异常
|
||||
*
|
||||
* @param msg 消息
|
||||
* @param params 消息参数
|
||||
* @return MybatisFlexException
|
||||
*/
|
||||
public static MybatisFlexException wrap(String msg, Object... params) {
|
||||
return new MybatisFlexException(String.format(msg, params));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 抛出一个新的 MybatisFlexException 异常
|
||||
*
|
||||
* @param condition 抛出条件
|
||||
* @param msg 消息
|
||||
* @param params 消息参数
|
||||
*/
|
||||
public static void throwIf(boolean condition, String msg, Object... params) {
|
||||
if (condition) {
|
||||
throw wrap(msg, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.exception;
|
||||
|
||||
public class MybatisFlexException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MybatisFlexException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MybatisFlexException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public MybatisFlexException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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.javassist;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ModifyAttrsRecord extends Serializable {
|
||||
|
||||
/**
|
||||
* 注意:
|
||||
* 对于 entity 来说,这里存放的只是 属性的名称,而非字段
|
||||
* 对于 row 来说,存放的则是 字段 名称
|
||||
*/
|
||||
Set<String> modifyAttrs = new LinkedHashSet<>();
|
||||
|
||||
default void addModifyAttr(String attr) {
|
||||
modifyAttrs.add(attr);
|
||||
}
|
||||
|
||||
default void removeModifyAttr(String attr) {
|
||||
modifyAttrs.remove(attr);
|
||||
}
|
||||
|
||||
default Set<String> obtainModifyAttrs() {
|
||||
return new LinkedHashSet<>(modifyAttrs);
|
||||
}
|
||||
|
||||
default void clearModifyFlag() {
|
||||
modifyAttrs.clear();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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.javassist;
|
||||
|
||||
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
import org.apache.ibatis.javassist.util.proxy.MethodHandler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
|
||||
public class ModifyAttrsRecordHandler implements MethodHandler {
|
||||
|
||||
|
||||
@Override
|
||||
public Object invoke(Object self, Method originalMethod, Method proxyMethod, Object[] args) throws Throwable {
|
||||
|
||||
if (originalMethod.getName().startsWith("set")){
|
||||
String property = StringUtil.firstCharToLowerCase(originalMethod.getName().substring(3));
|
||||
((ModifyAttrsRecord) self).addModifyAttr(property);
|
||||
}
|
||||
|
||||
return proxyMethod.invoke(self, args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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.javassist;
|
||||
|
||||
import org.apache.ibatis.javassist.util.proxy.ProxyFactory;
|
||||
import org.apache.ibatis.javassist.util.proxy.ProxyObject;
|
||||
import org.apache.ibatis.logging.LogFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
public class ModifyAttrsRecordProxyFactory {
|
||||
|
||||
private static ModifyAttrsRecordProxyFactory instance = new ModifyAttrsRecordProxyFactory();
|
||||
|
||||
public static ModifyAttrsRecordProxyFactory getInstance(){
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ModifyAttrsRecordProxyFactory(){}
|
||||
|
||||
public <T> T get(Class<T> target) {
|
||||
ProxyFactory factory = new ProxyFactory();
|
||||
factory.setSuperclass(target);
|
||||
|
||||
|
||||
Class<?>[] interfaces = Arrays.copyOf(target.getInterfaces(), target.getInterfaces().length + 1);
|
||||
interfaces[interfaces.length - 1] = ModifyAttrsRecord.class;
|
||||
factory.setInterfaces(interfaces);
|
||||
|
||||
final Class<?> proxyClass = factory.createClass();
|
||||
|
||||
T proxyObject = null;
|
||||
try {
|
||||
proxyObject = (T) proxyClass.newInstance();
|
||||
((ProxyObject) proxyObject).setHandler(new ModifyAttrsRecordHandler());
|
||||
} catch (Throwable e) {
|
||||
LogFactory.getLog(ModifyAttrsRecordProxyFactory.class).error(e.toString(),e);
|
||||
}
|
||||
|
||||
return proxyObject;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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.key;
|
||||
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.exception.FlexExceptions;
|
||||
import com.mybatisflex.core.table.IdInfo;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.reflection.invoker.Invoker;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
|
||||
import java.sql.Statement;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 通过 java 编码的方式生成主键
|
||||
* 当主键类型配置为 KeyType#Generator 时,使用此生成器生成
|
||||
* {@link com.mybatisflex.core.enums.KeyType#Generator}
|
||||
*/
|
||||
public class CustomKeyGenerator implements KeyGenerator {
|
||||
|
||||
protected Configuration configuration;
|
||||
protected IKeyGenerator keyGenerator;
|
||||
protected TableInfo tableInfo;
|
||||
protected IdInfo idInfo;
|
||||
|
||||
|
||||
public CustomKeyGenerator(Configuration configuration, TableInfo tableInfo, IdInfo idInfo) {
|
||||
this.configuration = configuration;
|
||||
this.keyGenerator = KeyGeneratorFactory.getKeyGenerator(idInfo.getValue());
|
||||
this.tableInfo = tableInfo;
|
||||
this.idInfo = idInfo;
|
||||
|
||||
ensuresKeyGeneratorNotNull();
|
||||
}
|
||||
|
||||
private void ensuresKeyGeneratorNotNull() {
|
||||
if (keyGenerator == null) {
|
||||
throw FlexExceptions.wrap("The name of \"%s\" key generator not exist.\n" +
|
||||
"please check annotation @Id(value=\"%s\") at field: %s#%s"
|
||||
, idInfo.getValue(), idInfo.getValue(), tableInfo.getEntityClass().getName(), idInfo.getProperty());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
Object entity = ((Map) parameter).get(FlexConsts.ENTITY);
|
||||
Object generateId = keyGenerator.generate(entity, idInfo.getColumn());
|
||||
try {
|
||||
Invoker setInvoker = tableInfo.getReflector().getSetInvoker(idInfo.getProperty());
|
||||
Object id = convert(generateId, setInvoker.getType());
|
||||
setInvoker.invoke(entity, new Object[]{id});
|
||||
} catch (Exception e) {
|
||||
throw FlexExceptions.wrap(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Object convert(Object value, Class<?> targetClass) {
|
||||
if (value == null || (value.getClass() == String.class && StringUtil.isBlank((String) value)
|
||||
&& targetClass != String.class)) {
|
||||
return null;
|
||||
}
|
||||
if (value.getClass().isAssignableFrom(targetClass)) {
|
||||
return value;
|
||||
}
|
||||
if (targetClass == String.class) {
|
||||
return value.toString();
|
||||
} else if (targetClass == Long.class || targetClass == long.class) {
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
return Long.parseLong(value.toString());
|
||||
} else if (targetClass == java.math.BigInteger.class) {
|
||||
return new java.math.BigInteger(value.toString());
|
||||
} else if (targetClass == java.math.BigDecimal.class) {
|
||||
return new java.math.BigDecimal(value.toString());
|
||||
} else if (targetClass == Integer.class || targetClass == int.class) {
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
return Integer.parseInt(value.toString());
|
||||
} else if (targetClass == Double.class || targetClass == double.class) {
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
return Double.parseDouble(value.toString());
|
||||
} else if (targetClass == Float.class || targetClass == float.class) {
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).floatValue();
|
||||
}
|
||||
return Float.parseFloat(value.toString());
|
||||
} else if (targetClass == Short.class || targetClass == short.class) {
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).shortValue();
|
||||
}
|
||||
return Short.parseShort(value.toString());
|
||||
}
|
||||
|
||||
throw FlexExceptions.wrap("\"%s\" can not be parsed for primary key.", value);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
@ -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.key;
|
||||
|
||||
public interface IKeyGenerator {
|
||||
|
||||
Object generate(Object entity,String keyColumn);
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.key;
|
||||
|
||||
public interface IMultiKeyGenerator {
|
||||
|
||||
/**
|
||||
* 是否需要数据库生成主键
|
||||
*
|
||||
* @return true 需要生成主键
|
||||
*/
|
||||
boolean isNeedGeneratedKeys();
|
||||
|
||||
/**
|
||||
* 数据库主键的列名
|
||||
*
|
||||
* @return 列名数组
|
||||
*/
|
||||
String[] getKeyColumnNames();
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.key;
|
||||
|
||||
import com.mybatisflex.core.key.impl.UUIDKeyGenerator;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class KeyGeneratorFactory {
|
||||
|
||||
private static final Map<String, IKeyGenerator> KEY_GENERATOR_MAP = new HashMap<>();
|
||||
static {
|
||||
/** 内置了 uuid 的生成器,因此主键配置的时候可以直接配置为 @Id(keyType = KeyType.Generator, value = "uuid")
|
||||
* {@link com.mybatisflex.annotation.Id}
|
||||
*/
|
||||
register("uuid", new UUIDKeyGenerator());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取 主键生成器
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static IKeyGenerator getKeyGenerator(String name) {
|
||||
return KEY_GENERATOR_MAP.get(name.trim());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 注册一个主键生成器
|
||||
*
|
||||
* @param key
|
||||
* @param keyGenerator
|
||||
*/
|
||||
public static void register(String key, IKeyGenerator keyGenerator) {
|
||||
KEY_GENERATOR_MAP.put(key.trim(), keyGenerator);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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.key;
|
||||
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 多实体主键生成器,用于批量插入的场景
|
||||
*/
|
||||
public class MultiEntityKeyGenerator implements KeyGenerator {
|
||||
|
||||
private KeyGenerator keyGenerator;
|
||||
|
||||
public MultiEntityKeyGenerator(KeyGenerator keyGenerator) {
|
||||
this.keyGenerator = keyGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
List<Object> entities = (List<Object>) ((Map) parameter).get(FlexConsts.ENTITIES);
|
||||
if (CollectionUtil.isNotEmpty(entities)) {
|
||||
for (Object entity : entities) {
|
||||
((Map) parameter).put(FlexConsts.ENTITY, entity);
|
||||
keyGenerator.processBefore(executor, ms, stmt, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
// do nothing
|
||||
// 多条数据批量插入的场景下,不支持后设置主键
|
||||
// 比如 INSERT INTO `tb_account`(uuid,name,sex) VALUES (?, ?, ?), (?, ?, ?)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 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.key;
|
||||
|
||||
import com.mybatisflex.core.table.IdInfo;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 多主键、复合主键 id 生成器
|
||||
*/
|
||||
public class MultiPrimaryKeyGenerator implements KeyGenerator, IMultiKeyGenerator {
|
||||
|
||||
private List<KeyGenerator> keyGenerators;
|
||||
|
||||
//所有自增字段的名称
|
||||
private String[] autoGenKeyColumnNames;
|
||||
|
||||
public MultiPrimaryKeyGenerator(MappedStatement mappedStatement, TableInfo tableInfo, List<IdInfo> primaryKeyList) {
|
||||
this.keyGenerators = new ArrayList<>();
|
||||
|
||||
List<String> autoGenKeyColumnNameList = new ArrayList<>();
|
||||
for (IdInfo idInfo : primaryKeyList) {
|
||||
KeyGenerator idKeyGenerator = MybatisKeyGeneratorUtil.createIdKeyGenerator(tableInfo, mappedStatement, idInfo);
|
||||
keyGenerators.add(idKeyGenerator);
|
||||
if (idKeyGenerator == Jdbc3KeyGenerator.INSTANCE) {
|
||||
autoGenKeyColumnNameList.add(idInfo.getColumn());
|
||||
}
|
||||
}
|
||||
|
||||
this.autoGenKeyColumnNames = autoGenKeyColumnNameList.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
for (KeyGenerator keyGenerator : keyGenerators) {
|
||||
keyGenerator.processBefore(executor, ms, stmt, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
for (KeyGenerator keyGenerator : keyGenerators) {
|
||||
keyGenerator.processAfter(executor, ms, stmt, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要数据库 自动生成主键
|
||||
*
|
||||
* @return true: need generated keys
|
||||
*/
|
||||
@Override
|
||||
public boolean isNeedGeneratedKeys() {
|
||||
for (KeyGenerator keyGenerator : keyGenerators) {
|
||||
if (keyGenerator == Jdbc3KeyGenerator.INSTANCE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动生成主键的 columns 字段
|
||||
*
|
||||
* @return keyColumnNames
|
||||
*/
|
||||
@Override
|
||||
public String[] getKeyColumnNames() {
|
||||
return autoGenKeyColumnNames;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.key;
|
||||
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 用于批量插入的场景
|
||||
*/
|
||||
public class MultiRowKeyGenerator implements KeyGenerator {
|
||||
|
||||
private KeyGenerator keyGenerator;
|
||||
|
||||
public MultiRowKeyGenerator(KeyGenerator keyGenerator) {
|
||||
this.keyGenerator = keyGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
List<Row> rows = (List<Row>) ((Map) parameter).get(FlexConsts.ROWS);
|
||||
if (CollectionUtil.isNotEmpty(rows)) {
|
||||
for (Row entity : rows) {
|
||||
((Map) parameter).put(FlexConsts.ROW, entity);
|
||||
keyGenerator.processBefore(executor, ms, stmt, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
// do nothing
|
||||
// 多条数据批量插入的场景下,不支持后设置主键
|
||||
// 比如 INSERT INTO `tb_account`(uuid,name,sex) VALUES (?, ?, ?), (?, ?, ?)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
/**
|
||||
* 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.key;
|
||||
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.enums.KeyType;
|
||||
import com.mybatisflex.core.exception.FlexExceptions;
|
||||
import com.mybatisflex.core.table.IdInfo;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
|
||||
import org.apache.ibatis.mapping.*;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class MybatisKeyGeneratorUtil {
|
||||
|
||||
|
||||
public static KeyGenerator createTableKeyGenerator(TableInfo tableInfo, MappedStatement ms) {
|
||||
List<IdInfo> primaryKeyList = tableInfo.getPrimaryKeyList();
|
||||
|
||||
//无主键
|
||||
if (primaryKeyList == null || primaryKeyList.isEmpty()) {
|
||||
return NoKeyGenerator.INSTANCE;
|
||||
}
|
||||
|
||||
//多主键的
|
||||
if (primaryKeyList.size() > 1) {
|
||||
return new MultiPrimaryKeyGenerator(ms, tableInfo, primaryKeyList);
|
||||
}
|
||||
|
||||
return createIdKeyGenerator(tableInfo, ms, primaryKeyList.get(0));
|
||||
}
|
||||
|
||||
|
||||
public static KeyGenerator createIdKeyGenerator(TableInfo tableInfo, MappedStatement ms, IdInfo idInfo) {
|
||||
if (idInfo.getKeyType() == KeyType.None) {
|
||||
return NoKeyGenerator.INSTANCE;
|
||||
}
|
||||
|
||||
//自增主键
|
||||
if (idInfo.getKeyType() == KeyType.Auto) {
|
||||
return Jdbc3KeyGenerator.INSTANCE;
|
||||
}
|
||||
|
||||
//通过 java 生成的主键
|
||||
if (idInfo.getKeyType() == KeyType.Generator) {
|
||||
return new CustomKeyGenerator(ms.getConfiguration(), tableInfo, idInfo);
|
||||
}
|
||||
|
||||
//通过序列生成的注解
|
||||
String sequence = idInfo.getValue();
|
||||
if (StringUtil.isBlank(sequence)) {
|
||||
throw FlexExceptions.wrap("please config @Id(value=\"...\") for field: %s in class: %s"
|
||||
, idInfo.getProperty()
|
||||
, tableInfo.getEntityClass().getName());
|
||||
}
|
||||
|
||||
|
||||
String selectId = ms.getId() + SelectKeyGenerator.SELECT_KEY_SUFFIX;
|
||||
SqlSource sqlSource = ms.getLang().createSqlSource(ms.getConfiguration(), sequence.trim(), idInfo.getPropertyType());
|
||||
MappedStatement.Builder msBuilder = new MappedStatement.Builder(ms.getConfiguration(), selectId, sqlSource, SqlCommandType.SELECT)
|
||||
.resource(ms.getResource())
|
||||
.fetchSize(null)
|
||||
.timeout(null)
|
||||
.statementType(StatementType.PREPARED)
|
||||
.keyGenerator(NoKeyGenerator.INSTANCE)
|
||||
.keyProperty(FlexConsts.ENTITY + "." + idInfo.getProperty())
|
||||
.keyColumn(idInfo.getColumn())
|
||||
.databaseId(ms.getDatabaseId())
|
||||
.lang(ms.getLang())
|
||||
.resultOrdered(false)
|
||||
.resultSets(null)
|
||||
.resultMaps(createIdResultMaps(ms.getConfiguration(), selectId + "-Inline", idInfo.getPropertyType(), new ArrayList<>()))
|
||||
.resultSetType(null)
|
||||
.flushCacheRequired(false)
|
||||
.useCache(false)
|
||||
.cache(ms.getCache());
|
||||
|
||||
MappedStatement keyMappedStatement = msBuilder.build();
|
||||
ms.getConfiguration().addMappedStatement(keyMappedStatement);
|
||||
|
||||
//看到有的框架把 keyGenerator 添加到 mybatis 的当前配置里去,其实是完全没必要的
|
||||
//因为只有在 xml 解析的时候,才可能存在多一个 MappedStatement 拥有同一个 keyGenerator 的情况
|
||||
//当前每个方法都拥有一个自己的 keyGenerator 了,没必要添加
|
||||
//this.addKeyGenerator(selectId, keyGenerator);
|
||||
|
||||
return new SelectKeyGenerator(keyMappedStatement, idInfo.isBefore());
|
||||
}
|
||||
|
||||
|
||||
private static List<ResultMap> createIdResultMaps(Configuration configuration,
|
||||
String statementId, Class<?> resultType, List<ResultMapping> resultMappings) {
|
||||
ResultMap resultMap = new ResultMap.Builder(configuration, statementId, resultType, resultMappings, null)
|
||||
.build();
|
||||
return Arrays.asList(resultMap);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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.key;
|
||||
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.exception.FlexExceptions;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import com.mybatisflex.core.row.RowKey;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
|
||||
import java.sql.Statement;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 通过 java 编码的方式生成主键
|
||||
* 当主键类型配置为 KeyType#Generator 时,使用此生成器生成
|
||||
* {@link com.mybatisflex.core.enums.KeyType#Generator}
|
||||
*/
|
||||
public class RowCustomKeyGenerator implements KeyGenerator {
|
||||
|
||||
protected RowKey rowKey;
|
||||
protected IKeyGenerator keyGenerator;
|
||||
|
||||
|
||||
public RowCustomKeyGenerator(RowKey rowKey) {
|
||||
this.rowKey = rowKey;
|
||||
this.keyGenerator = KeyGeneratorFactory.getKeyGenerator(rowKey.getValue());
|
||||
|
||||
ensuresKeyGeneratorNotNull();
|
||||
}
|
||||
|
||||
private void ensuresKeyGeneratorNotNull() {
|
||||
if (keyGenerator == null) {
|
||||
throw FlexExceptions.wrap("The name of \"%s\" key generator not exist.", rowKey.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
Row row = (Row) ((Map) parameter).get(FlexConsts.ROW);
|
||||
Object generateId = keyGenerator.generate(row, rowKey.getKeyColumn());
|
||||
try {
|
||||
row.put(rowKey.getKeyColumn(), generateId);
|
||||
} catch (Exception e) {
|
||||
throw FlexExceptions.wrap(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,270 @@
|
||||
/**
|
||||
* 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.key;
|
||||
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import org.apache.ibatis.binding.MapperMethod.ParamMap;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.ExecutorException;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.reflection.ArrayUtil;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.apache.ibatis.reflection.ParamNameResolver;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
import org.apache.ibatis.session.defaults.DefaultSqlSession.StrictMap;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
import org.apache.ibatis.type.TypeHandlerRegistry;
|
||||
import org.apache.ibatis.util.MapUtil;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* 主要作用是为 Row 生成自增主键
|
||||
*/
|
||||
public class RowJdbc3KeyGenerator implements KeyGenerator {
|
||||
|
||||
private String keyProperty;
|
||||
|
||||
|
||||
private static final String SECOND_GENERIC_PARAM_NAME = ParamNameResolver.GENERIC_NAME_PREFIX + "2";
|
||||
private static final String MSG_TOO_MANY_KEYS = "Too many keys are generated. There are only %d target objects. "
|
||||
+ "You either specified a wrong 'keyProperty' or encountered a driver bug like #1523.";
|
||||
|
||||
|
||||
public RowJdbc3KeyGenerator(String keyProperty) {
|
||||
this.keyProperty = keyProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
processBatch(ms, stmt, parameter);
|
||||
}
|
||||
|
||||
public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {
|
||||
final String[] keyProperties = new String[]{FlexConsts.ROW + "." + keyProperty};
|
||||
try (ResultSet rs = stmt.getGeneratedKeys()) {
|
||||
final ResultSetMetaData rsmd = rs.getMetaData();
|
||||
final Configuration configuration = ms.getConfiguration();
|
||||
if (rsmd.getColumnCount() < keyProperties.length) {
|
||||
// Error?
|
||||
} else {
|
||||
assignKeys(configuration, rs, rsmd, keyProperties, parameter);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void assignKeys(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd, String[] keyProperties,
|
||||
Object parameter) throws SQLException {
|
||||
if (parameter instanceof ParamMap || parameter instanceof StrictMap) {
|
||||
// Multi-param or single param with @Param
|
||||
assignKeysToParamMap(configuration, rs, rsmd, keyProperties, (Map<String, ?>) parameter);
|
||||
} else if (parameter instanceof ArrayList && !((ArrayList<?>) parameter).isEmpty()
|
||||
&& ((ArrayList<?>) parameter).get(0) instanceof ParamMap) {
|
||||
// Multi-param or single param with @Param in batch operation
|
||||
assignKeysToParamMapList(configuration, rs, rsmd, keyProperties, (ArrayList<ParamMap<?>>) parameter);
|
||||
} else {
|
||||
// Single param without @Param
|
||||
assignKeysToParam(configuration, rs, rsmd, keyProperties, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
private void assignKeysToParam(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,
|
||||
String[] keyProperties, Object parameter) throws SQLException {
|
||||
Collection<?> params = collectionize(parameter);
|
||||
if (params.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<KeyAssigner> assignerList = new ArrayList<>();
|
||||
for (int i = 0; i < keyProperties.length; i++) {
|
||||
assignerList.add(new KeyAssigner(configuration, rsmd, i + 1, null, keyProperties[i]));
|
||||
}
|
||||
Iterator<?> iterator = params.iterator();
|
||||
while (rs.next()) {
|
||||
if (!iterator.hasNext()) {
|
||||
throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, params.size()));
|
||||
}
|
||||
Object param = iterator.next();
|
||||
assignerList.forEach(x -> x.assign(rs, param));
|
||||
}
|
||||
}
|
||||
|
||||
private void assignKeysToParamMapList(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,
|
||||
String[] keyProperties, ArrayList<ParamMap<?>> paramMapList) throws SQLException {
|
||||
Iterator<ParamMap<?>> iterator = paramMapList.iterator();
|
||||
List<KeyAssigner> assignerList = new ArrayList<>();
|
||||
long counter = 0;
|
||||
while (rs.next()) {
|
||||
if (!iterator.hasNext()) {
|
||||
throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, counter));
|
||||
}
|
||||
ParamMap<?> paramMap = iterator.next();
|
||||
if (assignerList.isEmpty()) {
|
||||
for (int i = 0; i < keyProperties.length; i++) {
|
||||
assignerList
|
||||
.add(getAssignerForParamMap(configuration, rsmd, i + 1, paramMap, keyProperties[i], keyProperties, false)
|
||||
.getValue());
|
||||
}
|
||||
}
|
||||
assignerList.forEach(x -> x.assign(rs, paramMap));
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
private void assignKeysToParamMap(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,
|
||||
String[] keyProperties, Map<String, ?> paramMap) throws SQLException {
|
||||
if (paramMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, Entry<Iterator<?>, List<KeyAssigner>>> assignerMap = new HashMap<>();
|
||||
for (int i = 0; i < keyProperties.length; i++) {
|
||||
Entry<String, KeyAssigner> entry = getAssignerForParamMap(configuration, rsmd, i + 1, paramMap, keyProperties[i],
|
||||
keyProperties, true);
|
||||
Entry<Iterator<?>, List<KeyAssigner>> iteratorPair = MapUtil.computeIfAbsent(assignerMap, entry.getKey(),
|
||||
k -> MapUtil.entry(collectionize(paramMap.get(k)).iterator(), new ArrayList<>()));
|
||||
iteratorPair.getValue().add(entry.getValue());
|
||||
}
|
||||
long counter = 0;
|
||||
while (rs.next()) {
|
||||
for (Entry<Iterator<?>, List<KeyAssigner>> pair : assignerMap.values()) {
|
||||
if (!pair.getKey().hasNext()) {
|
||||
throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, counter));
|
||||
}
|
||||
Object param = pair.getKey().next();
|
||||
pair.getValue().forEach(x -> x.assign(rs, param));
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
private Entry<String, KeyAssigner> getAssignerForParamMap(Configuration config, ResultSetMetaData rsmd,
|
||||
int columnPosition, Map<String, ?> paramMap, String keyProperty, String[] keyProperties, boolean omitParamName) {
|
||||
Set<String> keySet = paramMap.keySet();
|
||||
// A caveat : if the only parameter has {@code @Param("param2")} on it,
|
||||
// it must be referenced with param name e.g. 'param2.x'.
|
||||
boolean singleParam = !keySet.contains(SECOND_GENERIC_PARAM_NAME);
|
||||
int firstDot = keyProperty.indexOf('.');
|
||||
if (firstDot == -1) {
|
||||
if (singleParam) {
|
||||
return getAssignerForSingleParam(config, rsmd, columnPosition, paramMap, keyProperty, omitParamName);
|
||||
}
|
||||
throw new ExecutorException("Could not determine which parameter to assign generated keys to. "
|
||||
+ "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
|
||||
+ "Specified key properties are " + ArrayUtil.toString(keyProperties) + " and available parameters are "
|
||||
+ keySet);
|
||||
}
|
||||
String paramName = keyProperty.substring(0, firstDot);
|
||||
if (keySet.contains(paramName)) {
|
||||
String argParamName = omitParamName ? null : paramName;
|
||||
String argKeyProperty = keyProperty.substring(firstDot + 1);
|
||||
return MapUtil.entry(paramName, new KeyAssigner(config, rsmd, columnPosition, argParamName, argKeyProperty));
|
||||
} else if (singleParam) {
|
||||
return getAssignerForSingleParam(config, rsmd, columnPosition, paramMap, keyProperty, omitParamName);
|
||||
} else {
|
||||
throw new ExecutorException("Could not find parameter '" + paramName + "'. "
|
||||
+ "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
|
||||
+ "Specified key properties are " + ArrayUtil.toString(keyProperties) + " and available parameters are "
|
||||
+ keySet);
|
||||
}
|
||||
}
|
||||
|
||||
private Entry<String, KeyAssigner> getAssignerForSingleParam(Configuration config, ResultSetMetaData rsmd,
|
||||
int columnPosition, Map<String, ?> paramMap, String keyProperty, boolean omitParamName) {
|
||||
// Assume 'keyProperty' to be a property of the single param.
|
||||
String singleParamName = nameOfSingleParam(paramMap);
|
||||
String argParamName = omitParamName ? null : singleParamName;
|
||||
return MapUtil.entry(singleParamName, new KeyAssigner(config, rsmd, columnPosition, argParamName, keyProperty));
|
||||
}
|
||||
|
||||
private static String nameOfSingleParam(Map<String, ?> paramMap) {
|
||||
// There is virtually one parameter, so any key works.
|
||||
return paramMap.keySet().iterator().next();
|
||||
}
|
||||
|
||||
private static Collection<?> collectionize(Object param) {
|
||||
if (param instanceof Collection) {
|
||||
return (Collection<?>) param;
|
||||
} else if (param instanceof Object[]) {
|
||||
return Arrays.asList((Object[]) param);
|
||||
} else {
|
||||
return Arrays.asList(param);
|
||||
}
|
||||
}
|
||||
|
||||
private class KeyAssigner {
|
||||
private final Configuration configuration;
|
||||
private final ResultSetMetaData rsmd;
|
||||
private final TypeHandlerRegistry typeHandlerRegistry;
|
||||
private final int columnPosition;
|
||||
private final String paramName;
|
||||
private final String propertyName;
|
||||
private TypeHandler<?> typeHandler;
|
||||
|
||||
protected KeyAssigner(Configuration configuration, ResultSetMetaData rsmd, int columnPosition, String paramName,
|
||||
String propertyName) {
|
||||
super();
|
||||
this.configuration = configuration;
|
||||
this.rsmd = rsmd;
|
||||
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
|
||||
this.columnPosition = columnPosition;
|
||||
this.paramName = paramName;
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
protected void assign(ResultSet rs, Object param) {
|
||||
if (paramName != null) {
|
||||
// If paramName is set, param is ParamMap
|
||||
param = ((ParamMap<?>) param).get(paramName);
|
||||
}
|
||||
MetaObject metaParam = configuration.newMetaObject(param);
|
||||
try {
|
||||
if (typeHandler == null) {
|
||||
if (metaParam.hasSetter(propertyName)) {
|
||||
Class<?> propertyType = metaParam.getSetterType(propertyName);
|
||||
typeHandler = typeHandlerRegistry.getTypeHandler(propertyType,
|
||||
JdbcType.forCode(rsmd.getColumnType(columnPosition)));
|
||||
} else {
|
||||
throw new ExecutorException("No setter found for the keyProperty '" + propertyName + "' in '"
|
||||
+ metaParam.getOriginalObject().getClass().getName() + "'.");
|
||||
}
|
||||
}
|
||||
if (typeHandler == null) {
|
||||
// Error?
|
||||
} else {
|
||||
Object value = typeHandler.getResult(rs, columnPosition);
|
||||
metaParam.setValue(propertyName, value);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e,
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,154 @@
|
||||
/**
|
||||
* 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.key;
|
||||
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.enums.KeyType;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import com.mybatisflex.core.row.RowKey;
|
||||
import com.mybatisflex.core.util.ArrayUtil;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlCommandType;
|
||||
import org.apache.ibatis.mapping.SqlSource;
|
||||
import org.apache.ibatis.mapping.StatementType;
|
||||
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 为 row 的主键生成器
|
||||
*/
|
||||
public class RowKeyGenerator implements KeyGenerator, IMultiKeyGenerator {
|
||||
private static KeyGenerator[] NO_KEY_GENERATORS = new KeyGenerator[0];
|
||||
|
||||
private MappedStatement ms;
|
||||
private KeyGenerator[] keyGenerators;
|
||||
private List<String> autoKeyGeneratorNames;
|
||||
|
||||
public RowKeyGenerator(MappedStatement methodMappedStatement) {
|
||||
this.ms = methodMappedStatement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
Row row = (Row) ((Map) parameter).get(FlexConsts.ROW);
|
||||
keyGenerators = buildRowKeyGenerators(row.obtainsPrimaryKeys());
|
||||
for (KeyGenerator keyGenerator : keyGenerators) {
|
||||
keyGenerator.processBefore(executor, ms, stmt, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
|
||||
for (KeyGenerator keyGenerator : keyGenerators) {
|
||||
keyGenerator.processAfter(executor, ms, stmt, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private KeyGenerator[] buildRowKeyGenerators(RowKey[] rowKeys) {
|
||||
if (ArrayUtil.isEmpty(rowKeys)) {
|
||||
return NO_KEY_GENERATORS;
|
||||
}
|
||||
|
||||
KeyGenerator[] keyGenerators = new KeyGenerator[rowKeys.length];
|
||||
for (int i = 0; i < rowKeys.length; i++) {
|
||||
KeyGenerator keyGenerator = createByRowKey(rowKeys[i]);
|
||||
keyGenerators[i] = keyGenerator;
|
||||
}
|
||||
return keyGenerators;
|
||||
}
|
||||
|
||||
|
||||
private KeyGenerator createByRowKey(RowKey rowKey) {
|
||||
if (rowKey == null || rowKey.getKeyType() == KeyType.None) {
|
||||
return NoKeyGenerator.INSTANCE;
|
||||
}
|
||||
|
||||
if (rowKey.getKeyType() == KeyType.Auto) {
|
||||
if (autoKeyGeneratorNames == null) {
|
||||
autoKeyGeneratorNames = new ArrayList<>();
|
||||
}
|
||||
autoKeyGeneratorNames.add(rowKey.getKeyColumn());
|
||||
return new RowJdbc3KeyGenerator(rowKey.getKeyColumn());
|
||||
}
|
||||
|
||||
if (rowKey.getKeyType() == KeyType.Generator) {
|
||||
return new RowCustomKeyGenerator(rowKey);
|
||||
}
|
||||
//通过数据库的 sequence 生成主键
|
||||
else {
|
||||
String selectId = "row." + SelectKeyGenerator.SELECT_KEY_SUFFIX;
|
||||
String sequence = rowKey.getValue();
|
||||
SqlSource sqlSource = ms.getLang().createSqlSource(ms.getConfiguration(), sequence.trim(), Object.class);
|
||||
MappedStatement.Builder msBuilder = new MappedStatement.Builder(ms.getConfiguration(), selectId, sqlSource, SqlCommandType.SELECT)
|
||||
.resource(ms.getResource())
|
||||
.fetchSize(null)
|
||||
.timeout(null)
|
||||
.statementType(StatementType.PREPARED)
|
||||
.keyGenerator(NoKeyGenerator.INSTANCE)
|
||||
.keyProperty(FlexConsts.ROW + "." + rowKey.getKeyColumn())
|
||||
.keyColumn(FlexConsts.ROW + "." + rowKey.getKeyColumn())
|
||||
.databaseId(ms.getDatabaseId())
|
||||
.lang(ms.getLang())
|
||||
.resultOrdered(false)
|
||||
.resultSets(null)
|
||||
.resultMaps(new ArrayList<>())
|
||||
.resultSetType(null)
|
||||
.flushCacheRequired(false)
|
||||
.useCache(false)
|
||||
.cache(ms.getCache());
|
||||
|
||||
MappedStatement keyMappedStatement = msBuilder.build();
|
||||
ms.getConfiguration().addMappedStatement(keyMappedStatement);
|
||||
|
||||
//看到有的框架把 keyGenerator 添加到 mybatis 的当前配置里去,其实是完全没必要的
|
||||
//因为只有在 xml 解析的时候,才可能存在多一个 MappedStatement 拥有同一个 keyGenerator 的情况
|
||||
//当前每个方法都拥有一个自己的 keyGenerator 了,没必要添加
|
||||
//this.addKeyGenerator(selectId, keyGenerator);
|
||||
return new SelectKeyGenerator(keyMappedStatement, rowKey.isBefore());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要数据库生成主键
|
||||
*
|
||||
* @return true 需要生成
|
||||
*/
|
||||
@Override
|
||||
public boolean isNeedGeneratedKeys() {
|
||||
return CollectionUtil.isNotEmpty(autoKeyGeneratorNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库主键定义的 key
|
||||
*
|
||||
* @return key 数组
|
||||
*/
|
||||
@Override
|
||||
public String[] getKeyColumnNames() {
|
||||
return autoKeyGeneratorNames.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.key.impl;
|
||||
|
||||
import com.mybatisflex.core.key.IKeyGenerator;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class UUIDKeyGenerator implements IKeyGenerator {
|
||||
|
||||
@Override
|
||||
public Object generate(Object entity, String keyColumn) {
|
||||
return UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,246 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.key.MultiEntityKeyGenerator;
|
||||
import com.mybatisflex.core.key.MultiRowKeyGenerator;
|
||||
import com.mybatisflex.core.key.MybatisKeyGeneratorUtil;
|
||||
import com.mybatisflex.core.row.RowMapper;
|
||||
import com.mybatisflex.core.key.RowKeyGenerator;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
import com.mybatisflex.core.table.TableInfos;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
|
||||
import org.apache.ibatis.executor.parameter.ParameterHandler;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.logging.stdout.StdOutImpl;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.Environment;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.ResultMap;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class FlexConfiguration extends Configuration {
|
||||
|
||||
|
||||
public FlexConfiguration(Environment environment) {
|
||||
super(environment);
|
||||
setMapUnderscoreToCamelCase(true);
|
||||
setLogImpl(StdOutImpl.class);
|
||||
initDefaultMappers();
|
||||
}
|
||||
|
||||
public FlexConfiguration() {
|
||||
setLogImpl(StdOutImpl.class);
|
||||
initDefaultMappers();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 mybatis-flex 默认的 Mapper
|
||||
* 当前只有 RowMapper {@link RowMapper}
|
||||
*/
|
||||
private void initDefaultMappers() {
|
||||
addMapper(RowMapper.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 为原生 sql 设置参数
|
||||
*/
|
||||
@Override
|
||||
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
|
||||
String mappedStatementId = mappedStatement.getId();
|
||||
// 以 "!selectKey" 结尾的 mappedStatementId,是用于主键生成的,无需为其设置参数
|
||||
if (!mappedStatementId.endsWith(SelectKeyGenerator.SELECT_KEY_SUFFIX)
|
||||
&& parameterObject instanceof Map
|
||||
&& ((Map<?, ?>) parameterObject).containsKey(FlexConsts.SQL_ARGS)) {
|
||||
return new SqlArgsParameterHandler(mappedStatement, (Map) parameterObject, boundSql);
|
||||
} else {
|
||||
return super.newParameterHandler(mappedStatement, parameterObject, boundSql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换为 FlexRoutingStatementHandler,主要用来为实体类的多主键做支持
|
||||
* FlexRoutingStatementHandler 和 原生的 RoutingStatementHandler 对比,没有任何性能影响
|
||||
*/
|
||||
@Override
|
||||
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
|
||||
StatementHandler statementHandler = new FlexRoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
|
||||
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
|
||||
return statementHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMappedStatement(MappedStatement ms) {
|
||||
//替换 RowMapper.insertRow 的主键生成器
|
||||
//替换 RowMapper.insertBatchWithFirstRowColumns 的主键生成器
|
||||
if (ms.getId().startsWith("com.mybatisflex.core.row.RowMapper.insert")) {
|
||||
ms = replaceRowKeyGenerator(ms);
|
||||
}
|
||||
//entity insert methods
|
||||
else if (StringUtil.endsWithAny(ms.getId(), "insert", "insertBatchWithFirstEntityColumns")
|
||||
&& ms.getKeyGenerator() == NoKeyGenerator.INSTANCE) {
|
||||
ms = replaceEntityKeyGenerator(ms);
|
||||
}
|
||||
//entity select
|
||||
else if (StringUtil.endsWithAny(ms.getId(), "selectOneById", "selectListByIds"
|
||||
, "selectListByQuery", "selectCountByQuery")) {
|
||||
ms = replaceResultHandler(ms);
|
||||
}
|
||||
|
||||
super.addMappedStatement(ms);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 替换 entity 查询的 ResultHandler
|
||||
*/
|
||||
private MappedStatement replaceResultHandler(MappedStatement ms) {
|
||||
|
||||
TableInfo tableInfo = getTableInfo(ms);
|
||||
if (tableInfo == null) {
|
||||
return ms;
|
||||
}
|
||||
|
||||
String resultMapId = tableInfo.getEntityClass().getName();
|
||||
|
||||
ResultMap resultMap;
|
||||
if (hasResultMap(resultMapId)) {
|
||||
resultMap = getResultMap(resultMapId);
|
||||
} else {
|
||||
resultMap = tableInfo.buildResultMap(this);
|
||||
this.addResultMap(resultMap);
|
||||
}
|
||||
|
||||
return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
|
||||
.resource(ms.getResource())
|
||||
.fetchSize(ms.getFetchSize())
|
||||
.timeout(ms.getTimeout())
|
||||
.statementType(ms.getStatementType())
|
||||
.keyGenerator(NoKeyGenerator.INSTANCE)
|
||||
.keyProperty(ms.getKeyProperties() == null ? null : String.join(",", ms.getKeyProperties()))
|
||||
.keyColumn(ms.getKeyColumns() == null ? null : String.join(",", ms.getKeyColumns()))
|
||||
.databaseId(databaseId)
|
||||
.lang(ms.getLang())
|
||||
.resultOrdered(ms.isResultOrdered())
|
||||
.resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
|
||||
.resultMaps(CollectionUtil.newArrayList(resultMap)) // 替换resultMap
|
||||
.resultSetType(ms.getResultSetType())
|
||||
.flushCacheRequired(ms.isFlushCacheRequired())
|
||||
.useCache(ms.isUseCache())
|
||||
.cache(ms.getCache())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成新的、已替换主键生成器的 MappedStatement
|
||||
*
|
||||
* @param ms MappedStatement
|
||||
* @return replaced MappedStatement
|
||||
*/
|
||||
private MappedStatement replaceRowKeyGenerator(MappedStatement ms) {
|
||||
|
||||
|
||||
KeyGenerator keyGenerator = new RowKeyGenerator(ms);
|
||||
if (ms.getId().endsWith("insertBatchWithFirstRowColumns")) {
|
||||
keyGenerator = new MultiRowKeyGenerator(keyGenerator);
|
||||
}
|
||||
|
||||
return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
|
||||
.resource(ms.getResource())
|
||||
.fetchSize(ms.getFetchSize())
|
||||
.timeout(ms.getTimeout())
|
||||
.statementType(ms.getStatementType())
|
||||
.keyGenerator(keyGenerator) // 替换主键生成器
|
||||
.keyProperty(ms.getKeyProperties() == null ? null : String.join(",", ms.getKeyProperties()))
|
||||
.keyColumn(ms.getKeyColumns() == null ? null : String.join(",", ms.getKeyColumns()))
|
||||
.databaseId(databaseId)
|
||||
.lang(ms.getLang())
|
||||
.resultOrdered(ms.isResultOrdered())
|
||||
.resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
|
||||
.resultMaps(ms.getResultMaps())
|
||||
.resultSetType(ms.getResultSetType())
|
||||
.flushCacheRequired(ms.isFlushCacheRequired())
|
||||
.useCache(ms.isUseCache())
|
||||
.cache(ms.getCache())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成新的、已替换主键生成器的 MappedStatement
|
||||
*
|
||||
* @param ms MappedStatement
|
||||
* @return replaced MappedStatement
|
||||
*/
|
||||
private MappedStatement replaceEntityKeyGenerator(MappedStatement ms) {
|
||||
|
||||
TableInfo tableInfo = getTableInfo(ms);
|
||||
if (tableInfo == null) {
|
||||
return ms;
|
||||
}
|
||||
|
||||
KeyGenerator keyGenerator = MybatisKeyGeneratorUtil.createTableKeyGenerator(tableInfo, ms);
|
||||
if (keyGenerator == NoKeyGenerator.INSTANCE) {
|
||||
return ms;
|
||||
}
|
||||
|
||||
//批量插入
|
||||
if (ms.getId().endsWith("insertBatchWithFirstEntityColumns")) {
|
||||
keyGenerator = new MultiEntityKeyGenerator(keyGenerator);
|
||||
}
|
||||
|
||||
return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
|
||||
.resource(ms.getResource())
|
||||
.fetchSize(ms.getFetchSize())
|
||||
.timeout(ms.getTimeout())
|
||||
.statementType(ms.getStatementType())
|
||||
.keyGenerator(keyGenerator) // 替换主键生成器
|
||||
.keyProperty(tableInfo.getMappedStatementKeyProperties())
|
||||
.keyColumn(tableInfo.getMappedStatementKeyColumns())
|
||||
.databaseId(databaseId)
|
||||
.lang(ms.getLang())
|
||||
.resultOrdered(ms.isResultOrdered())
|
||||
.resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
|
||||
.resultMaps(ms.getResultMaps())
|
||||
.resultSetType(ms.getResultSetType())
|
||||
.flushCacheRequired(ms.isFlushCacheRequired())
|
||||
.useCache(ms.isUseCache())
|
||||
.cache(ms.getCache())
|
||||
.build();
|
||||
}
|
||||
|
||||
private TableInfo getTableInfo(MappedStatement ms) {
|
||||
String mapperClassName = ms.getId().substring(0, ms.getId().lastIndexOf("."));
|
||||
try {
|
||||
Class<?> mapperClass = Class.forName(mapperClassName);
|
||||
return TableInfos.ofMapperClass(mapperClass);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import com.mybatisflex.core.key.IMultiKeyGenerator;
|
||||
import com.mybatisflex.core.util.ArrayUtil;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||
import org.apache.ibatis.executor.statement.PreparedStatementHandler;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.ResultSetType;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
/**
|
||||
* @author Michael Yang(fuhai999@gmail.com)
|
||||
*/
|
||||
public class FlexPreparedStatementHandler extends PreparedStatementHandler {
|
||||
|
||||
public FlexPreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
|
||||
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Statement instantiateStatement(Connection connection) throws SQLException {
|
||||
// String sql = boundSql.getSql();
|
||||
// if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
|
||||
// String[] keyColumnNames = mappedStatement.getKeyColumns();
|
||||
// if (keyColumnNames == null) {
|
||||
// return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
|
||||
// } else {
|
||||
// return connection.prepareStatement(sql, keyColumnNames);
|
||||
// }
|
||||
// } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
|
||||
// return connection.prepareStatement(sql);
|
||||
// } else {
|
||||
// return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
|
||||
// }
|
||||
|
||||
String sql = boundSql.getSql();
|
||||
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
|
||||
if (keyGenerator instanceof Jdbc3KeyGenerator) {
|
||||
String[] keyColumnNames = mappedStatement.getKeyColumns();
|
||||
if (keyColumnNames == null) {
|
||||
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
|
||||
} else {
|
||||
return connection.prepareStatement(sql, keyColumnNames);
|
||||
}
|
||||
}
|
||||
// 多主键的场景
|
||||
else if (keyGenerator instanceof IMultiKeyGenerator) {
|
||||
if (((IMultiKeyGenerator) keyGenerator).isNeedGeneratedKeys()) {
|
||||
String[] keyColumnNames = ((IMultiKeyGenerator) keyGenerator).getKeyColumnNames();
|
||||
if (ArrayUtil.isNotEmpty(keyColumnNames)) {
|
||||
return connection.prepareStatement(sql, keyColumnNames);
|
||||
} else {
|
||||
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
|
||||
return connection.prepareStatement(sql);
|
||||
} else {
|
||||
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import org.apache.ibatis.cursor.Cursor;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.ExecutorException;
|
||||
import org.apache.ibatis.executor.parameter.ParameterHandler;
|
||||
import org.apache.ibatis.executor.statement.CallableStatementHandler;
|
||||
import org.apache.ibatis.executor.statement.SimpleStatementHandler;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 源于 {@link org.apache.ibatis.executor.statement.RoutingStatementHandler}
|
||||
* 主要是替换 PreparedStatementHandler 为 FlexPreparedStatementHandler
|
||||
*/
|
||||
public class FlexRoutingStatementHandler implements StatementHandler {
|
||||
|
||||
private final StatementHandler delegate;
|
||||
|
||||
public FlexRoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
|
||||
|
||||
switch (ms.getStatementType()) {
|
||||
case STATEMENT:
|
||||
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
|
||||
break;
|
||||
case PREPARED:
|
||||
// delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
|
||||
// use FlexPreparedStatementHandler to replace PreparedStatementHandler
|
||||
delegate = new FlexPreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
|
||||
break;
|
||||
case CALLABLE:
|
||||
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
|
||||
break;
|
||||
default:
|
||||
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
|
||||
return delegate.prepare(connection, transactionTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parameterize(Statement statement) throws SQLException {
|
||||
delegate.parameterize(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void batch(Statement statement) throws SQLException {
|
||||
delegate.batch(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Statement statement) throws SQLException {
|
||||
return delegate.update(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
|
||||
return delegate.query(statement, resultHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
|
||||
return delegate.queryCursor(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundSql getBoundSql() {
|
||||
return delegate.getBoundSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParameterHandler getParameterHandler() {
|
||||
return delegate.getParameterHandler();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import com.mybatisflex.core.FlexGlobalConfig;
|
||||
import com.mybatisflex.core.dialect.DbType;
|
||||
import com.mybatisflex.core.dialect.DbTypeUtil;
|
||||
import com.mybatisflex.core.exception.FlexExceptions;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
|
||||
import org.apache.ibatis.exceptions.ExceptionFactory;
|
||||
import org.apache.ibatis.executor.ErrorContext;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Properties;
|
||||
|
||||
public class FlexSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {
|
||||
|
||||
@Override
|
||||
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
|
||||
try {
|
||||
FlexXMLConfigBuilder parser = new FlexXMLConfigBuilder(inputStream, environment, properties);
|
||||
return build(parser.parse());
|
||||
} catch (Exception e) {
|
||||
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
|
||||
} finally {
|
||||
ErrorContext.instance().reset();
|
||||
try {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Intentionally ignore. Prefer previous error.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SqlSessionFactory build(Configuration configuration) {
|
||||
if (!FlexConfiguration.class.isAssignableFrom(configuration.getClass())) {
|
||||
throw FlexExceptions.wrap("only support FlexMybatisConfiguration.");
|
||||
}
|
||||
|
||||
SqlSessionFactory sessionFactory = super.build(configuration);
|
||||
|
||||
DbType dbType = getDbType(configuration);
|
||||
|
||||
//设置全局配置的 sessionFactory 和 dbType
|
||||
initGlobalConfig(configuration, sessionFactory, dbType);
|
||||
|
||||
return sessionFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置全局配置
|
||||
*
|
||||
* @param config
|
||||
* @param sessionFactory
|
||||
*/
|
||||
private void initGlobalConfig(Configuration config, SqlSessionFactory sessionFactory, DbType dbType) {
|
||||
FlexGlobalConfig flexGlobalConfig = new FlexGlobalConfig();
|
||||
flexGlobalConfig.setSqlSessionFactory(sessionFactory);
|
||||
flexGlobalConfig.setDbType(dbType);
|
||||
|
||||
String environmentId = config.getEnvironment().getId();
|
||||
FlexGlobalConfig.setConfig(environmentId, flexGlobalConfig);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前配置的 DbType
|
||||
*/
|
||||
private DbType getDbType(Configuration configuration) {
|
||||
DataSource dataSource = configuration.getEnvironment().getDataSource();
|
||||
String jdbcUrl = getJdbcUrl(dataSource);
|
||||
if (StringUtil.isNotBlank(jdbcUrl)){
|
||||
return DbTypeUtil.parseDbType(jdbcUrl);
|
||||
}
|
||||
|
||||
if ("org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy"
|
||||
.equals(dataSource.getClass().getName())){
|
||||
return DbType.H2;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过数据源中获取 jdbc 的 url 配置
|
||||
* 符合 HikariCP, druid, c3p0, DBCP, beecp 数据源框架 以及 MyBatis UnpooledDataSource 的获取规则
|
||||
* UnpooledDataSource 参考 @{@link UnpooledDataSource#getUrl()}
|
||||
* TODO: 2023/2/18 可能极个别特殊的数据源无法获取 JDBC 配置的 URL
|
||||
*
|
||||
* @return jdbc url 配置
|
||||
*/
|
||||
private String getJdbcUrl(DataSource dataSource) {
|
||||
String[] methodNames = new String[]{"getUrl", "getJdbcUrl"};
|
||||
for (String methodName : methodNames) {
|
||||
try {
|
||||
Method method = dataSource.getClass().getMethod(methodName);
|
||||
return (String) method.invoke(dataSource);
|
||||
} catch (Exception e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,409 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import org.apache.ibatis.builder.BaseBuilder;
|
||||
import org.apache.ibatis.builder.BuilderException;
|
||||
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
|
||||
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
|
||||
import org.apache.ibatis.datasource.DataSourceFactory;
|
||||
import org.apache.ibatis.executor.ErrorContext;
|
||||
import org.apache.ibatis.executor.loader.ProxyFactory;
|
||||
import org.apache.ibatis.io.Resources;
|
||||
import org.apache.ibatis.io.VFS;
|
||||
import org.apache.ibatis.logging.Log;
|
||||
import org.apache.ibatis.mapping.DatabaseIdProvider;
|
||||
import org.apache.ibatis.mapping.Environment;
|
||||
import org.apache.ibatis.parsing.XNode;
|
||||
import org.apache.ibatis.parsing.XPathParser;
|
||||
import org.apache.ibatis.plugin.Interceptor;
|
||||
import org.apache.ibatis.reflection.DefaultReflectorFactory;
|
||||
import org.apache.ibatis.reflection.MetaClass;
|
||||
import org.apache.ibatis.reflection.ReflectorFactory;
|
||||
import org.apache.ibatis.reflection.factory.ObjectFactory;
|
||||
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
|
||||
import org.apache.ibatis.session.*;
|
||||
import org.apache.ibatis.transaction.TransactionFactory;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author Clinton Begin
|
||||
* @author Kazuki Shimizu
|
||||
*/
|
||||
public class FlexXMLConfigBuilder extends BaseBuilder {
|
||||
|
||||
private boolean parsed;
|
||||
private final XPathParser parser;
|
||||
private String environment;
|
||||
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
|
||||
|
||||
public FlexXMLConfigBuilder(Reader reader) {
|
||||
this(reader, null, null);
|
||||
}
|
||||
|
||||
public FlexXMLConfigBuilder(Reader reader, String environment) {
|
||||
this(reader, environment, null);
|
||||
}
|
||||
|
||||
public FlexXMLConfigBuilder(Reader reader, String environment, Properties props) {
|
||||
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
|
||||
}
|
||||
|
||||
public FlexXMLConfigBuilder(InputStream inputStream) {
|
||||
this(inputStream, null, null);
|
||||
}
|
||||
|
||||
public FlexXMLConfigBuilder(InputStream inputStream, String environment) {
|
||||
this(inputStream, environment, null);
|
||||
}
|
||||
|
||||
public FlexXMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
|
||||
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
|
||||
}
|
||||
|
||||
private FlexXMLConfigBuilder(XPathParser parser, String environment, Properties props) {
|
||||
// 目前暂时只能通过这里替换 configuration
|
||||
// 新的版本已经支持自定义 configuration,但需新版本 release:
|
||||
// 详情 https://github.com/mybatis/mybatis-3/commit/d7826d71a7005a8b4d4e0e7a020db0f6c7e253a4
|
||||
super(new FlexConfiguration());
|
||||
ErrorContext.instance().resource("SQL Mapper Configuration");
|
||||
this.configuration.setVariables(props);
|
||||
this.parsed = false;
|
||||
this.environment = environment;
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
public Configuration parse() {
|
||||
if (parsed) {
|
||||
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
|
||||
}
|
||||
parsed = true;
|
||||
parseConfiguration(parser.evalNode("/configuration"));
|
||||
return configuration;
|
||||
}
|
||||
|
||||
private void parseConfiguration(XNode root) {
|
||||
try {
|
||||
// issue #117 read properties first
|
||||
propertiesElement(root.evalNode("properties"));
|
||||
Properties settings = settingsAsProperties(root.evalNode("settings"));
|
||||
loadCustomVfs(settings);
|
||||
loadCustomLogImpl(settings);
|
||||
typeAliasesElement(root.evalNode("typeAliases"));
|
||||
pluginElement(root.evalNode("plugins"));
|
||||
objectFactoryElement(root.evalNode("objectFactory"));
|
||||
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
|
||||
reflectorFactoryElement(root.evalNode("reflectorFactory"));
|
||||
settingsElement(settings);
|
||||
// read it after objectFactory and objectWrapperFactory issue #631
|
||||
environmentsElement(root.evalNode("environments"));
|
||||
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
|
||||
typeHandlerElement(root.evalNode("typeHandlers"));
|
||||
mapperElement(root.evalNode("mappers"));
|
||||
} catch (Exception e) {
|
||||
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Properties settingsAsProperties(XNode context) {
|
||||
if (context == null) {
|
||||
return new Properties();
|
||||
}
|
||||
Properties props = context.getChildrenAsProperties();
|
||||
// Check that all settings are known to the configuration class
|
||||
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
|
||||
for (Object key : props.keySet()) {
|
||||
if (!metaConfig.hasSetter(String.valueOf(key))) {
|
||||
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
private void loadCustomVfs(Properties props) throws ClassNotFoundException {
|
||||
String value = props.getProperty("vfsImpl");
|
||||
if (value != null) {
|
||||
String[] clazzes = value.split(",");
|
||||
for (String clazz : clazzes) {
|
||||
if (!clazz.isEmpty()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
|
||||
configuration.setVfsImpl(vfsImpl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadCustomLogImpl(Properties props) {
|
||||
Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
|
||||
configuration.setLogImpl(logImpl);
|
||||
}
|
||||
|
||||
private void typeAliasesElement(XNode parent) {
|
||||
if (parent != null) {
|
||||
for (XNode child : parent.getChildren()) {
|
||||
if ("package".equals(child.getName())) {
|
||||
String typeAliasPackage = child.getStringAttribute("name");
|
||||
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
|
||||
} else {
|
||||
String alias = child.getStringAttribute("alias");
|
||||
String type = child.getStringAttribute("type");
|
||||
try {
|
||||
Class<?> clazz = Resources.classForName(type);
|
||||
if (alias == null) {
|
||||
typeAliasRegistry.registerAlias(clazz);
|
||||
} else {
|
||||
typeAliasRegistry.registerAlias(alias, clazz);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void pluginElement(XNode parent) throws Exception {
|
||||
if (parent != null) {
|
||||
for (XNode child : parent.getChildren()) {
|
||||
String interceptor = child.getStringAttribute("interceptor");
|
||||
Properties properties = child.getChildrenAsProperties();
|
||||
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
|
||||
interceptorInstance.setProperties(properties);
|
||||
configuration.addInterceptor(interceptorInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void objectFactoryElement(XNode context) throws Exception {
|
||||
if (context != null) {
|
||||
String type = context.getStringAttribute("type");
|
||||
Properties properties = context.getChildrenAsProperties();
|
||||
ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();
|
||||
factory.setProperties(properties);
|
||||
configuration.setObjectFactory(factory);
|
||||
}
|
||||
}
|
||||
|
||||
private void objectWrapperFactoryElement(XNode context) throws Exception {
|
||||
if (context != null) {
|
||||
String type = context.getStringAttribute("type");
|
||||
ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();
|
||||
configuration.setObjectWrapperFactory(factory);
|
||||
}
|
||||
}
|
||||
|
||||
private void reflectorFactoryElement(XNode context) throws Exception {
|
||||
if (context != null) {
|
||||
String type = context.getStringAttribute("type");
|
||||
ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();
|
||||
configuration.setReflectorFactory(factory);
|
||||
}
|
||||
}
|
||||
|
||||
private void propertiesElement(XNode context) throws Exception {
|
||||
if (context != null) {
|
||||
Properties defaults = context.getChildrenAsProperties();
|
||||
String resource = context.getStringAttribute("resource");
|
||||
String url = context.getStringAttribute("url");
|
||||
if (resource != null && url != null) {
|
||||
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
|
||||
}
|
||||
if (resource != null) {
|
||||
defaults.putAll(Resources.getResourceAsProperties(resource));
|
||||
} else if (url != null) {
|
||||
defaults.putAll(Resources.getUrlAsProperties(url));
|
||||
}
|
||||
Properties vars = configuration.getVariables();
|
||||
if (vars != null) {
|
||||
defaults.putAll(vars);
|
||||
}
|
||||
parser.setVariables(defaults);
|
||||
configuration.setVariables(defaults);
|
||||
}
|
||||
}
|
||||
|
||||
private void settingsElement(Properties props) {
|
||||
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
|
||||
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
|
||||
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
|
||||
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
|
||||
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
|
||||
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
|
||||
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
|
||||
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
|
||||
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
|
||||
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
|
||||
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
|
||||
configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
|
||||
configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
|
||||
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
|
||||
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
|
||||
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
|
||||
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
|
||||
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
|
||||
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
|
||||
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
|
||||
configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
|
||||
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
|
||||
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
|
||||
configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
|
||||
configuration.setLogPrefix(props.getProperty("logPrefix"));
|
||||
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
|
||||
configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
|
||||
configuration.setArgNameBasedConstructorAutoMapping(booleanValueOf(props.getProperty("argNameBasedConstructorAutoMapping"), false));
|
||||
configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
|
||||
configuration.setNullableOnForEach(booleanValueOf(props.getProperty("nullableOnForEach"), false));
|
||||
}
|
||||
|
||||
private void environmentsElement(XNode context) throws Exception {
|
||||
if (context != null) {
|
||||
if (environment == null) {
|
||||
environment = context.getStringAttribute("default");
|
||||
}
|
||||
for (XNode child : context.getChildren()) {
|
||||
String id = child.getStringAttribute("id");
|
||||
if (isSpecifiedEnvironment(id)) {
|
||||
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
|
||||
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
|
||||
DataSource dataSource = dsFactory.getDataSource();
|
||||
Environment.Builder environmentBuilder = new Environment.Builder(id)
|
||||
.transactionFactory(txFactory)
|
||||
.dataSource(dataSource);
|
||||
configuration.setEnvironment(environmentBuilder.build());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void databaseIdProviderElement(XNode context) throws Exception {
|
||||
DatabaseIdProvider databaseIdProvider = null;
|
||||
if (context != null) {
|
||||
String type = context.getStringAttribute("type");
|
||||
// awful patch to keep backward compatibility
|
||||
if ("VENDOR".equals(type)) {
|
||||
type = "DB_VENDOR";
|
||||
}
|
||||
Properties properties = context.getChildrenAsProperties();
|
||||
databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();
|
||||
databaseIdProvider.setProperties(properties);
|
||||
}
|
||||
Environment environment = configuration.getEnvironment();
|
||||
if (environment != null && databaseIdProvider != null) {
|
||||
String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
|
||||
configuration.setDatabaseId(databaseId);
|
||||
}
|
||||
}
|
||||
|
||||
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
|
||||
if (context != null) {
|
||||
String type = context.getStringAttribute("type");
|
||||
Properties props = context.getChildrenAsProperties();
|
||||
TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
|
||||
factory.setProperties(props);
|
||||
return factory;
|
||||
}
|
||||
throw new BuilderException("Environment declaration requires a TransactionFactory.");
|
||||
}
|
||||
|
||||
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
|
||||
if (context != null) {
|
||||
String type = context.getStringAttribute("type");
|
||||
Properties props = context.getChildrenAsProperties();
|
||||
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
|
||||
factory.setProperties(props);
|
||||
return factory;
|
||||
}
|
||||
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
|
||||
}
|
||||
|
||||
private void typeHandlerElement(XNode parent) {
|
||||
if (parent != null) {
|
||||
for (XNode child : parent.getChildren()) {
|
||||
if ("package".equals(child.getName())) {
|
||||
String typeHandlerPackage = child.getStringAttribute("name");
|
||||
typeHandlerRegistry.register(typeHandlerPackage);
|
||||
} else {
|
||||
String javaTypeName = child.getStringAttribute("javaType");
|
||||
String jdbcTypeName = child.getStringAttribute("jdbcType");
|
||||
String handlerTypeName = child.getStringAttribute("handler");
|
||||
Class<?> javaTypeClass = resolveClass(javaTypeName);
|
||||
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
|
||||
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
|
||||
if (javaTypeClass != null) {
|
||||
if (jdbcType == null) {
|
||||
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
|
||||
} else {
|
||||
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
|
||||
}
|
||||
} else {
|
||||
typeHandlerRegistry.register(typeHandlerClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mapperElement(XNode parent) throws Exception {
|
||||
if (parent != null) {
|
||||
for (XNode child : parent.getChildren()) {
|
||||
if ("package".equals(child.getName())) {
|
||||
String mapperPackage = child.getStringAttribute("name");
|
||||
configuration.addMappers(mapperPackage);
|
||||
} else {
|
||||
String resource = child.getStringAttribute("resource");
|
||||
String url = child.getStringAttribute("url");
|
||||
String mapperClass = child.getStringAttribute("class");
|
||||
if (resource != null && url == null && mapperClass == null) {
|
||||
ErrorContext.instance().resource(resource);
|
||||
try(InputStream inputStream = Resources.getResourceAsStream(resource)) {
|
||||
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
|
||||
mapperParser.parse();
|
||||
}
|
||||
} else if (resource == null && url != null && mapperClass == null) {
|
||||
ErrorContext.instance().resource(url);
|
||||
try(InputStream inputStream = Resources.getUrlAsStream(url)){
|
||||
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
|
||||
mapperParser.parse();
|
||||
}
|
||||
} else if (resource == null && url == null && mapperClass != null) {
|
||||
Class<?> mapperInterface = Resources.classForName(mapperClass);
|
||||
configuration.addMapper(mapperInterface);
|
||||
} else {
|
||||
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSpecifiedEnvironment(String id) {
|
||||
if (environment == null) {
|
||||
throw new BuilderException("No environment specified.");
|
||||
}
|
||||
if (id == null) {
|
||||
throw new BuilderException("Environment requires an id attribute.");
|
||||
}
|
||||
return environment.equals(id);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.mybatisflex.core.mybatis;
|
||||
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
public class SqlArgsParameterHandler extends DefaultParameterHandler {
|
||||
|
||||
private final Map parameterObject;
|
||||
|
||||
|
||||
public SqlArgsParameterHandler(MappedStatement mappedStatement, Map parameterObject, BoundSql boundSql) {
|
||||
super(mappedStatement, parameterObject, boundSql);
|
||||
this.parameterObject = parameterObject;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setParameters(PreparedStatement ps) {
|
||||
try {
|
||||
doSetParameters(ps);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void doSetParameters(PreparedStatement ps) throws SQLException {
|
||||
Object[] sqlArgs = (Object[]) ((Map<?, ?>) parameterObject).get(FlexConsts.SQL_ARGS);
|
||||
if (sqlArgs != null && sqlArgs.length > 0) {
|
||||
int index = 1;
|
||||
for (Object value : sqlArgs) {
|
||||
//在 Oracle、SqlServer 中 TIMESTAMP、DATE 类型的数据是支持 java.util.Date 给值的
|
||||
if (value instanceof java.util.Date) {
|
||||
setDateParameter(ps, (Date) value, index++);
|
||||
} else if (value instanceof byte[]) {
|
||||
ps.setBytes(index++, (byte[]) value);
|
||||
} else {
|
||||
/** 在 MySql,Oracle 等驱动中,通过 PreparedStatement.setObject 后,驱动会自动根据 value 内容进行转换
|
||||
* 源码可参考: {{@link com.mysql.jdbc.PreparedStatement#setObject(int, Object)}
|
||||
**/
|
||||
ps.setObject(index++, value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
super.setParameters(ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle、SqlServer 需要主动设置下 date 类型
|
||||
* MySql 通过 setObject 后会自动转换,具体查看 MySql 驱动源码
|
||||
*
|
||||
* @param ps PreparedStatement
|
||||
* @param value date value
|
||||
* @param index set to index
|
||||
* @throws SQLException
|
||||
*/
|
||||
private void setDateParameter(PreparedStatement ps, Date value, int index) throws SQLException {
|
||||
if (value instanceof java.sql.Date) {
|
||||
ps.setDate(index, (java.sql.Date) value);
|
||||
} else if (value instanceof java.sql.Timestamp) {
|
||||
ps.setTimestamp(index, (java.sql.Timestamp) value);
|
||||
} else {
|
||||
ps.setTimestamp(index, new java.sql.Timestamp(value.getTime()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
/**
|
||||
* 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.paginate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
public class Page<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final int INIT_VALUE = -1;
|
||||
|
||||
private List<T> list; // list result of this page
|
||||
private int pageNumber = INIT_VALUE; // page number
|
||||
private int pageSize = INIT_VALUE; // result amount of this page
|
||||
private long totalPage = INIT_VALUE; // total page
|
||||
private long totalRow = INIT_VALUE; // total row
|
||||
|
||||
public static Page of(int pageNumber, int pageSize) {
|
||||
return new Page(pageNumber, pageSize);
|
||||
}
|
||||
|
||||
public static Page of(int pageNumber, int pageSize, long totalRow) {
|
||||
return new Page(pageNumber, pageSize, totalRow);
|
||||
}
|
||||
|
||||
public Page() {
|
||||
|
||||
}
|
||||
|
||||
public Page(int pageNumber, int pageSize) {
|
||||
this.pageNumber = pageNumber;
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
public Page(int pageNumber, int pageSize, long totalRow) {
|
||||
this.pageNumber = pageNumber;
|
||||
this.pageSize = pageSize;
|
||||
this.totalRow = totalRow;
|
||||
this.totalPage = totalRow % pageSize == 0 ? (totalRow / pageSize) : (totalRow / pageSize + 1);
|
||||
}
|
||||
|
||||
public Page(List<T> list, int pageNumber, int pageSize, long totalRow) {
|
||||
this.list = list;
|
||||
this.pageNumber = pageNumber;
|
||||
this.pageSize = pageSize;
|
||||
this.totalRow = totalRow;
|
||||
this.totalPage = totalRow % pageSize == 0 ? (totalRow / pageSize) : (totalRow / pageSize + 1);
|
||||
}
|
||||
|
||||
|
||||
public List<T> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(List<T> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public int getPageNumber() {
|
||||
return pageNumber;
|
||||
}
|
||||
|
||||
public void setPageNumber(int pageNumber) {
|
||||
this.pageNumber = pageNumber;
|
||||
}
|
||||
|
||||
|
||||
public int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
public void setPageSize(int pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
public long getTotalPage() {
|
||||
return totalPage;
|
||||
}
|
||||
|
||||
public void setTotalPage(long totalPage) {
|
||||
this.totalPage = totalPage;
|
||||
}
|
||||
|
||||
public long getTotalRow() {
|
||||
return totalRow;
|
||||
}
|
||||
|
||||
public void setTotalRow(long totalRow) {
|
||||
this.totalRow = totalRow;
|
||||
this.totalPage = totalRow % pageSize == 0 ? (totalRow / pageSize) : (totalRow / pageSize + 1);
|
||||
}
|
||||
|
||||
public boolean isFirstPage() {
|
||||
return pageNumber == 1;
|
||||
}
|
||||
|
||||
public boolean isLastPage() {
|
||||
return pageNumber >= totalPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Page{" +
|
||||
"pageNumber=" + pageNumber +
|
||||
", pageSize=" + pageSize +
|
||||
", totalPage=" + totalPage +
|
||||
", totalRow=" + totalRow +
|
||||
", list=" + list +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,292 @@
|
||||
/**
|
||||
* 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.provider;
|
||||
|
||||
import com.mybatisflex.core.dialect.DialectFactory;
|
||||
import com.mybatisflex.core.exception.FlexExceptions;
|
||||
import com.mybatisflex.core.querywrapper.QueryWrapper;
|
||||
import com.mybatisflex.core.querywrapper.CPI;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
import com.mybatisflex.core.util.ArrayUtil;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import org.apache.ibatis.builder.annotation.ProviderContext;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class EntitySqlProvider {
|
||||
|
||||
|
||||
/**
|
||||
* 不让实例化,使用静态方法的模式,效率更高,非静态方法每次都会实例化当前类
|
||||
* 参考源码: {{@link org.apache.ibatis.builder.annotation.ProviderSqlSource#getBoundSql(Object)}
|
||||
*/
|
||||
private EntitySqlProvider() {
|
||||
}
|
||||
|
||||
/**
|
||||
* insert 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @param context
|
||||
* @return sql
|
||||
* @see com.mybatisflex.core.BaseMapper#insert(Object)
|
||||
*/
|
||||
public static String insert(Map params, ProviderContext context) {
|
||||
Object entity = ProviderUtil.getEntity(params);
|
||||
if (entity == null) {
|
||||
throw FlexExceptions.wrap("entity can not be null.");
|
||||
}
|
||||
|
||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
||||
|
||||
Object[] values = tableInfo.obtainInsertValues(entity);
|
||||
ProviderUtil.setSqlArgs(params, values);
|
||||
|
||||
return DialectFactory.getDialect().forInsertEntity(tableInfo, entity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* insertBatchWithFirstEntityColumns 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @param context
|
||||
* @return sql
|
||||
* @see com.mybatisflex.core.BaseMapper#insertBatchWithFirstEntityColumns(List)
|
||||
*/
|
||||
public static String insertBatchWithFirstEntityColumns(Map params, ProviderContext context) {
|
||||
List<Object> entities = ProviderUtil.getEntities(params);
|
||||
if (CollectionUtil.isEmpty(entities)) {
|
||||
throw FlexExceptions.wrap("entities can not be null or empty.");
|
||||
}
|
||||
|
||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
||||
Object[] values = new Object[0];
|
||||
for (Object entity : entities) {
|
||||
values = ArrayUtil.concat(values, tableInfo.obtainInsertValues(entity));
|
||||
}
|
||||
|
||||
ProviderUtil.setSqlArgs(params, values);
|
||||
|
||||
return DialectFactory.getDialect().forInsertBatchWithFirstEntityColumns(tableInfo, entities);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* deleteById 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @param context
|
||||
* @return sql
|
||||
* @see com.mybatisflex.core.BaseMapper#deleteById(Serializable)
|
||||
*/
|
||||
public static String deleteById(Map params, ProviderContext context) {
|
||||
Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
|
||||
if (ArrayUtil.isEmpty(primaryValues)) {
|
||||
throw FlexExceptions.wrap("primaryValues can not be null or empty.");
|
||||
}
|
||||
|
||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
||||
ProviderUtil.setSqlArgs(params, primaryValues);
|
||||
|
||||
return DialectFactory.getDialect().forDeleteEntityById(tableInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* deleteBatchByIds 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @param context
|
||||
* @return sql
|
||||
* @see com.mybatisflex.core.BaseMapper#deleteBatchByIds(Collection)
|
||||
*/
|
||||
public static String deleteBatchByIds(Map params, ProviderContext context) {
|
||||
Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
|
||||
if (ArrayUtil.isEmpty(primaryValues)) {
|
||||
throw FlexExceptions.wrap("primaryValues can not be null or empty.");
|
||||
}
|
||||
|
||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
||||
ProviderUtil.setSqlArgs(params, primaryValues);
|
||||
|
||||
return DialectFactory.getDialect().forDeleteEntityBatchById(tableInfo, primaryValues);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* deleteByQuery 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @param context
|
||||
* @return sql
|
||||
* @see com.mybatisflex.core.BaseMapper#deleteByQuery(QueryWrapper)
|
||||
*/
|
||||
public static String deleteByQuery(Map params, ProviderContext context) {
|
||||
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
||||
if (queryWrapper == null) {
|
||||
throw FlexExceptions.wrap("queryWrapper can not be null or empty.");
|
||||
}
|
||||
|
||||
ProviderUtil.setSqlArgs(params, CPI.getValueArray(queryWrapper));
|
||||
|
||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
||||
queryWrapper.from(tableInfo.getTableName());
|
||||
return DialectFactory.getDialect().forDeleteByQuery(queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* update 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @param context
|
||||
* @return sql
|
||||
* @see com.mybatisflex.core.BaseMapper#update(Object, boolean)
|
||||
*/
|
||||
public static String update(Map params, ProviderContext context) {
|
||||
Object entity = ProviderUtil.getEntity(params);
|
||||
if (entity == null) {
|
||||
throw FlexExceptions.wrap("entity can not be null");
|
||||
}
|
||||
|
||||
boolean ignoreNulls = ProviderUtil.isIgnoreNulls(params);
|
||||
|
||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
||||
Object[] updateValues = tableInfo.obtainUpdateValues(entity, ignoreNulls, false);
|
||||
Object[] primaryValues = tableInfo.obtainPrimaryValues(entity);
|
||||
ProviderUtil.setSqlArgs(params, ArrayUtil.concat(updateValues, primaryValues));
|
||||
|
||||
return DialectFactory.getDialect().forUpdateEntity(tableInfo, entity, ignoreNulls);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* updateByQuery 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @param context
|
||||
* @return sql
|
||||
* @see com.mybatisflex.core.BaseMapper#updateByQuery(Object, QueryWrapper)
|
||||
*/
|
||||
public static String updateByQuery(Map params, ProviderContext context) {
|
||||
Object entity = ProviderUtil.getEntity(params);
|
||||
if (entity == null) {
|
||||
throw FlexExceptions.wrap("entity can not be null");
|
||||
}
|
||||
boolean ignoreNulls = ProviderUtil.isIgnoreNulls(params);
|
||||
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
||||
|
||||
|
||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
||||
Object[] values = tableInfo.obtainUpdateValues(entity, ignoreNulls, true);
|
||||
|
||||
ProviderUtil.setSqlArgs(params, ArrayUtil.concat(values, CPI.getValueArray(queryWrapper)));
|
||||
|
||||
return DialectFactory.getDialect().forUpdateEntityByQuery(tableInfo, entity, ignoreNulls, queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* selectOneById 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @param context
|
||||
* @return sql
|
||||
* @see com.mybatisflex.core.BaseMapper#selectOneById(Serializable)
|
||||
*/
|
||||
public static String selectOneById(Map params, ProviderContext context) {
|
||||
Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
|
||||
if (ArrayUtil.isEmpty(primaryValues)) {
|
||||
throw FlexExceptions.wrap("primaryValues can not be null or empty.");
|
||||
}
|
||||
|
||||
ProviderUtil.setSqlArgs(params, primaryValues);
|
||||
|
||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
||||
return DialectFactory.getDialect().forSelectOneEntityById(tableInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* selectListByIds 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @param context
|
||||
* @return sql
|
||||
* @see com.mybatisflex.core.BaseMapper#selectListByIds(Collection)
|
||||
*/
|
||||
public static String selectListByIds(Map params, ProviderContext context) {
|
||||
Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
|
||||
if (ArrayUtil.isEmpty(primaryValues)) {
|
||||
throw FlexExceptions.wrap("primaryValues can not be null or empty.");
|
||||
}
|
||||
|
||||
ProviderUtil.setSqlArgs(params, primaryValues);
|
||||
|
||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
||||
return DialectFactory.getDialect().forSelectEntityListByIds(tableInfo, primaryValues);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* selectListByQuery 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @param context
|
||||
* @return sql
|
||||
* @see com.mybatisflex.core.BaseMapper#selectListByQuery(QueryWrapper)
|
||||
*/
|
||||
public static String selectListByQuery(Map params, ProviderContext context) {
|
||||
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
||||
if (queryWrapper == null) {
|
||||
throw FlexExceptions.wrap("queryWrapper can not be null.");
|
||||
}
|
||||
Object[] values = CPI.getValueArray(queryWrapper);
|
||||
ProviderUtil.setSqlArgs(params, values);
|
||||
|
||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
||||
queryWrapper.from(tableInfo.getTableName());
|
||||
return DialectFactory.getDialect().forSelectListByQuery(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* selectCountByQuery 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @param context
|
||||
* @return sql
|
||||
* @see com.mybatisflex.core.BaseMapper#selectCountByQuery(QueryWrapper)
|
||||
*/
|
||||
public static String selectCountByQuery(Map params, ProviderContext context) {
|
||||
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
||||
if (queryWrapper == null) {
|
||||
throw FlexExceptions.wrap("queryWrapper can not be null.");
|
||||
}
|
||||
|
||||
Object[] values = CPI.getValueArray(queryWrapper);
|
||||
ProviderUtil.setSqlArgs(params, values);
|
||||
|
||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
||||
queryWrapper.from(tableInfo.getTableName());
|
||||
return DialectFactory.getDialect().forSelectCountByQuery(queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 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.provider;
|
||||
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.exception.FlexExceptions;
|
||||
import com.mybatisflex.core.querywrapper.QueryWrapper;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
import com.mybatisflex.core.table.TableInfos;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
import org.apache.ibatis.builder.annotation.ProviderContext;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
class ProviderUtil {
|
||||
|
||||
private static final Object[] NULL_ARGS = new Object[0];
|
||||
|
||||
public static String getSqlString(Map params) {
|
||||
return (String) params.get(FlexConsts.SQL);
|
||||
}
|
||||
|
||||
public static void setSqlArgs(Map params, Object[] args) {
|
||||
params.put(FlexConsts.SQL_ARGS, args);
|
||||
}
|
||||
|
||||
public static String getTableName(Map params) {
|
||||
return params.get(FlexConsts.TABLE_NAME).toString().trim();
|
||||
}
|
||||
|
||||
public static String[] getPrimaryKeys(Map params) {
|
||||
String primaryKey = (String) params.get(FlexConsts.PRIMARY_KEY);
|
||||
if (StringUtil.isBlank(primaryKey)) {
|
||||
throw FlexExceptions.wrap("primaryKey can not be null or blank.");
|
||||
}
|
||||
String[] primaryKeys = primaryKey.split(",");
|
||||
for (int i = 0; i < primaryKeys.length; i++) {
|
||||
primaryKeys[i] = primaryKeys[i].trim();
|
||||
}
|
||||
return primaryKeys;
|
||||
}
|
||||
|
||||
public static Object[] getPrimaryValues(Map params) {
|
||||
Object primaryValue = params.get(FlexConsts.PRIMARY_VALUE);
|
||||
if (primaryValue == null) {
|
||||
return NULL_ARGS;
|
||||
}
|
||||
if (primaryValue.getClass().isArray()) {
|
||||
return (Object[]) primaryValue;
|
||||
} else if (primaryValue instanceof Collection) {
|
||||
return ((Collection<?>) primaryValue).toArray();
|
||||
} else {
|
||||
return new Object[]{primaryValue};
|
||||
}
|
||||
}
|
||||
|
||||
public static QueryWrapper getQueryWrapper(Map params) {
|
||||
return (QueryWrapper) params.get(FlexConsts.QUERY);
|
||||
}
|
||||
|
||||
public static Row getRow(Map params) {
|
||||
return (Row) params.get(FlexConsts.ROW);
|
||||
}
|
||||
|
||||
public static List<Row> getRows(Map params) {
|
||||
return (List<Row>) params.get(FlexConsts.ROWS);
|
||||
}
|
||||
|
||||
public static TableInfo getTableInfo(ProviderContext context){
|
||||
return TableInfos.ofMapperClass(context.getMapperType());
|
||||
}
|
||||
|
||||
public static Object getEntity(Map params) {
|
||||
return params.get(FlexConsts.ENTITY);
|
||||
}
|
||||
|
||||
|
||||
public static List<Object> getEntities(Map params) {
|
||||
return (List<Object>) params.get(FlexConsts.ENTITIES);
|
||||
}
|
||||
|
||||
public static boolean isIgnoreNulls(Map params){
|
||||
return params.containsKey(FlexConsts.IGNORE_NULLS) && (boolean) params.get(FlexConsts.IGNORE_NULLS);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,272 @@
|
||||
/**
|
||||
* 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.provider;
|
||||
|
||||
import com.mybatisflex.core.dialect.DialectFactory;
|
||||
import com.mybatisflex.core.exception.FlexExceptions;
|
||||
import com.mybatisflex.core.querywrapper.CPI;
|
||||
import com.mybatisflex.core.querywrapper.QueryWrapper;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import com.mybatisflex.core.row.RowMapper;
|
||||
import com.mybatisflex.core.util.ArrayUtil;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class RowSqlProvider {
|
||||
|
||||
|
||||
public static final String METHOD_RAW_SQL = "providerRawSql";
|
||||
|
||||
/**
|
||||
* 不让实例化,使用静态方法的模式,效率更高,非静态方法每次都会实例化当前类
|
||||
* 参考源码: {{@link org.apache.ibatis.builder.annotation.ProviderSqlSource#getBoundSql(Object)}
|
||||
*/
|
||||
private RowSqlProvider() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行原生 sql 的方法
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#insertBySql(String, Object...)
|
||||
* @see RowMapper#deleteBySql(String, Object...)
|
||||
* @see RowMapper#updateBySql(String, Object...)
|
||||
*/
|
||||
public static String providerRawSql(Map params) {
|
||||
return ProviderUtil.getSqlString(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* insertRow 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#insertRow(String, Row)
|
||||
*/
|
||||
public static String insertRow(Map params) {
|
||||
String tableName = ProviderUtil.getTableName(params);
|
||||
Row row = ProviderUtil.getRow(params);
|
||||
ProviderUtil.setSqlArgs(params, row.obtainModifyValues());
|
||||
return DialectFactory.getDialect().forInsertRow(tableName, row);
|
||||
}
|
||||
|
||||
/**
|
||||
* insertBatch 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#insertBatchWithFirstRowColumns(String, List)
|
||||
*/
|
||||
public static String insertBatchWithFirstRowColumns(Map params) {
|
||||
String tableName = ProviderUtil.getTableName(params);
|
||||
List<Row> rows = ProviderUtil.getRows(params);
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
throw FlexExceptions.wrap("rows can not be null or empty.");
|
||||
}
|
||||
|
||||
//让所有 row 的列顺序和值的数量与第条数据保持一致
|
||||
Set<String> modifyAttrs = rows.get(0).obtainModifyAttrs();
|
||||
rows.forEach(row -> row.keep(modifyAttrs));
|
||||
|
||||
|
||||
Object[] values = new Object[]{};
|
||||
for (Row row : rows) {
|
||||
values = ArrayUtil.concat(values, row.obtainModifyValues());
|
||||
}
|
||||
ProviderUtil.setSqlArgs(params, values);
|
||||
|
||||
|
||||
//sql: INSERT INTO `tb_table`(`name`, `sex`) VALUES (?, ?),(?, ?),(?, ?)
|
||||
return DialectFactory.getDialect().forInsertBatchWithFirstRowColumns(tableName, rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* deleteById 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#deleteById(String, String, Object)
|
||||
*/
|
||||
public static String deleteById(Map params) {
|
||||
String tableName = ProviderUtil.getTableName(params);
|
||||
String[] primaryKeys = ProviderUtil.getPrimaryKeys(params);
|
||||
Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
|
||||
|
||||
if (primaryValues.length == 0) {
|
||||
throw FlexExceptions.wrap("primaryValue can not be null");
|
||||
} else {
|
||||
ProviderUtil.setSqlArgs(params, primaryValues);
|
||||
}
|
||||
|
||||
return DialectFactory.getDialect().forDeleteById(tableName, primaryKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* deleteBatchByIds 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#deleteBatchByIds(String, String, Collection)
|
||||
*/
|
||||
public static String deleteBatchByIds(Map params) {
|
||||
String tableName = ProviderUtil.getTableName(params);
|
||||
String[] primaryKeys = ProviderUtil.getPrimaryKeys(params);
|
||||
Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
|
||||
|
||||
ProviderUtil.setSqlArgs(params, primaryValues);
|
||||
return DialectFactory.getDialect().forDeleteBatchByIds(tableName, primaryKeys, primaryValues);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* deleteByQuery 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#deleteByQuery(String, QueryWrapper)
|
||||
*/
|
||||
public static String deleteByQuery(Map params) {
|
||||
String tableName = ProviderUtil.getTableName(params);
|
||||
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
||||
|
||||
Object[] valueArray = CPI.getValueArray(queryWrapper);
|
||||
ProviderUtil.setSqlArgs(params, valueArray);
|
||||
|
||||
queryWrapper.from(tableName);
|
||||
return DialectFactory.getDialect().forDeleteByQuery(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* updateById 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#updateById(String, Row)
|
||||
*/
|
||||
public static String updateById(Map params) {
|
||||
String tableName = ProviderUtil.getTableName(params);
|
||||
Row row = ProviderUtil.getRow(params);
|
||||
ProviderUtil.setSqlArgs(params, row.obtainModifyValuesAndPrimaryValues());
|
||||
return DialectFactory.getDialect().forUpdateById(tableName, row);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* updateByQuery 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#updateByQuery(String, Row, QueryWrapper)
|
||||
*/
|
||||
public static String updateByQuery(Map params) {
|
||||
String tableName = ProviderUtil.getTableName(params);
|
||||
Row data = ProviderUtil.getRow(params);
|
||||
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
||||
|
||||
|
||||
Object[] modifyValues = data.obtainModifyValues();
|
||||
Object[] valueArray = CPI.getValueArray(queryWrapper);
|
||||
|
||||
ProviderUtil.setSqlArgs(params, ArrayUtil.concat(modifyValues, valueArray));
|
||||
|
||||
return DialectFactory.getDialect().forUpdateByQuery(tableName, data, queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* updateBatchById 的 sql 构建
|
||||
* mysql 等链接配置需要开启 allowMultiQueries=true
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#updateBatchById(String, List)
|
||||
*/
|
||||
public static String updateBatchById(Map params) {
|
||||
String tableName = ProviderUtil.getTableName(params);
|
||||
List<Row> rows = ProviderUtil.getRows(params);
|
||||
if (CollectionUtil.isEmpty(rows)) {
|
||||
throw FlexExceptions.wrap("rows can not be null or empty.");
|
||||
}
|
||||
|
||||
Object[] values = new Object[0];
|
||||
for (Row row : rows) {
|
||||
values = ArrayUtil.concat(values, row.obtainModifyValuesAndPrimaryValues());
|
||||
}
|
||||
ProviderUtil.setSqlArgs(params, values);
|
||||
return DialectFactory.getDialect().forUpdateBatchById(tableName, rows);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* selectOneById 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#selectOneById(String, String, Object)
|
||||
*/
|
||||
public static String selectOneById(Map params) {
|
||||
String tableName = ProviderUtil.getTableName(params);
|
||||
String[] primaryKeys = ProviderUtil.getPrimaryKeys(params);
|
||||
Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
|
||||
|
||||
ProviderUtil.setSqlArgs(params, primaryValues);
|
||||
|
||||
return DialectFactory.getDialect().forSelectOneById(tableName, primaryKeys, primaryValues);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* selectListByQuery 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#selectListByQuery(String, QueryWrapper)
|
||||
*/
|
||||
public static String selectListByQuery(Map params) {
|
||||
String tableName = ProviderUtil.getTableName(params);
|
||||
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
||||
|
||||
Object[] valueArray = CPI.getValueArray(queryWrapper);
|
||||
ProviderUtil.setSqlArgs(params, valueArray);
|
||||
|
||||
queryWrapper.from(tableName);
|
||||
return DialectFactory.getDialect().forSelectListByQuery(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* selectCountByQuery 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#selectCountByQuery(String, QueryWrapper)
|
||||
*/
|
||||
public static String selectCountByQuery(Map params) {
|
||||
String tableName = ProviderUtil.getTableName(params);
|
||||
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
||||
|
||||
Object[] valueArray = CPI.getValueArray(queryWrapper);
|
||||
ProviderUtil.setSqlArgs(params, valueArray);
|
||||
|
||||
return DialectFactory.getDialect().forSelectCountByQuery(queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,204 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class BaseQueryWrapper<T> implements Serializable {
|
||||
|
||||
protected List<QueryTable> queryTables;
|
||||
protected String datasource ;
|
||||
|
||||
protected List<QueryColumn> selectColumns;
|
||||
protected List<Join> joins;
|
||||
protected List<QueryTable> joinTables;
|
||||
protected QueryCondition whereQueryCondition;
|
||||
protected List<QueryColumn> groupByColumns;
|
||||
protected QueryCondition havingQueryCondition;
|
||||
protected List<QueryOrderBy> orderBys;
|
||||
|
||||
protected Integer limitOffset;
|
||||
protected Integer limitRows;
|
||||
|
||||
|
||||
protected T addSelectColumn(QueryColumn queryColumn){
|
||||
if (selectColumns == null){
|
||||
selectColumns = new LinkedList<>();
|
||||
}
|
||||
|
||||
selectColumns.add(queryColumn);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
protected T AddJoin(Join join){
|
||||
if (joins == null){
|
||||
joins = new LinkedList<>();
|
||||
}
|
||||
joins.add(join);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
protected T setWhereQueryCondition(QueryCondition queryCondition){
|
||||
if (whereQueryCondition != null){
|
||||
queryCondition.connect(whereQueryCondition,SqlConnector.AND);
|
||||
}
|
||||
|
||||
whereQueryCondition = queryCondition;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
protected T addWhereQueryCondition(QueryCondition queryCondition,SqlConnector connector){
|
||||
if (queryCondition != null) {
|
||||
if (whereQueryCondition == null) {
|
||||
whereQueryCondition = queryCondition;
|
||||
} else {
|
||||
whereQueryCondition.connect(queryCondition, connector);
|
||||
}
|
||||
}
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected T addGroupByColumns(QueryColumn queryColumn){
|
||||
if (groupByColumns == null){
|
||||
groupByColumns = new LinkedList<>();
|
||||
}
|
||||
|
||||
groupByColumns.add(queryColumn);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected T addHavingQueryCondition(QueryCondition queryCondition,SqlConnector connector){
|
||||
if (havingQueryCondition == null){
|
||||
havingQueryCondition = queryCondition;
|
||||
}else {
|
||||
havingQueryCondition.connect(queryCondition,connector);
|
||||
}
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected T addOrderBy(QueryOrderBy queryOrderBy){
|
||||
if (orderBys == null){
|
||||
orderBys = new LinkedList<>();
|
||||
}
|
||||
orderBys.add(queryOrderBy);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
protected void addJoinTable(QueryTable queryTable){
|
||||
if (joinTables == null){
|
||||
joinTables = new ArrayList<>();
|
||||
}
|
||||
joinTables.add(queryTable);
|
||||
}
|
||||
|
||||
|
||||
protected List<QueryTable> getQueryTables() {
|
||||
return queryTables;
|
||||
}
|
||||
|
||||
protected void setQueryTables(List<QueryTable> queryTables) {
|
||||
this.queryTables = queryTables;
|
||||
}
|
||||
|
||||
protected String getDatasource() {
|
||||
return datasource;
|
||||
}
|
||||
|
||||
protected void setDatasource(String datasource) {
|
||||
this.datasource = datasource;
|
||||
}
|
||||
|
||||
protected List<QueryColumn> getSelectColumns() {
|
||||
return selectColumns;
|
||||
}
|
||||
|
||||
protected void setSelectColumns(List<QueryColumn> selectColumns) {
|
||||
this.selectColumns = selectColumns;
|
||||
}
|
||||
|
||||
protected List<Join> getJoins() {
|
||||
return joins;
|
||||
}
|
||||
|
||||
protected void setJoins(List<Join> joins) {
|
||||
this.joins = joins;
|
||||
}
|
||||
|
||||
protected List<QueryTable> getJoinTables() {
|
||||
return joinTables;
|
||||
}
|
||||
|
||||
protected void setJoinTables(List<QueryTable> joinTables) {
|
||||
this.joinTables = joinTables;
|
||||
}
|
||||
|
||||
protected QueryCondition getWhereQueryCondition() {
|
||||
return whereQueryCondition;
|
||||
}
|
||||
|
||||
protected List<QueryColumn> getGroupByColumns() {
|
||||
return groupByColumns;
|
||||
}
|
||||
|
||||
protected void setGroupByColumns(List<QueryColumn> groupByColumns) {
|
||||
this.groupByColumns = groupByColumns;
|
||||
}
|
||||
|
||||
protected QueryCondition getHavingQueryCondition() {
|
||||
return havingQueryCondition;
|
||||
}
|
||||
|
||||
protected void setHavingQueryCondition(QueryCondition havingQueryCondition) {
|
||||
this.havingQueryCondition = havingQueryCondition;
|
||||
}
|
||||
|
||||
protected List<QueryOrderBy> getOrderBys() {
|
||||
return orderBys;
|
||||
}
|
||||
|
||||
protected void setOrderBys(List<QueryOrderBy> orderBys) {
|
||||
this.orderBys = orderBys;
|
||||
}
|
||||
|
||||
protected Integer getLimitOffset() {
|
||||
return limitOffset;
|
||||
}
|
||||
|
||||
protected void setLimitOffset(Integer limitOffset) {
|
||||
this.limitOffset = limitOffset;
|
||||
}
|
||||
|
||||
protected Integer getLimitRows() {
|
||||
return limitRows;
|
||||
}
|
||||
|
||||
protected void setLimitRows(Integer limitRows) {
|
||||
this.limitRows = limitRows;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 括号
|
||||
*/
|
||||
public class Brackets extends QueryCondition {
|
||||
|
||||
private QueryCondition childCondition;
|
||||
|
||||
|
||||
public Brackets(QueryCondition childCondition) {
|
||||
this.childCondition = childCondition;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public QueryCondition and(QueryCondition nextCondition) {
|
||||
connectToChild(nextCondition, SqlConnector.AND);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryCondition or(QueryCondition nextCondition) {
|
||||
connectToChild(nextCondition, SqlConnector.OR);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void connectToChild(QueryCondition nextCondition, SqlConnector connector) {
|
||||
childCondition.connect(nextCondition, connector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return checkEffective() ? WrapperUtil.getValues(childCondition) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
if (checkEffective()) {
|
||||
String childSql = childCondition.toSql(queryTables, dialect);
|
||||
QueryCondition effectiveBefore = getEffectiveBefore();
|
||||
if (effectiveBefore != null) {
|
||||
childSql = effectiveBefore.connector + "(" + childSql + ")";
|
||||
}
|
||||
sql.append(childSql);
|
||||
}
|
||||
|
||||
if (this.next != null) {
|
||||
return sql + next.toSql(queryTables, dialect);
|
||||
}
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Brackets{" +
|
||||
"childCondition=" + childCondition +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -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.querywrapper;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Cross Package Invoke
|
||||
* 夸包调用工具类,这么设计的原因,是需要保证 QueryWrapper 方法对于用户的纯净性
|
||||
* 而 framework 又可以通过 CPI 来调用 QueryWrapper 的其他方法
|
||||
*/
|
||||
|
||||
public class CPI {
|
||||
|
||||
|
||||
public static Object[] getValueArray(QueryWrapper queryWrapper) {
|
||||
return queryWrapper.getValueArray();
|
||||
}
|
||||
|
||||
|
||||
public static List<QueryTable> getQueryTables(QueryWrapper queryWrapper) {
|
||||
return queryWrapper.getQueryTables();
|
||||
}
|
||||
|
||||
public static void setQueryTable(QueryWrapper queryWrapper, List<QueryTable> queryTables) {
|
||||
queryWrapper.setQueryTables(queryTables);
|
||||
}
|
||||
|
||||
public static String getDatasource(QueryWrapper queryWrapper) {
|
||||
return queryWrapper.getDatasource();
|
||||
}
|
||||
|
||||
public static void setDatasource(QueryWrapper queryWrapper, String datasource) {
|
||||
queryWrapper.setDatasource(datasource);
|
||||
}
|
||||
|
||||
public static List<QueryColumn> getSelectColumns(QueryWrapper queryWrapper) {
|
||||
return queryWrapper.getSelectColumns();
|
||||
}
|
||||
|
||||
public static void setSelectColumns(QueryWrapper queryWrapper, List<QueryColumn> selectColumns) {
|
||||
queryWrapper.setSelectColumns(selectColumns);
|
||||
}
|
||||
|
||||
public static List<Join> getJoins(QueryWrapper queryWrapper) {
|
||||
return queryWrapper.getJoins();
|
||||
}
|
||||
|
||||
public static void setJoins(QueryWrapper queryWrapper, List<Join> joins) {
|
||||
queryWrapper.setJoins(joins);
|
||||
}
|
||||
|
||||
|
||||
public static List<QueryTable> getJoinTables(QueryWrapper queryWrapper) {
|
||||
return queryWrapper.getJoinTables();
|
||||
}
|
||||
|
||||
public static void setJoinTables(QueryWrapper queryWrapper,List<QueryTable> joinTables) {
|
||||
queryWrapper.setJoinTables(joinTables);
|
||||
}
|
||||
|
||||
|
||||
public static QueryCondition getWhereQueryCondition(QueryWrapper queryWrapper) {
|
||||
return queryWrapper.getWhereQueryCondition();
|
||||
}
|
||||
|
||||
public static List<QueryColumn> getGroupByColumns(QueryWrapper queryWrapper) {
|
||||
return queryWrapper.getGroupByColumns();
|
||||
}
|
||||
|
||||
public static void setGroupByColumns(QueryWrapper queryWrapper, List<QueryColumn> groupByColumns) {
|
||||
queryWrapper.setGroupByColumns(groupByColumns);
|
||||
}
|
||||
|
||||
public static QueryCondition getHavingQueryCondition(QueryWrapper queryWrapper) {
|
||||
return queryWrapper.getHavingQueryCondition();
|
||||
}
|
||||
|
||||
public static void setHavingQueryCondition(QueryWrapper queryWrapper, QueryCondition havingQueryCondition) {
|
||||
queryWrapper.setHavingQueryCondition(havingQueryCondition);
|
||||
}
|
||||
|
||||
public static List<QueryOrderBy> getOrderBys(QueryWrapper queryWrapper) {
|
||||
return queryWrapper.getOrderBys();
|
||||
}
|
||||
|
||||
public static void setOrderBys(QueryWrapper queryWrapper, List<QueryOrderBy> orderBys) {
|
||||
queryWrapper.setOrderBys(orderBys);
|
||||
}
|
||||
|
||||
public static Integer getLimitOffset(QueryWrapper queryWrapper) {
|
||||
return queryWrapper.getLimitOffset();
|
||||
}
|
||||
|
||||
public static void setLimitOffset(QueryWrapper queryWrapper, Integer limitOffset) {
|
||||
queryWrapper.setLimitOffset(limitOffset);
|
||||
}
|
||||
|
||||
public static Integer getLimitRows(QueryWrapper queryWrapper) {
|
||||
return queryWrapper.getLimitRows();
|
||||
}
|
||||
|
||||
public static void setLimitRows(QueryWrapper queryWrapper, Integer limitRows) {
|
||||
queryWrapper.setLimitRows(limitRows);
|
||||
}
|
||||
|
||||
|
||||
public static String toConditionSql(QueryColumn queryColumn,List<QueryTable> queryTables, IDialect dialect) {
|
||||
return queryColumn.toConditionSql(queryTables,dialect);
|
||||
}
|
||||
|
||||
public static String toSelectSql(QueryColumn queryColumn,List<QueryTable> queryTables, IDialect dialect) {
|
||||
return queryColumn.toSelectSql(queryTables,dialect);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.mybatisflex.core.querywrapper;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DistinctQueryColumn extends QueryColumn {
|
||||
|
||||
private List<QueryColumn> queryColumns;
|
||||
|
||||
public DistinctQueryColumn(QueryColumn... queryColumns) {
|
||||
this.queryColumns = CollectionUtil.newArrayList(queryColumns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
if (CollectionUtil.isEmpty(queryTables)) {
|
||||
return "";
|
||||
}
|
||||
return " DISTINCT " + StringUtil.join(",", queryColumns, queryColumn ->
|
||||
queryColumn.toSelectSql(queryTables, dialect));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据库 聚合函数,例如 count(id) ,max(account.age) 等等
|
||||
*/
|
||||
public class FunctionQueryColumn extends QueryColumn {
|
||||
|
||||
protected String fnName;
|
||||
protected QueryColumn column;
|
||||
|
||||
public FunctionQueryColumn(String fnName, String column) {
|
||||
this.fnName = fnName;
|
||||
this.column = new QueryColumn(column);
|
||||
}
|
||||
|
||||
public FunctionQueryColumn(String fnName, QueryColumn column) {
|
||||
this.fnName = fnName;
|
||||
this.column = column;
|
||||
}
|
||||
|
||||
public String getFnName() {
|
||||
return fnName;
|
||||
}
|
||||
|
||||
public void setFnName(String fnName) {
|
||||
this.fnName = fnName;
|
||||
}
|
||||
|
||||
public QueryColumn getColumn() {
|
||||
return column;
|
||||
}
|
||||
|
||||
public void setColumn(QueryColumn column) {
|
||||
this.column = column;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
String sql = column.toSelectSql(queryTables, dialect);
|
||||
return StringUtil.isBlank(sql) ? "" : fnName + "(" + sql + ")" + WrapperUtil.buildAsAlias(alias);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author michael yang (fuhai999@gmail.com)
|
||||
* @Date: 2020/1/14
|
||||
*/
|
||||
public class Join implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
static final String TYPE_LEFT = " LEFT JOIN ";
|
||||
static final String TYPE_RIGHT = " RIGHT JOIN ";
|
||||
static final String TYPE_INNER = " INNER JOIN ";
|
||||
static final String TYPE_FULL = " FULL JOIN ";
|
||||
static final String TYPE_CROSS = " CROSS JOIN ";
|
||||
|
||||
|
||||
private String type;
|
||||
private QueryTable queryTable;
|
||||
private QueryCondition on;
|
||||
private boolean effective;
|
||||
|
||||
public Join(String type, String table, boolean when) {
|
||||
this.type = type;
|
||||
this.queryTable = new QueryTable(table);
|
||||
this.effective = when;
|
||||
}
|
||||
|
||||
public Join(String type, QueryWrapper queryWrapper, boolean when) {
|
||||
this.type = type;
|
||||
this.queryTable = new SelectQueryTable(queryWrapper);
|
||||
this.effective = when;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QueryTable getQueryTable() {
|
||||
return queryTable;
|
||||
}
|
||||
|
||||
|
||||
public void on(QueryCondition condition) {
|
||||
this.on = condition;
|
||||
}
|
||||
|
||||
|
||||
public boolean checkEffective() {
|
||||
return effective;
|
||||
}
|
||||
|
||||
public void when(boolean when) {
|
||||
this.effective = when;
|
||||
}
|
||||
|
||||
public void when(Supplier<Boolean> fn) {
|
||||
this.effective = fn.get();
|
||||
}
|
||||
|
||||
public String toSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
//left join, right join, inner join ...
|
||||
StringBuilder sql = new StringBuilder(type);
|
||||
sql.append(queryTable.toSql(dialect));
|
||||
|
||||
//left join xxx as xxx2 on xxx2.id = xxx3.other
|
||||
List<QueryTable> newQueryTables = new ArrayList<>(queryTables);
|
||||
newQueryTables.add(queryTable);
|
||||
sql.append(" ON ").append(on.toSql(newQueryTables, dialect));
|
||||
return sql.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
/**
|
||||
* @author michael yang (fuhai999@gmail.com)
|
||||
* @Date: 2020/1/14
|
||||
*/
|
||||
public class Joiner<M> {
|
||||
|
||||
private M queryWrapper;
|
||||
private Join join;
|
||||
|
||||
public Joiner(M queryWrapper, Join join) {
|
||||
this.queryWrapper = queryWrapper;
|
||||
this.join = join;
|
||||
}
|
||||
|
||||
public Joiner<M> as(String alias) {
|
||||
join.getQueryTable().as(alias);
|
||||
return this;
|
||||
}
|
||||
|
||||
public M on(String on) {
|
||||
join.on(new StringQueryCondition(on));
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
public M on(QueryCondition on) {
|
||||
join.on(on);
|
||||
return queryWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
package com.mybatisflex.core.querywrapper;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 操作类型的操作
|
||||
* 示例1:and not ( id > 100 and name like %%)
|
||||
*/
|
||||
public class OperatorQueryCondition extends QueryCondition {
|
||||
|
||||
private String operator;
|
||||
private QueryCondition child;
|
||||
|
||||
public OperatorQueryCondition(String operator, QueryCondition child) {
|
||||
this.operator = operator;
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
//检测是否生效
|
||||
if (checkEffective()) {
|
||||
String childSql = child.toSql(queryTables, dialect);
|
||||
if (StringUtil.isNotBlank(childSql)) {
|
||||
QueryCondition effectiveBefore = getEffectiveBefore();
|
||||
if (effectiveBefore != null) {
|
||||
sql.append(effectiveBefore.connector);
|
||||
}
|
||||
sql.append(operator).append("(").append(childSql).append(")");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.next != null) {
|
||||
return sql + next.toSql(queryTables, dialect);
|
||||
}
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return WrapperUtil.getValues(child);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package com.mybatisflex.core.querywrapper;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 操作类型的操作
|
||||
* 示例1:and exist (select 1 from ... where ....)
|
||||
* 示例2:and not exist (select ... from ... where ....)
|
||||
*/
|
||||
public class OperatorSelectCondition extends QueryCondition {
|
||||
//操作符,例如 exist, not exist
|
||||
private String operator;
|
||||
private QueryWrapper queryWrapper;
|
||||
|
||||
public OperatorSelectCondition(String operator, QueryWrapper queryWrapper) {
|
||||
this.operator = operator;
|
||||
this.queryWrapper = queryWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
//检测是否生效
|
||||
if (checkEffective()) {
|
||||
String childSql = dialect.buildSelectSql(queryWrapper);
|
||||
if (StringUtil.isNotBlank(childSql)) {
|
||||
|
||||
QueryCondition effectiveBefore = getEffectiveBefore();
|
||||
if (effectiveBefore != null) {
|
||||
sql.append(effectiveBefore.connector);
|
||||
}
|
||||
sql.append(operator).append("(").append(childSql).append(")");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.next != null) {
|
||||
return sql + next.toSql(queryTables, dialect);
|
||||
}
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return queryWrapper.getValueArray();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,325 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.table.TableDef;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 查询列,描述的是一张表的字段
|
||||
*/
|
||||
public class QueryColumn implements Serializable {
|
||||
|
||||
protected QueryTable table;
|
||||
protected String name;
|
||||
protected String alias;
|
||||
|
||||
|
||||
public QueryColumn() {
|
||||
}
|
||||
|
||||
public QueryColumn(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public QueryColumn(String tableName, String name) {
|
||||
this.table = new QueryTable(tableName);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public QueryColumn(TableDef tableDef, String name) {
|
||||
this.table = new QueryTable(tableDef.getTableName());
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public QueryTable getTable() {
|
||||
return table;
|
||||
}
|
||||
|
||||
public void setTable(QueryTable table) {
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public QueryColumn as(String alias) {
|
||||
QueryColumn newColumn = new QueryColumn();
|
||||
newColumn.table = this.table;
|
||||
newColumn.name = this.name;
|
||||
newColumn.alias = alias;
|
||||
return newColumn;
|
||||
}
|
||||
|
||||
|
||||
// query methods ///////
|
||||
|
||||
/**
|
||||
* equals
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition eq(Object value) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_EQUALS, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* not equals !=
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition ne(Object value) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_NOT_EQUALS, value);
|
||||
}
|
||||
|
||||
|
||||
public QueryCondition like(Object value) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_LIKE, "%" + value + "%");
|
||||
}
|
||||
|
||||
|
||||
public QueryCondition likeLeft(Object value) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_LIKE, "%" + value);
|
||||
}
|
||||
|
||||
|
||||
public QueryCondition likeRight(Object value) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_LIKE, value + "%");
|
||||
}
|
||||
|
||||
/**
|
||||
* 大于 greater than
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition gt(Object value) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_GT, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 大于等于 greater or equal
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition ge(Object value) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_GE, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小于 less than
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition lt(Object value) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_LT, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小于等于 less or equal
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition le(Object value) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_LE, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* IS NULL
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition isNull(String name) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_IS_NULL, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* IS NOT NULL
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition isNotNull(String name) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_IS_NOT_NULL, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* in arrays
|
||||
*
|
||||
* @param arrays
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition in(Object... arrays) {
|
||||
//忽略 QueryWrapper.in("name", null) 的情况
|
||||
if (arrays != null && arrays.length == 1 && arrays[0] == null) {
|
||||
return QueryCondition.createEmpty();
|
||||
}
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_IN, arrays);
|
||||
}
|
||||
|
||||
/**
|
||||
* in child select
|
||||
* @param queryWrapper
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition in(QueryWrapper queryWrapper) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_IN, queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* in Collection
|
||||
*
|
||||
* @param collection
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition in(Collection<?> collection) {
|
||||
if (collection != null && !collection.isEmpty()) {
|
||||
return in(collection.toArray());
|
||||
}
|
||||
return QueryCondition.createEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* not int arrays
|
||||
*
|
||||
* @param arrays
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition notIn(Object... arrays) {
|
||||
//忽略 QueryWrapper.notIn("name", null) 的情况
|
||||
if (arrays != null && arrays.length == 1 && arrays[0] == null) {
|
||||
return QueryCondition.createEmpty();
|
||||
}
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_NOT_IN, arrays);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* not in Collection
|
||||
*
|
||||
* @param collection
|
||||
* @return
|
||||
*/
|
||||
public QueryCondition notIn(Collection<?> collection) {
|
||||
if (collection != null && !collection.isEmpty()) {
|
||||
return notIn(collection.toArray());
|
||||
}
|
||||
return QueryCondition.createEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* not in child select
|
||||
* @param queryWrapper
|
||||
*/
|
||||
public QueryCondition notIn(QueryWrapper queryWrapper) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_NOT_IN, queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* between
|
||||
* @param start
|
||||
* @param end
|
||||
*/
|
||||
public QueryCondition between(Object start, Object end) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_BETWEEN, new Object[]{start, end});
|
||||
}
|
||||
|
||||
/**
|
||||
* not between
|
||||
*
|
||||
* @param start
|
||||
* @param end
|
||||
*/
|
||||
public QueryCondition notBetween(Object start, Object end) {
|
||||
return QueryCondition.create(this, QueryCondition.LOGIC_NOT_BETWEEN, new Object[]{start, end});
|
||||
}
|
||||
|
||||
|
||||
////orrder by ////
|
||||
public QueryOrderBy asc() {
|
||||
QueryOrderBy queryOrderBy = new QueryOrderBy(this);
|
||||
return queryOrderBy;
|
||||
}
|
||||
|
||||
|
||||
public QueryOrderBy desc() {
|
||||
return new QueryOrderBy(this, "DESC");
|
||||
}
|
||||
|
||||
|
||||
public String wrap(IDialect dialect, String table, String column) {
|
||||
if (StringUtil.isNotBlank(table)) {
|
||||
return dialect.wrap(table) + "." + dialect.wrap(column);
|
||||
} else {
|
||||
return dialect.wrap(column);
|
||||
}
|
||||
}
|
||||
|
||||
String toConditionSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
String tableName = WrapperUtil.getRealTableName(queryTables, table);
|
||||
return wrap(dialect, tableName, name);
|
||||
}
|
||||
|
||||
|
||||
String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
String tableName = WrapperUtil.getRealTableName(queryTables, table);
|
||||
return wrap(dialect, tableName, name) + WrapperUtil.buildAsAlias(dialect.wrap(alias));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "QueryColumn{" +
|
||||
"table=" + table +
|
||||
", name='" + name + '\'' +
|
||||
", alias='" + alias + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,252 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class QueryCondition implements Serializable {
|
||||
|
||||
public static final String LOGIC_LIKE = "LIKE";
|
||||
public static final String LOGIC_GT = ">";
|
||||
public static final String LOGIC_GE = ">=";
|
||||
public static final String LOGIC_LT = "<";
|
||||
public static final String LOGIC_LE = "<=";
|
||||
public static final String LOGIC_EQUALS = "=";
|
||||
public static final String LOGIC_NOT_EQUALS = "!=";
|
||||
|
||||
public static final String LOGIC_IS_NULL = "IS NULL";
|
||||
public static final String LOGIC_IS_NOT_NULL = "IS NOT NULL";
|
||||
|
||||
public static final String LOGIC_IN = "IN";
|
||||
public static final String LOGIC_NOT_IN = "NOT IN";
|
||||
public static final String LOGIC_BETWEEN = "BETWEEN";
|
||||
public static final String LOGIC_NOT_BETWEEN = "NOT BETWEEN";
|
||||
|
||||
|
||||
protected QueryColumn column;
|
||||
protected String logic;
|
||||
protected Object value;
|
||||
protected boolean effective = true;
|
||||
|
||||
//当前条件的上个条件
|
||||
protected QueryCondition before;
|
||||
//当前条件的上个下一个
|
||||
protected QueryCondition next;
|
||||
//两个条件直接的连接符
|
||||
protected SqlConnector connector;
|
||||
|
||||
|
||||
public static QueryCondition createEmpty() {
|
||||
return new QueryCondition().when(false);
|
||||
}
|
||||
|
||||
|
||||
public static QueryCondition create(QueryColumn queryColumn, Object value) {
|
||||
return create(queryColumn, LOGIC_EQUALS, value);
|
||||
}
|
||||
|
||||
public static QueryCondition create(QueryColumn queryColumn, String logic, Object value) {
|
||||
QueryCondition column = new QueryCondition();
|
||||
column.setColumn(queryColumn);
|
||||
column.setLogic(logic);
|
||||
column.setValue(value);
|
||||
return column;
|
||||
}
|
||||
|
||||
public QueryCondition() {
|
||||
}
|
||||
|
||||
public QueryColumn getColumn() {
|
||||
return column;
|
||||
}
|
||||
|
||||
public void setColumn(QueryColumn column) {
|
||||
this.column = column;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getLogic() {
|
||||
return logic;
|
||||
}
|
||||
|
||||
public void setLogic(String logic) {
|
||||
this.logic = logic;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算问号(?)的数量
|
||||
*
|
||||
* @return 问号的数量
|
||||
*/
|
||||
public int calculateQuestionMarkCount() {
|
||||
if (LOGIC_IS_NULL.equals(logic)
|
||||
|| LOGIC_IS_NOT_NULL.equals(logic)
|
||||
|| value instanceof QueryColumn
|
||||
|| value instanceof QueryWrapper) {
|
||||
return 0;
|
||||
}
|
||||
//between, not between
|
||||
else if (LOGIC_BETWEEN.equals(logic) || LOGIC_NOT_BETWEEN.equals(logic)) {
|
||||
return 2;
|
||||
}
|
||||
//in, not in
|
||||
else if (LOGIC_IN.equals(logic) || LOGIC_NOT_IN.equals(logic)) {
|
||||
return calculateValueArrayCount();
|
||||
}
|
||||
//
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private int calculateValueArrayCount() {
|
||||
Object[] values = (Object[]) value;
|
||||
int paramsCount = 0;
|
||||
for (Object v : values) {
|
||||
if (v.getClass() == int[].class) {
|
||||
paramsCount += ((int[]) v).length;
|
||||
} else if (v.getClass() == long[].class) {
|
||||
paramsCount += ((long[]) v).length;
|
||||
} else if (v.getClass() == short[].class) {
|
||||
paramsCount += ((short[]) v).length;
|
||||
} else {
|
||||
paramsCount++;
|
||||
}
|
||||
}
|
||||
return paramsCount;
|
||||
}
|
||||
|
||||
public QueryCondition when(boolean effective) {
|
||||
this.effective = effective;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void when(Supplier<Boolean> fn) {
|
||||
this.effective = fn.get();
|
||||
}
|
||||
|
||||
public boolean checkEffective() {
|
||||
return effective;
|
||||
}
|
||||
|
||||
|
||||
public QueryCondition and(QueryCondition nextCondition) {
|
||||
return new Brackets(this).and(nextCondition);
|
||||
}
|
||||
|
||||
|
||||
public QueryCondition or(QueryCondition nextCondition) {
|
||||
return new Brackets(this).or(nextCondition);
|
||||
}
|
||||
|
||||
|
||||
protected void connect(QueryCondition nextCondition, SqlConnector connector) {
|
||||
if (this.next != null) {
|
||||
this.next.connect(nextCondition, connector);
|
||||
} else {
|
||||
this.next = nextCondition;
|
||||
this.connector = connector;
|
||||
nextCondition.before = this;
|
||||
}
|
||||
}
|
||||
|
||||
public String toSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
//检测是否生效
|
||||
if (checkEffective()) {
|
||||
QueryCondition effectiveBefore = getEffectiveBefore();
|
||||
if (effectiveBefore != null) {
|
||||
sql.append(effectiveBefore.connector);
|
||||
}
|
||||
sql.append(getColumn().toConditionSql(queryTables, dialect));
|
||||
sql.append(" ").append(logic).append(" ");
|
||||
if (value instanceof QueryColumn) {
|
||||
sql.append(((QueryColumn) value).toConditionSql(queryTables, dialect));
|
||||
}
|
||||
//子查询
|
||||
else if (value instanceof QueryWrapper) {
|
||||
sql.append("(").append(dialect.buildSelectSql((QueryWrapper) value)).append(")");
|
||||
}
|
||||
//正常查询,构建问号
|
||||
else {
|
||||
appendQuestionMark(sql, calculateQuestionMarkCount());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.next != null) {
|
||||
return sql + next.toSql(queryTables, dialect);
|
||||
}
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
|
||||
protected QueryCondition getEffectiveBefore() {
|
||||
if (before != null && before.checkEffective()) {
|
||||
return before;
|
||||
} else if (before != null) {
|
||||
return before.getEffectiveBefore();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static void appendQuestionMark(StringBuilder sqlBuilder, int paramsCount) {
|
||||
if (paramsCount == 1) {
|
||||
sqlBuilder.append(" ? ");
|
||||
}
|
||||
//between, not between
|
||||
else if (paramsCount == 2) {
|
||||
sqlBuilder.append(" ? AND ? ");
|
||||
}
|
||||
//in, not in
|
||||
else if (paramsCount > 0) {
|
||||
sqlBuilder.append('(');
|
||||
for (int i = 0; i < paramsCount; i++) {
|
||||
sqlBuilder.append('?');
|
||||
if (i != paramsCount - 1) {
|
||||
sqlBuilder.append(',');
|
||||
}
|
||||
}
|
||||
sqlBuilder.append(')');
|
||||
} else {
|
||||
// paramsCount == 0, ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "QueryCondition{" +
|
||||
"column=" + column +
|
||||
", logic='" + logic + '\'' +
|
||||
", value=" + value +
|
||||
", effective=" + effective +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
public class QueryMethods {
|
||||
|
||||
public static FunctionQueryColumn count() {
|
||||
return new FunctionQueryColumn("COUNT", "*");
|
||||
}
|
||||
|
||||
public static FunctionQueryColumn count(String column) {
|
||||
return new FunctionQueryColumn("COUNT", column);
|
||||
}
|
||||
|
||||
public static FunctionQueryColumn count(QueryColumn column) {
|
||||
return new FunctionQueryColumn("COUNT", column);
|
||||
}
|
||||
|
||||
public static FunctionQueryColumn max(String column) {
|
||||
return new FunctionQueryColumn("MAX", column);
|
||||
}
|
||||
|
||||
public static FunctionQueryColumn max(QueryColumn column) {
|
||||
return new FunctionQueryColumn("MAX", column);
|
||||
}
|
||||
|
||||
public static FunctionQueryColumn min(String column) {
|
||||
return new FunctionQueryColumn("MIN", column);
|
||||
}
|
||||
|
||||
public static FunctionQueryColumn min(QueryColumn column) {
|
||||
return new FunctionQueryColumn("MIN", column);
|
||||
}
|
||||
|
||||
public static FunctionQueryColumn avg(String column) {
|
||||
return new FunctionQueryColumn("AVG", column);
|
||||
}
|
||||
|
||||
public static FunctionQueryColumn avg(QueryColumn column) {
|
||||
return new FunctionQueryColumn("AVG", column);
|
||||
}
|
||||
|
||||
public static FunctionQueryColumn sum(String column) {
|
||||
return new FunctionQueryColumn("SUM", column);
|
||||
}
|
||||
|
||||
public static FunctionQueryColumn sum(QueryColumn column) {
|
||||
return new FunctionQueryColumn("SUM", column);
|
||||
}
|
||||
|
||||
public static DistinctQueryColumn distinct(QueryColumn... columns) {
|
||||
return new DistinctQueryColumn(columns);
|
||||
}
|
||||
|
||||
public static QueryCondition exist(QueryWrapper queryWrapper) {
|
||||
return new OperatorSelectCondition(" EXIST ", queryWrapper);
|
||||
}
|
||||
|
||||
public static QueryCondition notExist(QueryWrapper queryWrapper) {
|
||||
return new OperatorSelectCondition(" NOT EXIST ", queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
public static QueryCondition not(QueryCondition childCondition) {
|
||||
return new OperatorQueryCondition(" NOT ", childCondition);
|
||||
}
|
||||
|
||||
public static QueryCondition noCondition(){
|
||||
return QueryCondition.createEmpty();
|
||||
}
|
||||
|
||||
|
||||
private static QueryWrapper newWrapper() {
|
||||
return new QueryWrapper();
|
||||
}
|
||||
|
||||
public static QueryWrapper select(QueryColumn... queryColumns) {
|
||||
return newWrapper().select(queryColumns);
|
||||
}
|
||||
|
||||
public static QueryWrapper selectOne() {
|
||||
return select(new StringQueryColumn("1"));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 排序字段
|
||||
*/
|
||||
public class QueryOrderBy implements Serializable {
|
||||
|
||||
private QueryColumn queryColumn;
|
||||
|
||||
private String orderType = "ASC"; //asc desc
|
||||
|
||||
private boolean nullsFirst = false;
|
||||
private boolean nullsLast = false;
|
||||
|
||||
protected QueryOrderBy() {
|
||||
}
|
||||
|
||||
public QueryOrderBy(QueryColumn queryColumn, String orderType) {
|
||||
this.queryColumn = queryColumn;
|
||||
this.orderType = orderType;
|
||||
}
|
||||
|
||||
|
||||
public QueryOrderBy(QueryColumn queryColumn) {
|
||||
this.queryColumn = queryColumn;
|
||||
}
|
||||
|
||||
|
||||
public QueryOrderBy nullsFirst() {
|
||||
this.nullsFirst = true;
|
||||
this.nullsLast = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public QueryOrderBy nullsLast() {
|
||||
this.nullsFirst = false;
|
||||
this.nullsLast = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public String toSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
String sql = queryColumn.toConditionSql(queryTables, dialect) + " " + orderType;
|
||||
if (nullsFirst) {
|
||||
sql = sql + " NULLS FIRST";
|
||||
} else if (nullsLast) {
|
||||
sql = sql + " NULLS LAST";
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 查询列,描述的是一张表的字段
|
||||
*/
|
||||
public class QueryTable implements Serializable {
|
||||
|
||||
protected String name;
|
||||
protected String alias;
|
||||
|
||||
public QueryTable() {
|
||||
}
|
||||
|
||||
public QueryTable(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public QueryTable(String table, String alias) {
|
||||
this.name = table;
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public QueryTable as(String alias) {
|
||||
this.alias = alias;
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean isSameTable(QueryTable table) {
|
||||
return table != null && Objects.equals(name, table.name);
|
||||
}
|
||||
|
||||
|
||||
public String toSql(IDialect dialect) {
|
||||
return dialect.wrap(name) + WrapperUtil.buildAsAlias(dialect.wrap(alias));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "QueryTable{" +
|
||||
"name='" + name + '\'' +
|
||||
", alias='" + alias + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,315 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
import com.mybatisflex.core.exception.FlexExceptions;
|
||||
import com.mybatisflex.core.table.TableDef;
|
||||
import com.mybatisflex.core.util.ArrayUtil;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
public class QueryWrapper extends BaseQueryWrapper<QueryWrapper> {
|
||||
|
||||
|
||||
public static QueryWrapper create() {
|
||||
return new QueryWrapper();
|
||||
}
|
||||
|
||||
public QueryWrapper select(QueryColumn... queryColumns) {
|
||||
for (QueryColumn column : queryColumns) {
|
||||
if (column != null) {
|
||||
addSelectColumn(column);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper from(TableDef tableDef) {
|
||||
return from(tableDef.getTableName());
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper from(String table) {
|
||||
return from(new QueryTable(table));
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper from(QueryTable table) {
|
||||
if (CollectionUtil.isEmpty(queryTables)) {
|
||||
queryTables = new ArrayList<>();
|
||||
queryTables.add(table);
|
||||
} else {
|
||||
boolean contains = false;
|
||||
for (QueryTable queryTable : queryTables) {
|
||||
if (queryTable.isSameTable(table)) {
|
||||
contains = true;
|
||||
}
|
||||
}
|
||||
if (!contains) {
|
||||
queryTables.add(table);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper from(QueryWrapper queryWrapper) {
|
||||
return from(new SelectQueryTable(queryWrapper));
|
||||
}
|
||||
|
||||
public QueryWrapper as(String alias) {
|
||||
if (CollectionUtil.isEmpty(queryTables)) {
|
||||
throw new IllegalArgumentException("query table must not be empty.");
|
||||
}
|
||||
if (queryTables.size() > 1) {
|
||||
throw FlexExceptions.wrap("QueryWrapper.as(...) only support 1 table");
|
||||
}
|
||||
queryTables.get(0).alias = alias;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper where(QueryCondition queryCondition) {
|
||||
this.setWhereQueryCondition(queryCondition);
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapper where(String sql) {
|
||||
this.setWhereQueryCondition(new StringQueryCondition(sql));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper where(String sql, Object... params) {
|
||||
this.setWhereQueryCondition(new StringQueryCondition(sql, params));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper where(Map<String, Object> whereConditions) {
|
||||
if (whereConditions != null) {
|
||||
whereConditions.forEach((s, o) -> and(QueryCondition.create(new QueryColumn(s), o)));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper and(QueryCondition queryCondition) {
|
||||
return addWhereQueryCondition(queryCondition, SqlConnector.AND);
|
||||
}
|
||||
|
||||
public QueryWrapper and(String sql) {
|
||||
this.addWhereQueryCondition(new StringQueryCondition(sql), SqlConnector.AND);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper and(String sql, Object... params) {
|
||||
this.addWhereQueryCondition(new StringQueryCondition(sql, params), SqlConnector.AND);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper or(QueryCondition queryCondition) {
|
||||
return addWhereQueryCondition(queryCondition, SqlConnector.OR);
|
||||
}
|
||||
|
||||
|
||||
public Joiner<QueryWrapper> leftJoin(String table) {
|
||||
return joining(Join.TYPE_LEFT, table, true);
|
||||
}
|
||||
|
||||
|
||||
public Joiner<QueryWrapper> leftJoinIf(String table, boolean condition) {
|
||||
return joining(Join.TYPE_LEFT, table, condition);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> leftJoin(TableDef table) {
|
||||
return joining(Join.TYPE_LEFT, table.getTableName(), true);
|
||||
}
|
||||
|
||||
|
||||
public Joiner<QueryWrapper> leftJoinIf(TableDef table, boolean condition) {
|
||||
return joining(Join.TYPE_LEFT, table.getTableName(), condition);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> leftJoin(QueryWrapper table) {
|
||||
return joining(Join.TYPE_LEFT, table, true);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> leftJoinIf(QueryWrapper table, boolean condition) {
|
||||
return joining(Join.TYPE_LEFT, table, condition);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> rightJoin(String table) {
|
||||
return joining(Join.TYPE_RIGHT, table, true);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> rightJoinIf(String table, boolean condition) {
|
||||
return joining(Join.TYPE_RIGHT, table, condition);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> rightJoin(QueryWrapper table) {
|
||||
return joining(Join.TYPE_RIGHT, table, true);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> rightJoinIf(QueryWrapper table, boolean condition) {
|
||||
return joining(Join.TYPE_RIGHT, table, condition);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> innerJoin(String table) {
|
||||
return joining(Join.TYPE_INNER, table, true);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> innerJoinIf(String table, boolean condition) {
|
||||
return joining(Join.TYPE_INNER, table, condition);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> innerJoin(QueryWrapper table) {
|
||||
return joining(Join.TYPE_INNER, table, true);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> innerJoinIf(QueryWrapper table, boolean condition) {
|
||||
return joining(Join.TYPE_INNER, table, condition);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> fullJoin(String table) {
|
||||
return joining(Join.TYPE_FULL, table, true);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> fullJoinIf(String table, boolean condition) {
|
||||
return joining(Join.TYPE_FULL, table, condition);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> fullJoin(QueryWrapper table) {
|
||||
return joining(Join.TYPE_FULL, table, true);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> fullJoinIf(QueryWrapper table, boolean condition) {
|
||||
return joining(Join.TYPE_FULL, table, condition);
|
||||
}
|
||||
public Joiner<QueryWrapper> crossJoin(String table) {
|
||||
return joining(Join.TYPE_CROSS, table, true);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> crossJoinIf(String table, boolean condition) {
|
||||
return joining(Join.TYPE_CROSS, table, condition);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> crossJoin(QueryWrapper table) {
|
||||
return joining(Join.TYPE_CROSS, table, true);
|
||||
}
|
||||
|
||||
public Joiner<QueryWrapper> crossJoinIf(QueryWrapper table, boolean condition) {
|
||||
return joining(Join.TYPE_CROSS, table, condition);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected Joiner<QueryWrapper> joining(String type, String table, boolean condition) {
|
||||
Join join = new Join(type, table, condition);
|
||||
addJoinTable(join.getQueryTable());
|
||||
return new Joiner<>(AddJoin(join), join);
|
||||
}
|
||||
|
||||
protected Joiner<QueryWrapper> joining(String type, QueryWrapper queryWrapper, boolean condition) {
|
||||
Join join = new Join(type, queryWrapper, condition);
|
||||
addJoinTable(join.getQueryTable());
|
||||
return new Joiner<>(AddJoin(join), join);
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper groupBy(String name) {
|
||||
addGroupByColumns(new QueryColumn(name));
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapper groupBy(String... names) {
|
||||
for (String name : names) {
|
||||
groupBy(name);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapper groupBy(QueryColumn column) {
|
||||
addGroupByColumns(column);
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapper groupBy(QueryColumn... columns) {
|
||||
for (QueryColumn column : columns) {
|
||||
groupBy(column);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper having(QueryCondition queryCondition) {
|
||||
addHavingQueryCondition(queryCondition, SqlConnector.AND);
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapper orderBy(QueryOrderBy... orderBys) {
|
||||
for (QueryOrderBy queryOrderBy : orderBys) {
|
||||
addOrderBy(queryOrderBy);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapper orderBy(String... orderBys) {
|
||||
for (String queryOrderBy : orderBys) {
|
||||
addOrderBy(new StringQueryOrderBy(queryOrderBy));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public QueryWrapper limit(Integer rows) {
|
||||
setLimitRows(rows);
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapper offset(Integer offset) {
|
||||
setLimitOffset(offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapper limit(Integer offset, Integer rows) {
|
||||
setLimitOffset(offset);
|
||||
setLimitRows(rows);
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapper datasource(String datasource) {
|
||||
setDatasource(datasource);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 queryWrapper 的参数
|
||||
* 在构建 sql 的时候,需要保证 where 在 having 的前面
|
||||
*/
|
||||
Object[] getValueArray() {
|
||||
Object[] whereValues = WrapperUtil.getValues(whereQueryCondition);
|
||||
Object[] havingValues = WrapperUtil.getValues(havingQueryCondition);
|
||||
return ArrayUtil.concat(whereValues, havingValues);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package com.mybatisflex.core.querywrapper;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
/**
|
||||
* 查询的 table,
|
||||
* 实例1:用于构建 select * from (select ...) 中的第二个 select
|
||||
* 实例2:用于构建 left join (select ...) 中的 select
|
||||
*/
|
||||
public class SelectQueryTable extends QueryTable {
|
||||
|
||||
private QueryWrapper queryWrapper;
|
||||
|
||||
public SelectQueryTable(QueryWrapper queryWrapper) {
|
||||
super();
|
||||
this.queryWrapper = queryWrapper;
|
||||
}
|
||||
|
||||
public QueryWrapper getQueryWrapper() {
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
public void setQueryWrapper(QueryWrapper queryWrapper) {
|
||||
this.queryWrapper = queryWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSql(IDialect dialect) {
|
||||
String sql = dialect.buildSelectSql(queryWrapper);
|
||||
if (StringUtil.isNotBlank(alias)) {
|
||||
return "(" + sql + ") AS " + dialect.wrap(alias);
|
||||
} else {
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
public enum SqlConnector {
|
||||
|
||||
|
||||
AND(" AND "),
|
||||
// AND_NOT(" AND NOT "),
|
||||
// AND_EXISTS(" AND EXISTS "),
|
||||
// AND_NOT_EXISTS(" AND NOT EXISTS "),
|
||||
OR(" OR "),
|
||||
// OR_NOT(" OR NOT "),
|
||||
// OR_EXISTS(" OR EXISTS "),
|
||||
// OR_NOT_EXISTS(" OR NOT EXISTS "),
|
||||
// NOT(" NOT "),
|
||||
;
|
||||
|
||||
|
||||
private String value;
|
||||
|
||||
SqlConnector(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -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.querywrapper;
|
||||
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 自定义字符串列,用于扩展
|
||||
*/
|
||||
public class StringQueryColumn extends QueryColumn {
|
||||
|
||||
protected String content;
|
||||
|
||||
|
||||
public StringQueryColumn(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
String toConditionSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StringQueryColumn{" +
|
||||
"content='" + content + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 自定义字符串列,用于扩展
|
||||
*/
|
||||
public class StringQueryCondition extends QueryCondition {
|
||||
|
||||
protected String sqlContent;
|
||||
|
||||
|
||||
public StringQueryCondition(String content) {
|
||||
this.sqlContent = content;
|
||||
}
|
||||
|
||||
public StringQueryCondition(String content, Object... paras) {
|
||||
this.sqlContent = content;
|
||||
this.setValue(paras);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
|
||||
//检测是否生效
|
||||
if (checkEffective()) {
|
||||
QueryCondition effectiveBefore = getEffectiveBefore();
|
||||
if (effectiveBefore != null) {
|
||||
sql.append(effectiveBefore.connector);
|
||||
}
|
||||
sql.append(" ").append(sqlContent).append(" ");
|
||||
}
|
||||
|
||||
if (this.next != null) {
|
||||
return sql + next.toSql(queryTables, dialect);
|
||||
}
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 排序字段
|
||||
*/
|
||||
public class StringQueryOrderBy extends QueryOrderBy {
|
||||
|
||||
private String orderBy;
|
||||
|
||||
public StringQueryOrderBy(String orderBy) {
|
||||
this.orderBy = orderBy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
return orderBy;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
/**
|
||||
* 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.querywrapper;
|
||||
|
||||
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
class WrapperUtil {
|
||||
|
||||
|
||||
static String buildAsAlias(String alias) {
|
||||
return StringUtil.isBlank(alias) ? "" : " AS " + alias;
|
||||
}
|
||||
|
||||
static final Object[] NULL_PARA_ARRAY = new Object[0];
|
||||
|
||||
static Object[] getValues(QueryCondition condition) {
|
||||
if (condition == null) {
|
||||
return NULL_PARA_ARRAY;
|
||||
}
|
||||
|
||||
List<Object> paras = new LinkedList<>();
|
||||
getValues(condition, paras);
|
||||
|
||||
return paras.isEmpty() ? NULL_PARA_ARRAY : paras.toArray();
|
||||
}
|
||||
|
||||
|
||||
private static void getValues(QueryCondition condition, List<Object> paras) {
|
||||
if (condition == null) {
|
||||
return;
|
||||
}
|
||||
Object value = condition.getValue();
|
||||
if (value != null) {
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] values = (Object[]) value;
|
||||
for (Object v : values) {
|
||||
if (v.getClass() == int[].class) {
|
||||
addAll(paras, (int[]) v);
|
||||
} else if (v.getClass() == long[].class) {
|
||||
addAll(paras, (long[]) v);
|
||||
} else if (v.getClass() == short[].class) {
|
||||
addAll(paras, (short[]) v);
|
||||
} else {
|
||||
paras.add(v);
|
||||
}
|
||||
}
|
||||
} else if (value instanceof QueryWrapper) {
|
||||
Object[] valueArray = ((QueryWrapper) value).getValueArray();
|
||||
paras.addAll(Arrays.asList(valueArray));
|
||||
} else {
|
||||
paras.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
getValues(condition.next, paras);
|
||||
}
|
||||
|
||||
|
||||
private static void addAll(List<Object> paras, int[] ints) {
|
||||
for (int i : ints) {
|
||||
paras.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addAll(List<Object> paras, long[] longs) {
|
||||
for (long i : longs) {
|
||||
paras.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void addAll(List<Object> paras, short[] shorts) {
|
||||
for (short i : shorts) {
|
||||
paras.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getRealTableName(List<QueryTable> queryTables, QueryTable queryTable) {
|
||||
QueryTable realTable = getRealTable(queryTables, queryTable);
|
||||
if (realTable == null) {
|
||||
return "";
|
||||
}
|
||||
return StringUtil.isNotBlank(realTable.alias) ? realTable.alias : realTable.name;
|
||||
}
|
||||
|
||||
public static QueryTable getRealTable(List<QueryTable> queryTables, QueryTable queryTable) {
|
||||
if (CollectionUtil.isEmpty(queryTables)) {
|
||||
return queryTable;
|
||||
}
|
||||
|
||||
if (queryTable == null && queryTables.size() == 1) {
|
||||
return queryTables.get(0);
|
||||
}
|
||||
|
||||
for (QueryTable table : queryTables) {
|
||||
if (table.isSameTable(queryTable)) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
return queryTable;
|
||||
}
|
||||
|
||||
}
|
||||
175
mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Db.java
Normal file
175
mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Db.java
Normal file
@ -0,0 +1,175 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import com.mybatisflex.core.FlexGlobalConfig;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.querywrapper.QueryWrapper;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.apache.ibatis.util.MapUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 针对 RowMapper 的静态方法进行封装
|
||||
*/
|
||||
public class Db {
|
||||
|
||||
private static final Map<String, RowMapperInvoker> INVOKER_MAP = new ConcurrentHashMap<>();
|
||||
static RowMapperInvoker defaultRowMapperInvoker;
|
||||
|
||||
public static RowMapperInvoker invoker() {
|
||||
if (defaultRowMapperInvoker == null) {
|
||||
SqlSessionFactory sqlSessionFactory = FlexGlobalConfig.getDefaultConfig().getSqlSessionFactory();
|
||||
defaultRowMapperInvoker = new RowMapperInvoker(sqlSessionFactory);
|
||||
}
|
||||
return defaultRowMapperInvoker;
|
||||
}
|
||||
|
||||
public static RowMapperInvoker invoker(String environmentId) {
|
||||
return MapUtil.computeIfAbsent(INVOKER_MAP, environmentId, key -> {
|
||||
SqlSessionFactory sqlSessionFactory = FlexGlobalConfig.getConfig(key).getSqlSessionFactory();
|
||||
return new RowMapperInvoker(sqlSessionFactory);
|
||||
});
|
||||
}
|
||||
|
||||
public static int insertBySql(String sql, Object... args) {
|
||||
return invoker().insertBySql(sql, args);
|
||||
}
|
||||
|
||||
public static int insertRow(String tableName, Row row) {
|
||||
return invoker().insertRow(tableName, row);
|
||||
}
|
||||
|
||||
public static int[] insertBatch(String tableName, Collection<Row> rows) {
|
||||
return insertBatch(tableName, rows, rows.size());
|
||||
}
|
||||
|
||||
public static int[] insertBatch(String tableName, Collection<Row> rows, int batchSize) {
|
||||
return invoker().insertBatch(tableName, rows, batchSize);
|
||||
}
|
||||
|
||||
public static int insertBatchWithFirstRowColumns(String tableName, List<Row> rows) {
|
||||
return invoker().insertBatchWithFirstRowColumns(tableName, rows);
|
||||
}
|
||||
|
||||
public static int deleteBySql(String sql, Object... args) {
|
||||
return invoker().deleteBySql(sql, args);
|
||||
}
|
||||
|
||||
public static int deleteById(String tableName, Row row) {
|
||||
return invoker().deleteById(tableName, row);
|
||||
}
|
||||
|
||||
public static int deleteById(String tableName, String primaryKey, Object id) {
|
||||
return invoker().deleteById(tableName, primaryKey, id);
|
||||
}
|
||||
|
||||
public static int deleteBatchByIds(String tableName, String primaryKey, Collection<?> ids) {
|
||||
return invoker().deleteBatchByIds(tableName, primaryKey, ids);
|
||||
}
|
||||
|
||||
public static int deleteByByMap(String tableName, Map<String, Object> whereColumns) {
|
||||
return invoker().deleteByByMap(tableName, whereColumns);
|
||||
}
|
||||
|
||||
public static int deleteByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||
return invoker().deleteByQuery(tableName, queryWrapper);
|
||||
}
|
||||
|
||||
public static int updateBySql(String sql, Object... args) {
|
||||
return invoker().updateBySql(sql, args);
|
||||
}
|
||||
|
||||
public static int updateById(String tableName, Row row) {
|
||||
return invoker().updateById(tableName, row);
|
||||
}
|
||||
|
||||
public static int updateByMap(String tableName, Row data, Map<String, Object> whereColumns) {
|
||||
return invoker().updateByMap(tableName, data, whereColumns);
|
||||
}
|
||||
|
||||
public static int updateByQuery(String tableName, Row data, QueryWrapper queryWrapper) {
|
||||
return invoker().updateByQuery(tableName, data, queryWrapper);
|
||||
}
|
||||
|
||||
public static int updateBatchById(String tableName, List<Row> rows) {
|
||||
return invoker().updateBatchById(tableName, rows);
|
||||
}
|
||||
|
||||
public static Row selectOneBySql(String sql, Object... args) {
|
||||
return invoker().selectOneBySql(sql, args);
|
||||
}
|
||||
|
||||
public static Row selectOneById(String tableName, Row row) {
|
||||
return invoker().selectOneById(tableName, row);
|
||||
}
|
||||
|
||||
public static Row selectOneById(String tableName, String primaryKey, Object id) {
|
||||
return invoker().selectOneById(tableName, primaryKey, id);
|
||||
}
|
||||
|
||||
public static Row selectOneByMap(String tableName, Map whereColumns) {
|
||||
return invoker().selectOneByMap(tableName, whereColumns);
|
||||
}
|
||||
|
||||
public static Row selectOneByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||
return invoker().selectOneByQuery(tableName, queryWrapper);
|
||||
}
|
||||
|
||||
public static List<Row> selectListBySql(String sql, Object... args) {
|
||||
return invoker().selectListBySql(sql, args);
|
||||
}
|
||||
|
||||
public static List<Row> selectListByMap(String tableName, Map<String, Object> whereColumns) {
|
||||
return invoker().selectListByMap(tableName, whereColumns);
|
||||
}
|
||||
|
||||
public static List<Row> selectListByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||
return invoker().selectListByQuery(tableName, queryWrapper);
|
||||
}
|
||||
|
||||
public static List<Row> selectAll(String tableName) {
|
||||
return invoker().selectAll(tableName);
|
||||
}
|
||||
|
||||
public static Object selectObject(String sql, Object... args) {
|
||||
return invoker().selectObject(sql, args);
|
||||
}
|
||||
|
||||
public static List<Object> selectObjectList(String sql, Object... args) {
|
||||
return invoker().selectObjectList(sql, args);
|
||||
}
|
||||
|
||||
public static long selectCount(String sql, Object... args) {
|
||||
return invoker().selectCount(sql, args);
|
||||
}
|
||||
|
||||
public static long selectCountByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||
return invoker().selectCountByQuery(tableName, queryWrapper);
|
||||
}
|
||||
|
||||
public static Page<Row> paginate(String tableName, int pageNumber, int pageSize, QueryWrapper queryWrapper) {
|
||||
return invoker().paginate(tableName, pageNumber, pageSize, queryWrapper);
|
||||
}
|
||||
|
||||
public static Page<Row> paginate(String tableName, Page<Row> page, QueryWrapper queryWrapper) {
|
||||
return invoker().paginate(tableName, page, queryWrapper);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,182 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import com.mybatisflex.core.javassist.ModifyAttrsRecord;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
import com.mybatisflex.core.table.TableInfos;
|
||||
import com.mybatisflex.core.util.ArrayUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
|
||||
public class Row extends HashMap<String, Object> implements ModifyAttrsRecord {
|
||||
private static final Object[] NULL_ARGS = new Object[0];
|
||||
|
||||
//主键,多个主键用英文逗号隔开
|
||||
private RowKey[] primaryKeys;
|
||||
|
||||
public static Row of(String key, Object value) {
|
||||
Row row = new Row();
|
||||
return row.set(key, value);
|
||||
}
|
||||
|
||||
|
||||
public static Row ofKey(String primaryKey, Object value) {
|
||||
Row row = new Row();
|
||||
String[] primaryKeyStrings = primaryKey.split(",");
|
||||
row.primaryKeys = new RowKey[primaryKeyStrings.length];
|
||||
|
||||
for (int i = 0; i < primaryKeyStrings.length; i++) {
|
||||
row.primaryKeys[i] = RowKey.of(primaryKeyStrings[i].trim());
|
||||
}
|
||||
|
||||
if (primaryKeyStrings.length > 0 && !value.getClass().isArray()) {
|
||||
throw new IllegalArgumentException("the type of value[\"" + value + "\"] must be an array.");
|
||||
}
|
||||
|
||||
if (primaryKeyStrings.length == 1) {
|
||||
row.put(primaryKey.trim(), value);
|
||||
} else {
|
||||
Object[] values = (Object[]) value;
|
||||
for (int i = 0; i < primaryKeyStrings.length; i++) {
|
||||
row.put(primaryKeyStrings[i].trim(), values[i]);
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
public static Row ofKey(RowKey ...rowKeys) {
|
||||
Row row = new Row();
|
||||
row.primaryKeys = rowKeys;
|
||||
return row;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Row ofKey(RowKey rowKey, Object value) {
|
||||
Row row = new Row();
|
||||
row.primaryKeys = new RowKey[]{rowKey};
|
||||
row.put(rowKey.keyColumn, value);
|
||||
return row;
|
||||
}
|
||||
|
||||
|
||||
public static Row ofKey(RowKey[] rowKeys, Object[] value) {
|
||||
Row row = new Row();
|
||||
row.primaryKeys = rowKeys;
|
||||
for (int i = 0; i < rowKeys.length; i++) {
|
||||
row.put(rowKeys[i].keyColumn, value[i]);
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
|
||||
public Row set(String key, Object value) {
|
||||
put(key, value);
|
||||
boolean isPrimaryKey = false;
|
||||
if (this.primaryKeys != null){
|
||||
for (RowKey rowKey : primaryKeys) {
|
||||
if (rowKey.getKeyColumn().equals(key)){
|
||||
isPrimaryKey = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isPrimaryKey){
|
||||
addModifyAttr(key);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Object get(Object key, Object defaultValue) {
|
||||
Object result = super.get(key);
|
||||
return result != null ? result : defaultValue;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object remove(Object key) {
|
||||
removeModifyAttr(key.toString());
|
||||
return super.remove(key);
|
||||
}
|
||||
|
||||
|
||||
public <T> T toEntity(Class<T> entityClass) {
|
||||
TableInfo tableInfo = TableInfos.ofEntityClass(entityClass);
|
||||
return tableInfo.newInstanceByRow(this);
|
||||
}
|
||||
|
||||
|
||||
public void keep(Set<String> attrs) {
|
||||
if (attrs == null) {
|
||||
throw new NullPointerException("attrs is null.");
|
||||
}
|
||||
|
||||
clearModifyFlag();
|
||||
modifyAttrs.addAll(attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取修改的值,值需要保持顺序
|
||||
* 返回的内容不包含主键的值
|
||||
*
|
||||
* @return values 数组
|
||||
*/
|
||||
public Object[] obtainModifyValues() {
|
||||
Object[] values = new Object[modifyAttrs.size()];
|
||||
int index = 0;
|
||||
for (String modifyAttr : modifyAttrs) {
|
||||
values[index++] = get(modifyAttr);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
public String[] obtainsPrimaryKeyStrings() {
|
||||
String[] returnKeys = new String[primaryKeys.length];
|
||||
for (int i = 0; i < primaryKeys.length; i++) {
|
||||
returnKeys[i] = primaryKeys[i].keyColumn;
|
||||
}
|
||||
return returnKeys;
|
||||
}
|
||||
|
||||
|
||||
public RowKey[] obtainsPrimaryKeys() {
|
||||
return this.primaryKeys;
|
||||
}
|
||||
|
||||
|
||||
public Object[] obtainsPrimaryValues() {
|
||||
if (ArrayUtil.isEmpty(primaryKeys)) {
|
||||
return NULL_ARGS;
|
||||
}
|
||||
Object[] values = new Object[primaryKeys.length];
|
||||
for (int i = 0; i < primaryKeys.length; i++) {
|
||||
values[i] = get(primaryKeys[i].keyColumn);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
public Object[] obtainModifyValuesAndPrimaryValues() {
|
||||
return ArrayUtil.concat(obtainModifyValues(), obtainsPrimaryValues());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,149 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import com.mybatisflex.core.enums.KeyType;
|
||||
|
||||
/**
|
||||
* row 的主键策略
|
||||
*/
|
||||
public class RowKey {
|
||||
|
||||
/**
|
||||
* 自增 ID
|
||||
*/
|
||||
public static final RowKey ID_AUTO = new UnModifiableRowKey("id",KeyType.Auto,null,false);
|
||||
|
||||
/**
|
||||
* UUID 的 ID
|
||||
*/
|
||||
public static final RowKey ID_UUID = new UnModifiableRowKey("id",KeyType.Generator,"uuid",true);
|
||||
|
||||
|
||||
public static RowKey of(String keyColumn) {
|
||||
RowKey rowKey = new RowKey();
|
||||
rowKey.keyColumn = keyColumn;
|
||||
return rowKey;
|
||||
}
|
||||
|
||||
public static RowKey of(String keyColumn, KeyType keyType) {
|
||||
RowKey rowKey = new RowKey();
|
||||
rowKey.keyColumn = keyColumn;
|
||||
rowKey.keyType = keyType;
|
||||
return rowKey;
|
||||
}
|
||||
|
||||
public static RowKey of(String keyColumn, KeyType keyType, String keyTypeValue) {
|
||||
RowKey rowKey = new RowKey();
|
||||
rowKey.keyColumn = keyColumn;
|
||||
rowKey.keyType = keyType;
|
||||
rowKey.value = keyTypeValue;
|
||||
return rowKey;
|
||||
}
|
||||
|
||||
public static RowKey of(String keyColumn, KeyType keyType, String keyTypeValue, boolean before) {
|
||||
RowKey rowKey = new RowKey();
|
||||
rowKey.keyColumn = keyColumn;
|
||||
rowKey.keyType = keyType;
|
||||
rowKey.value = keyTypeValue;
|
||||
rowKey.before = before;
|
||||
return rowKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主键字段
|
||||
*/
|
||||
protected String keyColumn;
|
||||
|
||||
/**
|
||||
* 主键类型
|
||||
*/
|
||||
protected KeyType keyType = KeyType.Auto;
|
||||
|
||||
/**
|
||||
* 主键类型为 Sequence 和 Generator 时的对应的内容
|
||||
*/
|
||||
protected String value;
|
||||
|
||||
/**
|
||||
* 是否前执行
|
||||
*/
|
||||
protected boolean before = true;
|
||||
|
||||
|
||||
public String getKeyColumn() {
|
||||
return keyColumn;
|
||||
}
|
||||
|
||||
public void setKeyColumn(String keyColumn) {
|
||||
this.keyColumn = keyColumn;
|
||||
}
|
||||
|
||||
public KeyType getKeyType() {
|
||||
return keyType;
|
||||
}
|
||||
|
||||
public void setKeyType(KeyType keyType) {
|
||||
this.keyType = keyType;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean isBefore() {
|
||||
return before;
|
||||
}
|
||||
|
||||
public void setBefore(boolean before) {
|
||||
this.before = before;
|
||||
}
|
||||
|
||||
static class UnModifiableRowKey extends RowKey{
|
||||
|
||||
public UnModifiableRowKey(String keyColumn, KeyType keyType, String value, boolean before) {
|
||||
super();
|
||||
this.keyColumn = keyColumn;
|
||||
this.keyType = keyType;
|
||||
this.value = value;
|
||||
this.before = before;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeyColumn(String keyColumn) {
|
||||
throw new UnsupportedOperationException("unsupported setKeyColumn!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeyType(KeyType keyType) {
|
||||
throw new UnsupportedOperationException("unsupported setKeyType!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
throw new UnsupportedOperationException("unsupported setValue!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBefore(boolean before) {
|
||||
throw new UnsupportedOperationException("unsupported setBefore!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,446 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.exception.FlexExceptions;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.provider.RowSqlProvider;
|
||||
import com.mybatisflex.core.querywrapper.CPI;
|
||||
import com.mybatisflex.core.querywrapper.QueryColumn;
|
||||
import com.mybatisflex.core.querywrapper.QueryWrapper;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
import org.apache.ibatis.exceptions.TooManyResultsException;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public interface RowMapper {
|
||||
|
||||
//////insert //////
|
||||
|
||||
/**
|
||||
* 执行 insert sql 语句
|
||||
*
|
||||
* @param sql insert sql 语句
|
||||
* @param args 参数
|
||||
* @return 执行影响的行数
|
||||
* @see Db#insertBySql(String, Object...)
|
||||
*/
|
||||
@InsertProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
|
||||
int insertBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
|
||||
|
||||
|
||||
/**
|
||||
* 插入 row 到数据表
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param row 数据内容,当设置有主键时,主键会自动填充
|
||||
* @return 执行影响的行数
|
||||
* @see RowSqlProvider#insertRow(Map)
|
||||
*/
|
||||
@InsertProvider(value = RowSqlProvider.class, method = "insertRow")
|
||||
int insertRow(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROW) Row row);
|
||||
|
||||
|
||||
/**
|
||||
* 批量插入 rows 到数据表
|
||||
* <p>
|
||||
* 注意,批量插入中,只会根据第一条 row 数据来构建 Sql 插入字段,若每条数据字段不一致,可能造成个别字段无法插入的情况
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param rows 数据内容,当设置有主键时,主键会自动填充
|
||||
* @return 执行影响的行数
|
||||
* @see RowSqlProvider#insertBatchWithFirstRowColumns(Map)
|
||||
*/
|
||||
@InsertProvider(value = RowSqlProvider.class, method = "insertBatchWithFirstRowColumns")
|
||||
int insertBatchWithFirstRowColumns(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROWS) List<Row> rows);
|
||||
|
||||
|
||||
/////// delete /////
|
||||
|
||||
/**
|
||||
* 执行 delete sql 语言
|
||||
*
|
||||
* @param sql delete sql 语句
|
||||
* @param args 参数
|
||||
* @return 执行影响的行数
|
||||
*/
|
||||
@DeleteProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
|
||||
int deleteBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
|
||||
|
||||
/**
|
||||
* 根据 id 删除数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param row id 和 值的数据,可以通过 {@link Row#ofKey(String, Object)} 来创建
|
||||
* @return 执行影响的行数
|
||||
*/
|
||||
|
||||
default int deleteById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROW) Row row) {
|
||||
return deleteById(tableName, StringUtil.join(",", row.obtainsPrimaryKeyStrings()), row.obtainsPrimaryValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 id 删除数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param primaryKey 主键,多个主键用英文逗号隔开
|
||||
* @param id 数据,多个主键时传入数组,例如 new Object[]{1,2}
|
||||
* @return 执行影响的行数
|
||||
* @see RowSqlProvider#deleteById(Map)
|
||||
*/
|
||||
@DeleteProvider(value = RowSqlProvider.class, method = "deleteById")
|
||||
int deleteById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.PRIMARY_KEY) String primaryKey, @Param(FlexConsts.PRIMARY_VALUE) Object id);
|
||||
|
||||
|
||||
/**
|
||||
* 根据 多个 id 值删除多条数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param primaryKey 主键
|
||||
* @param ids id 的集合
|
||||
* @return 执行影响的行数
|
||||
* @see RowSqlProvider#deleteBatchByIds(Map)
|
||||
*/
|
||||
@DeleteProvider(value = RowSqlProvider.class, method = "deleteBatchByIds")
|
||||
int deleteBatchByIds(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.PRIMARY_KEY) String primaryKey, @Param(FlexConsts.PRIMARY_VALUE) Collection<?> ids);
|
||||
|
||||
/**
|
||||
* 根据 map 来构建条件删除数据
|
||||
* <p>
|
||||
* <b>注意:</b>
|
||||
* 删除 map 不允许为 null 或者 空内容,否则可能造成数据全部删除的情况
|
||||
* 若想删除全部数据,请执行 {@link RowMapper#deleteBySql(String, Object...)} 方法
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param whereConditions 条件,通过 map 的 key:value 来构建,都是 and 的关系
|
||||
* @return 执行影响的行数
|
||||
*/
|
||||
default int deleteByByMap(String tableName, Map<String, Object> whereConditions) {
|
||||
if (whereConditions == null || whereConditions.isEmpty()) {
|
||||
throw FlexExceptions.wrap("whereConditions can not be null or empty.");
|
||||
}
|
||||
return deleteByQuery(tableName, new QueryWrapper().where(whereConditions));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据 queryWrapper 构建 where 条件来删除数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param queryWrapper queryWrapper
|
||||
* @return 执行影响的行数
|
||||
* @see RowSqlProvider#deleteByQuery(Map)
|
||||
*/
|
||||
@DeleteProvider(value = RowSqlProvider.class, method = "deleteByQuery")
|
||||
int deleteByQuery(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
////////update ////
|
||||
|
||||
/**
|
||||
* 执行 update sql 语句
|
||||
*
|
||||
* @param sql sql 语句
|
||||
* @param args 参数内容
|
||||
* @return 执行影响的行数
|
||||
*/
|
||||
@UpdateProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
|
||||
int updateBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
|
||||
|
||||
|
||||
/**
|
||||
* 根据主键来更新数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param row 数据,其必须包含主键数据列名和值
|
||||
* @return 执行影响的行数
|
||||
* @see RowSqlProvider#updateById(Map)
|
||||
*/
|
||||
@UpdateProvider(value = RowSqlProvider.class, method = "updateById")
|
||||
int updateById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROW) Row row);
|
||||
|
||||
|
||||
/**
|
||||
* 根据 map 来更新数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param data 要更新的数据
|
||||
* @param whereConditions 条件,通过 map 的 key:value 来构建,都是 and 的关系
|
||||
* @return 执行影响的行数
|
||||
*/
|
||||
default int updateByMap(String tableName, Row data, Map<String, Object> whereConditions) {
|
||||
return updateByQuery(tableName, data, new QueryWrapper().where(whereConditions));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据 queryWrapper 来构建 where 条件更新数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param data 更新数据
|
||||
* @param queryWrapper queryWrapper
|
||||
* @return 执行影响的行数
|
||||
* @see RowSqlProvider#updateByQuery(Map)
|
||||
*/
|
||||
@UpdateProvider(value = RowSqlProvider.class, method = "updateByQuery")
|
||||
int updateByQuery(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROW) Row data, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
/**
|
||||
* 根据主键来批量更新数据
|
||||
* 注意:
|
||||
* 1、此方法需要在 mysql 等链接配置需要开启 allowMultiQueries=true
|
||||
* 2、更新成功返回的结果也可能为 0
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param rows 数据,其必须包含主键数据列名和值
|
||||
* @return 执行影响的行数
|
||||
* @see RowSqlProvider#updateBatchById(Map)
|
||||
*/
|
||||
@UpdateProvider(value = RowSqlProvider.class, method = "updateBatchById")
|
||||
int updateBatchById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.ROWS) List<Row> rows);
|
||||
|
||||
///////select /////
|
||||
|
||||
/**
|
||||
* 通过原生 SQL 查询 1 条数据,要求数据必须返回 1 条内容,否则会报错
|
||||
*
|
||||
* @param sql select sql 语句
|
||||
* @param args 参数
|
||||
* @return 返回一条数据
|
||||
*/
|
||||
default Row selectOneBySql(String sql, Object... args) {
|
||||
List<Row> rows = selectListBySql(sql, args);
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return null;
|
||||
} else if (rows.size() == 1) {
|
||||
return rows.get(0);
|
||||
} else {
|
||||
/** 当返回多条数据时,抛出异常, 保持和 Mybatis DefaultSqlSession 的统一逻辑,
|
||||
* see: {@link org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(String, Object)} **/
|
||||
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOneBySql(), but found: " + rows.size());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过主键来查询数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param row 主键和ID的描述,通过 {@link Row#ofKey(String, Object)} 来进行构建
|
||||
* @return 返回一条数据,或者 null
|
||||
*/
|
||||
default Row selectOneById(String tableName, Row row) {
|
||||
return selectOneById(tableName, StringUtil.join(",", row.obtainsPrimaryKeyStrings()), row.obtainsPrimaryValues());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据主键来查询数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param primaryKey 主键
|
||||
* @param id id 值
|
||||
* @return row or null
|
||||
* @see RowSqlProvider#selectOneById(Map)
|
||||
*/
|
||||
@SelectProvider(value = RowSqlProvider.class, method = "selectOneById")
|
||||
Row selectOneById(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.PRIMARY_KEY) String primaryKey, @Param(FlexConsts.PRIMARY_VALUE) Object id);
|
||||
|
||||
/**
|
||||
* 根据 map 组成的条件查询 1 条数据
|
||||
*
|
||||
* @param tableName
|
||||
* @param whereConditions
|
||||
* @return
|
||||
*/
|
||||
default Row selectOneByMap(String tableName, Map whereConditions) {
|
||||
return selectOneByQuery(tableName, new QueryWrapper().where(whereConditions));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据 queryWrapper 来查询 1 条数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param queryWrapper queryWrapper
|
||||
* @return row or null
|
||||
*/
|
||||
default Row selectOneByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||
List<Row> rows = selectListByQuery(tableName, queryWrapper.limit(1));
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return rows.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过自定义 sql 来查询一个 Row 列表
|
||||
*
|
||||
* @param sql 自定义的 sql
|
||||
* @param args sql 参数
|
||||
* @return row 列表
|
||||
*/
|
||||
@SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
|
||||
List<Row> selectListBySql(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
|
||||
|
||||
|
||||
/**
|
||||
* 根据 map 来查询一个 Row 列表
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param whereConditions 条件
|
||||
* @return row 列表
|
||||
*/
|
||||
default List<Row> selectListByMap(String tableName, Map<String, Object> whereConditions) {
|
||||
return selectListByQuery(tableName, new QueryWrapper().where(whereConditions));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据 queryWrapper 来查询一个 row 列表
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param queryWrapper queryWrapper
|
||||
* @return row 列表
|
||||
* @see RowSqlProvider#selectListByQuery(Map)
|
||||
*/
|
||||
@SelectProvider(value = RowSqlProvider.class, method = "selectListByQuery")
|
||||
List<Row> selectListByQuery(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
/**
|
||||
* 查询某张表的全部数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return row 列表
|
||||
*/
|
||||
default List<Row> selectAll(@Param(FlexConsts.TABLE_NAME) String tableName) {
|
||||
return selectListByMap(tableName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 sql 查询某一个数据,sql 执行的结果应该只有 1 行 1 列
|
||||
* 若返回有多列,则只取第一列的值,若有多行,则会出现 TooManyResultsException 错误
|
||||
*
|
||||
* @param sql sql
|
||||
* @param args sql 参数
|
||||
* @return object
|
||||
*/
|
||||
@SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
|
||||
Object selectObject(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
|
||||
|
||||
|
||||
/**
|
||||
* 通过 sql 查询多行数据,sql 执行的结果应该只有 1 列
|
||||
*
|
||||
* @param sql sql 语句
|
||||
* @param args sql 参数
|
||||
* @return object list
|
||||
*/
|
||||
@SelectProvider(value = RowSqlProvider.class, method = RowSqlProvider.METHOD_RAW_SQL)
|
||||
List<Object> selectObjectList(@Param(FlexConsts.SQL) String sql, @Param(FlexConsts.SQL_ARGS) Object... args);
|
||||
|
||||
|
||||
/**
|
||||
* 查询数据,一般用于 select count(*)... 的语言,也可用于执行的结果只有一个数值的其他 sql
|
||||
*
|
||||
* @param sql sql 语句
|
||||
* @param args sql 参数
|
||||
* @return 返回数据
|
||||
*/
|
||||
default long selectCount(String sql, Object... args) {
|
||||
Object object = selectObject(sql, args);
|
||||
if (object == null) {
|
||||
return 0;
|
||||
} else if (object instanceof Number) {
|
||||
return ((Number) object).longValue();
|
||||
} else {
|
||||
throw FlexExceptions.wrap("selectCount error, Can not get number value for sql: %s", sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 queryWrapper 来查询数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param queryWrapper queryWrapper
|
||||
* @return 数量
|
||||
* @see RowSqlProvider#selectCountByQuery(Map)
|
||||
*/
|
||||
@SelectProvider(value = RowSqlProvider.class, method = "selectCountByQuery")
|
||||
long selectCountByQuery(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
/**
|
||||
* 分页查询某张表的数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param pageNumber 当前页码
|
||||
* @param pageSize 每页的数据量
|
||||
* @param queryWrapper 条件封装
|
||||
* @return 一页数据
|
||||
*/
|
||||
default Page<Row> paginate(String tableName, int pageNumber, int pageSize, QueryWrapper queryWrapper) {
|
||||
Page<Row> page = new Page<>(pageNumber, pageSize);
|
||||
return paginate(tableName, page, queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分页查询数据
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param page page 封装类
|
||||
* @param queryWrapper 条件
|
||||
* @return
|
||||
*/
|
||||
default Page<Row> paginate(String tableName, Page<Row> page, QueryWrapper queryWrapper) {
|
||||
|
||||
|
||||
List<QueryColumn> groupByColumns = CPI.getGroupByColumns(queryWrapper);
|
||||
|
||||
// 只有 totalRow 小于 0 的时候才会去查询总量
|
||||
// 这样方便用户做总数缓存,而非每次都要去查询总量
|
||||
// 一般的分页场景中,只有第一页的时候有必要去查询总量,第二页以后是不需要的
|
||||
if (page.getTotalRow() < 0) {
|
||||
|
||||
//清除group by 去查询数据
|
||||
CPI.setGroupByColumns(queryWrapper, null);
|
||||
long count = selectCountByQuery(tableName, queryWrapper);
|
||||
page.setTotalRow(count);
|
||||
}
|
||||
|
||||
if (page.getTotalRow() == 0 || page.getPageNumber() > page.getTotalPage()) {
|
||||
return page;
|
||||
}
|
||||
|
||||
//恢复数量查询清除的 groupBy
|
||||
CPI.setGroupByColumns(queryWrapper, groupByColumns);
|
||||
int offset = page.getPageSize() * (page.getPageNumber() - 1);
|
||||
queryWrapper.limit(offset, page.getPageSize());
|
||||
List<Row> rows = selectListByQuery(tableName, queryWrapper);
|
||||
page.setList(rows);
|
||||
return page;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,211 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import com.mybatisflex.core.FlexGlobalConfig;
|
||||
import com.mybatisflex.core.dialect.DbType;
|
||||
import com.mybatisflex.core.dialect.DialectFactory;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.querywrapper.QueryWrapper;
|
||||
import org.apache.ibatis.executor.BatchResult;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class RowMapperInvoker {
|
||||
|
||||
private final SqlSessionFactory sqlSessionFactory;
|
||||
private final DbType dbType;
|
||||
private RowSessionManager rowSessionManager = RowSessionManager.DEFAULT;
|
||||
|
||||
public RowMapperInvoker(SqlSessionFactory sqlSessionFactory) {
|
||||
this.sqlSessionFactory = sqlSessionFactory;
|
||||
this.dbType = FlexGlobalConfig.getConfig(sqlSessionFactory.getConfiguration().getEnvironment().getId()).getDbType();
|
||||
}
|
||||
|
||||
public RowSessionManager getRowSessionManager() {
|
||||
return rowSessionManager;
|
||||
}
|
||||
|
||||
public void setRowSessionManager(RowSessionManager rowSessionManager) {
|
||||
this.rowSessionManager = rowSessionManager;
|
||||
}
|
||||
|
||||
private <R> R execute(Function<RowMapper, R> function) {
|
||||
SqlSession sqlSession = rowSessionManager.getSqlSession(sqlSessionFactory);
|
||||
try {
|
||||
DialectFactory.setHintDbType(dbType);
|
||||
RowMapper mapper = sqlSession.getMapper(RowMapper.class);
|
||||
return function.apply(mapper);
|
||||
} finally {
|
||||
DialectFactory.clearHintDbType();
|
||||
rowSessionManager.releaseSqlSession(sqlSession, sqlSessionFactory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int insertBySql(String sql, Object... args) {
|
||||
return execute(mapper -> mapper.insertBySql(sql, args));
|
||||
}
|
||||
|
||||
public int insertRow(String tableName, Row row) {
|
||||
return execute(mapper -> mapper.insertRow(tableName, row));
|
||||
}
|
||||
|
||||
public int[] insertBatch(String tableName, Collection<Row> rows, int batchSize) {
|
||||
int[] results = new int[rows.size()];
|
||||
SqlSession sqlSession = rowSessionManager.getSqlSession(sqlSessionFactory, ExecutorType.BATCH);
|
||||
try {
|
||||
DialectFactory.setHintDbType(dbType);
|
||||
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.insertRow(tableName, row);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
DialectFactory.clearHintDbType();
|
||||
rowSessionManager.releaseSqlSession(sqlSession, sqlSessionFactory);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public int insertBatchWithFirstRowColumns(String tableName, List<Row> rows) {
|
||||
return execute(mapper -> mapper.insertBatchWithFirstRowColumns(tableName, rows));
|
||||
}
|
||||
|
||||
public int deleteBySql(String sql, Object... args) {
|
||||
return execute(mapper -> mapper.deleteBySql(sql, args));
|
||||
}
|
||||
|
||||
public int deleteById(String tableName, Row row) {
|
||||
return execute(mapper -> mapper.deleteById(tableName, row));
|
||||
}
|
||||
|
||||
public int deleteById(String tableName, String primaryKey, Object id) {
|
||||
return execute(mapper -> mapper.deleteById(tableName, primaryKey, id));
|
||||
}
|
||||
|
||||
public int deleteBatchByIds(String tableName, String primaryKey, Collection<?> ids) {
|
||||
return execute(mapper -> mapper.deleteBatchByIds(tableName, primaryKey, ids));
|
||||
}
|
||||
|
||||
public int deleteByByMap(String tableName, Map<String, Object> whereColumns) {
|
||||
return execute(mapper -> mapper.deleteByByMap(tableName, whereColumns));
|
||||
}
|
||||
|
||||
public int deleteByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||
return execute(mapper -> mapper.deleteByQuery(tableName, queryWrapper));
|
||||
}
|
||||
|
||||
public int updateBySql(String sql, Object... args) {
|
||||
return execute(mapper -> mapper.updateBySql(sql, args));
|
||||
}
|
||||
|
||||
public int updateById(String tableName, Row row) {
|
||||
return execute(mapper -> mapper.updateById(tableName, row));
|
||||
}
|
||||
|
||||
public int updateByMap(String tableName, Row data, Map<String, Object> whereColumns) {
|
||||
return execute(mapper -> mapper.updateByMap(tableName, data, whereColumns));
|
||||
}
|
||||
|
||||
public int updateByQuery(String tableName, Row data, QueryWrapper queryWrapper) {
|
||||
return execute(mapper -> mapper.updateByQuery(tableName, data, queryWrapper));
|
||||
}
|
||||
|
||||
public int updateBatchById(String tableName, List<Row> rows) {
|
||||
return execute(mapper -> mapper.updateBatchById(tableName, rows));
|
||||
}
|
||||
|
||||
public Row selectOneBySql(String sql, Object... args) {
|
||||
return execute(mapper -> mapper.selectOneBySql(sql, args));
|
||||
}
|
||||
|
||||
public Row selectOneById(String tableName, Row row) {
|
||||
return execute(mapper -> mapper.selectOneById(tableName, row));
|
||||
}
|
||||
|
||||
public Row selectOneById(String tableName, String primaryKey, Object id) {
|
||||
return execute(mapper -> mapper.selectOneById(tableName, primaryKey, id));
|
||||
}
|
||||
|
||||
public Row selectOneByMap(String tableName, Map whereColumns) {
|
||||
return execute(mapper -> mapper.selectOneByMap(tableName, whereColumns));
|
||||
}
|
||||
|
||||
public Row selectOneByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||
return execute(mapper -> mapper.selectOneByQuery(tableName, queryWrapper));
|
||||
}
|
||||
|
||||
public List<Row> selectListBySql(String sql, Object... args) {
|
||||
return execute(mapper -> mapper.selectListBySql(sql, args));
|
||||
}
|
||||
|
||||
public List<Row> selectListByMap(String tableName, Map<String, Object> whereColumns) {
|
||||
return execute(mapper -> mapper.selectListByMap(tableName, whereColumns));
|
||||
}
|
||||
|
||||
public List<Row> selectListByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||
return execute(mapper -> mapper.selectListByQuery(tableName, queryWrapper));
|
||||
}
|
||||
|
||||
public List<Row> selectAll(String tableName) {
|
||||
return execute(mapper -> mapper.selectAll(tableName));
|
||||
}
|
||||
|
||||
public Object selectObject(String sql, Object... args) {
|
||||
return execute(mapper -> mapper.selectObject(sql, args));
|
||||
}
|
||||
|
||||
public List<Object> selectObjectList(String sql, Object... args) {
|
||||
return execute(mapper -> mapper.selectObjectList(sql, args));
|
||||
}
|
||||
|
||||
public long selectCount(String sql, Object... args) {
|
||||
return execute(mapper -> mapper.selectCount(sql, args));
|
||||
}
|
||||
|
||||
public long selectCountByQuery(String tableName, QueryWrapper queryWrapper) {
|
||||
return execute(mapper -> mapper.selectCountByQuery(tableName, queryWrapper));
|
||||
}
|
||||
|
||||
public Page<Row> paginate(String tableName, int pageNumber, int pageSize, QueryWrapper queryWrapper) {
|
||||
return execute(mapper -> mapper.paginate(tableName, pageNumber, pageSize, queryWrapper));
|
||||
}
|
||||
|
||||
public Page<Row> paginate(String tableName, Page<Row> page, QueryWrapper queryWrapper) {
|
||||
return execute(mapper -> mapper.paginate(tableName, page, queryWrapper));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package com.mybatisflex.core.row;
|
||||
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
|
||||
public interface RowSessionManager {
|
||||
RowSessionManager DEFAULT = new RowSessionManager() {
|
||||
@Override
|
||||
public SqlSession getSqlSession(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
|
||||
return sqlSessionFactory.openSession(executorType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseSqlSession(SqlSession sqlSession, SqlSessionFactory sqlSessionFactory) {
|
||||
sqlSession.commit();
|
||||
sqlSession.close();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取 sqlSession
|
||||
*
|
||||
* @param sqlSessionFactory
|
||||
*/
|
||||
default SqlSession getSqlSession(SqlSessionFactory sqlSessionFactory){
|
||||
return getSqlSession(sqlSessionFactory,sqlSessionFactory.getConfiguration().getDefaultExecutorType());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取 sqlSession
|
||||
* @param sqlSessionFactory
|
||||
* @param executorType
|
||||
*/
|
||||
SqlSession getSqlSession(SqlSessionFactory sqlSessionFactory, ExecutorType executorType);
|
||||
|
||||
/**
|
||||
* 释放 sqlSession
|
||||
*
|
||||
* @param sqlSession
|
||||
*/
|
||||
void releaseSqlSession(SqlSession sqlSession, SqlSessionFactory sqlSessionFactory);
|
||||
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.table;
|
||||
|
||||
import org.apache.ibatis.reflection.ReflectorFactory;
|
||||
|
||||
public abstract class BaseReflectorFactory implements ReflectorFactory {
|
||||
@Override
|
||||
public boolean isClassCacheEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClassCacheEnabled(boolean classCacheEnabled) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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.table;
|
||||
|
||||
public class ColumnInfo {
|
||||
|
||||
/**
|
||||
* 数据库列名
|
||||
*/
|
||||
protected String column;
|
||||
|
||||
/**
|
||||
* java entity 定义的属性名称
|
||||
*/
|
||||
protected String property;
|
||||
|
||||
/**
|
||||
* 属性类型
|
||||
*/
|
||||
protected Class<?> propertyType;
|
||||
|
||||
public String getColumn() {
|
||||
return column;
|
||||
}
|
||||
|
||||
public void setColumn(String column) {
|
||||
this.column = column;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public void setProperty(String property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
public Class<?> getPropertyType() {
|
||||
return propertyType;
|
||||
}
|
||||
|
||||
public void setPropertyType(Class<?> propertyType) {
|
||||
this.propertyType = propertyType;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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.table;
|
||||
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.apache.ibatis.reflection.ReflectorFactory;
|
||||
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
|
||||
import org.apache.ibatis.reflection.factory.ObjectFactory;
|
||||
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
|
||||
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
|
||||
|
||||
|
||||
public final class EntityMetaObject {
|
||||
|
||||
public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
|
||||
public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
|
||||
|
||||
private EntityMetaObject() {
|
||||
// Prevent Instantiation of Static Class
|
||||
}
|
||||
|
||||
|
||||
public static MetaObject forObject(Object object, ReflectorFactory reflectorFactory) {
|
||||
return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, reflectorFactory);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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.table;
|
||||
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.core.enums.KeyType;
|
||||
|
||||
public class IdInfo extends ColumnInfo {
|
||||
|
||||
/**
|
||||
* id 生成策略
|
||||
*/
|
||||
private KeyType keyType;
|
||||
|
||||
/**
|
||||
* 若 keyType 类型是 sequence, value 则代表的是
|
||||
* sequence 序列的 sql 内容
|
||||
* 例如:select SEQ_USER_ID.nextval as id from dual
|
||||
*
|
||||
* 若 keyType 是 Generator,value 则代表的是使用的那个 keyGenerator 的名称
|
||||
*/
|
||||
private String value;
|
||||
|
||||
|
||||
/**
|
||||
* sequence 序列内容执行顺序
|
||||
*
|
||||
* @see org.apache.ibatis.executor.keygen.SelectKeyGenerator
|
||||
*/
|
||||
private boolean before;
|
||||
|
||||
|
||||
public IdInfo(ColumnInfo columnInfo) {
|
||||
this.setColumn(columnInfo.getColumn());
|
||||
this.setProperty(columnInfo.getProperty());
|
||||
this.setPropertyType(columnInfo.getPropertyType());
|
||||
|
||||
//当 id 的类型为数值时,默认设置为自增的方式
|
||||
if (Number.class.isAssignableFrom(columnInfo.getPropertyType())) {
|
||||
keyType = KeyType.Auto;
|
||||
} else {
|
||||
keyType = KeyType.None;
|
||||
}
|
||||
}
|
||||
|
||||
public IdInfo(String column, String property, Class<?> propertyType, Id id) {
|
||||
this.column = column;
|
||||
this.property = property;
|
||||
this.propertyType = propertyType;
|
||||
this.keyType = id.keyType();
|
||||
this.value = id.value();
|
||||
this.before = id.before();
|
||||
}
|
||||
|
||||
public KeyType getKeyType() {
|
||||
return keyType;
|
||||
}
|
||||
|
||||
public void setKeyType(KeyType keyType) {
|
||||
this.keyType = keyType;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean isBefore() {
|
||||
return before;
|
||||
}
|
||||
|
||||
public void setBefore(boolean before) {
|
||||
this.before = before;
|
||||
}
|
||||
}
|
||||
@ -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.table;
|
||||
|
||||
import com.mybatisflex.core.querywrapper.QueryTable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class TableDef implements Serializable {
|
||||
|
||||
private String tableName;
|
||||
|
||||
public TableDef(String tableName) {
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
public String getTableName() {
|
||||
return tableName;
|
||||
}
|
||||
|
||||
public QueryTable as(String alias) {
|
||||
return new QueryTable(tableName, alias);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,382 @@
|
||||
/**
|
||||
* 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.table;
|
||||
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.enums.KeyType;
|
||||
import com.mybatisflex.core.javassist.ModifyAttrsRecord;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import com.mybatisflex.core.util.ArrayUtil;
|
||||
import com.mybatisflex.core.util.ClassUtil;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import org.apache.ibatis.mapping.ResultFlag;
|
||||
import org.apache.ibatis.mapping.ResultMap;
|
||||
import org.apache.ibatis.mapping.ResultMapping;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.apache.ibatis.reflection.Reflector;
|
||||
import org.apache.ibatis.reflection.ReflectorFactory;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class TableInfo {
|
||||
|
||||
private String schema; //schema
|
||||
private String tableName; //表名
|
||||
private Class<?> entityClass; //实体类
|
||||
private boolean useCached = false;
|
||||
private boolean camelToUnderline = true;
|
||||
|
||||
|
||||
private String[] columns = new String[0]; // 所有的字段,但除了主键的列
|
||||
private String[] primaryKeys = new String[0]; //主键字段
|
||||
|
||||
//在插入数据的时候,支持主动插入的主键字段
|
||||
//排除主键为 auto 和 before 为 false 的主键字段
|
||||
private String[] insertPrimaryKeys;
|
||||
|
||||
private List<ColumnInfo> columnInfoList;
|
||||
private List<IdInfo> primaryKeyList;
|
||||
|
||||
//column 和 java 属性的称的关系映射
|
||||
private Map<String, String> columnPropertyMapping = new HashMap<>();
|
||||
private Map<String, String> propertyColumnMapping = new HashMap<>();
|
||||
|
||||
private final ReflectorFactory reflectorFactory = new BaseReflectorFactory() {
|
||||
@Override
|
||||
public Reflector findForClass(Class<?> type) {
|
||||
return getReflector();
|
||||
}
|
||||
};
|
||||
private Reflector reflector; //反射工具
|
||||
|
||||
public String getSchema() {
|
||||
return schema;
|
||||
}
|
||||
|
||||
public void setSchema(String schema) {
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
public String getTableName() {
|
||||
return tableName;
|
||||
}
|
||||
|
||||
public void setTableName(String tableName) {
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
public Class<?> getEntityClass() {
|
||||
return entityClass;
|
||||
}
|
||||
|
||||
public void setEntityClass(Class<?> entityClass) {
|
||||
this.entityClass = entityClass;
|
||||
}
|
||||
|
||||
public boolean isUseCached() {
|
||||
return useCached;
|
||||
}
|
||||
|
||||
public void setUseCached(boolean useCached) {
|
||||
this.useCached = useCached;
|
||||
}
|
||||
|
||||
public boolean isCamelToUnderline() {
|
||||
return camelToUnderline;
|
||||
}
|
||||
|
||||
public void setCamelToUnderline(boolean camelToUnderline) {
|
||||
this.camelToUnderline = camelToUnderline;
|
||||
}
|
||||
|
||||
public Reflector getReflector() {
|
||||
return reflector;
|
||||
}
|
||||
|
||||
public ReflectorFactory getReflectorFactory() {
|
||||
return reflectorFactory;
|
||||
}
|
||||
|
||||
public void setReflector(Reflector reflector) {
|
||||
this.reflector = reflector;
|
||||
}
|
||||
|
||||
public String[] getColumns() {
|
||||
return columns;
|
||||
}
|
||||
|
||||
|
||||
public void setColumns(String[] columns) {
|
||||
this.columns = columns;
|
||||
}
|
||||
|
||||
public String[] getPrimaryKeys() {
|
||||
return primaryKeys;
|
||||
}
|
||||
|
||||
public void setPrimaryKeys(String[] primaryKeys) {
|
||||
this.primaryKeys = primaryKeys;
|
||||
}
|
||||
|
||||
|
||||
public List<ColumnInfo> getColumnInfoList() {
|
||||
return columnInfoList;
|
||||
}
|
||||
|
||||
|
||||
void setColumnInfoList(List<ColumnInfo> columnInfoList) {
|
||||
this.columnInfoList = columnInfoList;
|
||||
this.columns = new String[columnInfoList.size()];
|
||||
for (int i = 0; i < columnInfoList.size(); i++) {
|
||||
ColumnInfo columnInfo = columnInfoList.get(i);
|
||||
columns[i] = columnInfo.getColumn();
|
||||
columnPropertyMapping.put(columnInfo.column, columnInfo.property);
|
||||
propertyColumnMapping.put(columnInfo.property, columnInfo.column);
|
||||
}
|
||||
}
|
||||
|
||||
public List<IdInfo> getPrimaryKeyList() {
|
||||
return primaryKeyList;
|
||||
}
|
||||
|
||||
void setPrimaryKeyList(List<IdInfo> primaryKeyList) {
|
||||
this.primaryKeyList = primaryKeyList;
|
||||
this.primaryKeys = new String[primaryKeyList.size()];
|
||||
|
||||
List<String> insertIdFields = new ArrayList<>();
|
||||
for (int i = 0; i < primaryKeyList.size(); i++) {
|
||||
IdInfo idInfo = primaryKeyList.get(i);
|
||||
primaryKeys[i] = idInfo.getColumn();
|
||||
|
||||
if (idInfo.getKeyType() != KeyType.Auto && idInfo.isBefore()) {
|
||||
insertIdFields.add(idInfo.getColumn());
|
||||
}
|
||||
|
||||
columnPropertyMapping.put(idInfo.column, idInfo.property);
|
||||
propertyColumnMapping.put(idInfo.property, idInfo.column);
|
||||
}
|
||||
this.insertPrimaryKeys = insertIdFields.toArray(new String[0]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 插入(新增)数据时,获取所有要插入的字段
|
||||
*
|
||||
* @return 字段列表
|
||||
*/
|
||||
public String[] obtainInsertColumns() {
|
||||
return ArrayUtil.concat(insertPrimaryKeys, columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 插入字段 获取所有插入的值
|
||||
*
|
||||
* @param entity 从 entity 中获取
|
||||
* @return 数组
|
||||
*/
|
||||
public Object[] obtainInsertValues(Object entity) {
|
||||
MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
|
||||
String[] insertColumns = obtainInsertColumns();
|
||||
Object[] values = new Object[insertColumns.length];
|
||||
for (int i = 0; i < insertColumns.length; i++) {
|
||||
Object value = getColumnValue(metaObject, insertColumns[i]);
|
||||
values[i] = value;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取要修改的值
|
||||
*
|
||||
* @param entity
|
||||
* @param ignoreNulls
|
||||
* @return
|
||||
*/
|
||||
public Set<String> obtainUpdateColumns(Object entity, boolean ignoreNulls, boolean includePrimary) {
|
||||
MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
|
||||
Set<String> columns = new LinkedHashSet<>(); //需使用 LinkedHashSet 保证 columns 的顺序
|
||||
if (entity instanceof ModifyAttrsRecord) {
|
||||
Set<String> properties = ((ModifyAttrsRecord) entity).obtainModifyAttrs();
|
||||
if (properties.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
for (String property : properties) {
|
||||
String column = getColumnByProperty(property);
|
||||
if (!includePrimary && ArrayUtil.contains(primaryKeys, column)) {
|
||||
continue;
|
||||
}
|
||||
Object value = getPropertyValue(metaObject, property);
|
||||
if (ignoreNulls && value == null) {
|
||||
continue;
|
||||
}
|
||||
columns.add(column);
|
||||
}
|
||||
}
|
||||
//not ModifyAttrsRecord
|
||||
else {
|
||||
for (String column : this.columns) {
|
||||
Object value = getColumnValue(metaObject, column);
|
||||
if (ignoreNulls && value == null) {
|
||||
continue;
|
||||
}
|
||||
columns.add(column);
|
||||
}
|
||||
|
||||
// 普通 entity(非 ModifyAttrsRecord) 忽略 includePrimary 的设置
|
||||
// if (includePrimary) {
|
||||
// for (String column : this.primaryKeys) {
|
||||
// Object value = getColumnValue(metaObject, column);
|
||||
// if (ignoreNulls && value == null) {
|
||||
// continue;
|
||||
// }
|
||||
// columns.add(column);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有要修改的值,默认为全部除了主键以外的字段
|
||||
*
|
||||
* @param entity 实体对象
|
||||
* @return 数组
|
||||
*/
|
||||
public Object[] obtainUpdateValues(Object entity, boolean ignoreNulls, boolean includePrimary) {
|
||||
MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
|
||||
List<Object> values = new ArrayList<>();
|
||||
if (entity instanceof ModifyAttrsRecord) {
|
||||
Set<String> properties = ((ModifyAttrsRecord) entity).obtainModifyAttrs();
|
||||
if (properties.isEmpty()) {
|
||||
return values.toArray();
|
||||
}
|
||||
for (String property : properties) {
|
||||
String column = getColumnByProperty(property);
|
||||
if (!includePrimary && ArrayUtil.contains(primaryKeys, column)) {
|
||||
continue;
|
||||
}
|
||||
Object value = getPropertyValue(metaObject, property);
|
||||
if (ignoreNulls && value == null) {
|
||||
continue;
|
||||
}
|
||||
values.add(value);
|
||||
}
|
||||
}
|
||||
// not ModifyAttrsRecord
|
||||
else {
|
||||
for (String column : this.columns) {
|
||||
Object value = getColumnValue(metaObject, column);
|
||||
if (ignoreNulls && value == null) {
|
||||
continue;
|
||||
}
|
||||
values.add(value);
|
||||
}
|
||||
// 普通 entity 忽略 includePrimary 的设置
|
||||
// if (includePrimary) {
|
||||
// }
|
||||
}
|
||||
|
||||
return values.toArray();
|
||||
}
|
||||
|
||||
|
||||
public Object[] obtainPrimaryValues(Object entity) {
|
||||
MetaObject metaObject = EntityMetaObject.forObject(entity, reflectorFactory);
|
||||
Object[] values = new Object[primaryKeys.length];
|
||||
for (int i = 0; i < primaryKeys.length; i++) {
|
||||
values[i] = getColumnValue(metaObject, primaryKeys[i]);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
public String getMappedStatementKeyProperties() {
|
||||
StringJoiner joiner = new StringJoiner(",");
|
||||
for (IdInfo value : primaryKeyList) {
|
||||
joiner.add(FlexConsts.ENTITY + "." + value.getProperty());
|
||||
}
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
|
||||
public String getMappedStatementKeyColumns() {
|
||||
StringJoiner joiner = new StringJoiner(",");
|
||||
for (IdInfo value : primaryKeyList) {
|
||||
joiner.add(FlexConsts.ENTITY + "." + value.getColumn());
|
||||
}
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
public ResultMap buildResultMap(Configuration configuration) {
|
||||
String resultMapId = entityClass.getName();
|
||||
List<ResultMapping> resultMappings = new ArrayList<>();
|
||||
|
||||
for (ColumnInfo columnInfo : columnInfoList) {
|
||||
ResultMapping mapping = new ResultMapping.Builder(configuration, columnInfo.getProperty(),
|
||||
columnInfo.getColumn(), columnInfo.getPropertyType()).build();
|
||||
resultMappings.add(mapping);
|
||||
}
|
||||
for (IdInfo idInfo : primaryKeyList) {
|
||||
ResultMapping mapping = new ResultMapping.Builder(configuration, idInfo.getProperty(),
|
||||
idInfo.getColumn(), idInfo.getPropertyType())
|
||||
.flags(CollectionUtil.newArrayList(ResultFlag.ID))
|
||||
// .typeHandler()
|
||||
.build();
|
||||
resultMappings.add(mapping);
|
||||
}
|
||||
|
||||
return new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
private Object getColumnValue(MetaObject metaObject, String column) {
|
||||
return getPropertyValue(metaObject, columnPropertyMapping.get(column));
|
||||
}
|
||||
|
||||
|
||||
private Object getPropertyValue(MetaObject metaObject, String property) {
|
||||
if (property != null && metaObject.hasGetter(property)) {
|
||||
return metaObject.getValue(property);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public String getColumnByProperty(String property) {
|
||||
return propertyColumnMapping.get(property);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过 row 实例类转换为一个 entity
|
||||
* @return entity
|
||||
*/
|
||||
public <T> T newInstanceByRow(Row row) {
|
||||
Object instance = ClassUtil.newInstance(entityClass);
|
||||
MetaObject metaObject = EntityMetaObject.forObject(instance, reflectorFactory);
|
||||
for (String column : row.keySet()) {
|
||||
String property = columnPropertyMapping.get(column);
|
||||
if (metaObject.hasSetter(property)) {
|
||||
metaObject.setValue(property, row.get(column));
|
||||
}
|
||||
}
|
||||
return (T) instance;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,166 @@
|
||||
/**
|
||||
* 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.table;
|
||||
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.util.ClassUtil;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
import org.apache.ibatis.reflection.Reflector;
|
||||
import org.apache.ibatis.util.MapUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class TableInfos {
|
||||
private static final Set<Class<?>> defaultSupportColumnTypes = CollectionUtil.newHashSet(
|
||||
int.class, Integer.class,
|
||||
short.class, Short.class,
|
||||
long.class, Long.class,
|
||||
float.class, Float.class,
|
||||
double.class, Double.class,
|
||||
boolean.class, Boolean.class,
|
||||
Date.class, java.sql.Date.class, LocalDate.class, LocalDateTime.class, LocalTime.class,
|
||||
byte[].class, Byte[].class,
|
||||
BigInteger.class, BigDecimal.class,
|
||||
char.class, String.class
|
||||
);
|
||||
|
||||
|
||||
private static Map<Class<?>, TableInfo> tableInfoMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
public static TableInfo ofMapperClass(Class<?> mapperClass) {
|
||||
return MapUtil.computeIfAbsent(tableInfoMap, mapperClass, key -> {
|
||||
Class<?> entityClass = getEntityClass(key);
|
||||
return entityClass != null ? ofEntityClass(entityClass) : null;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static TableInfo ofEntityClass(Class<?> entityClass) {
|
||||
return MapUtil.computeIfAbsent(tableInfoMap, entityClass, key -> createTableInfo(entityClass));
|
||||
}
|
||||
|
||||
|
||||
private static Class<?> getEntityClass(Class<?> mapperClass) {
|
||||
Type[] genericInterfaces = mapperClass.getGenericInterfaces();
|
||||
if (genericInterfaces.length == 1) {
|
||||
Type type = genericInterfaces[0];
|
||||
if (type instanceof ParameterizedType) {
|
||||
return (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
} else {
|
||||
return getEntityClass((Class<?>) type);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static TableInfo createTableInfo(Class<?> entityClass) {
|
||||
TableInfo tableInfo = new TableInfo();
|
||||
tableInfo.setEntityClass(entityClass);
|
||||
tableInfo.setReflector(new Reflector(entityClass));
|
||||
|
||||
|
||||
//初始化表名
|
||||
Table table = entityClass.getAnnotation(Table.class);
|
||||
if (table != null) {
|
||||
tableInfo.setTableName(table.value());
|
||||
tableInfo.setSchema(table.schema());
|
||||
tableInfo.setUseCached(table.useCached());
|
||||
tableInfo.setCamelToUnderline(table.camelToUnderline());
|
||||
} else {
|
||||
//默认为类名转驼峰下划线
|
||||
tableInfo.setTableName(StringUtil.camelToUnderline(entityClass.getSimpleName()));
|
||||
}
|
||||
|
||||
//初始化字段相关
|
||||
List<ColumnInfo> columnInfoList = new ArrayList<>();
|
||||
List<IdInfo> idInfos = new ArrayList<>();
|
||||
|
||||
Field idField = null;
|
||||
|
||||
List<Field> entityFields = ClassUtil.getAllFields(entityClass);
|
||||
for (Field field : entityFields) {
|
||||
|
||||
//只支持基本数据类型,不支持比如 list set 或者自定义的类等
|
||||
if (!defaultSupportColumnTypes.contains(field.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Column column = field.getAnnotation(Column.class);
|
||||
if (column != null && column.ignore()) {
|
||||
continue; // ignore
|
||||
}
|
||||
|
||||
String columnName = column != null && StringUtil.isNotBlank(column.value())
|
||||
? column.value()
|
||||
: (tableInfo.isCamelToUnderline() ? StringUtil.camelToUnderline(field.getName()) : field.getName());
|
||||
|
||||
Id id = field.getAnnotation(Id.class);
|
||||
ColumnInfo columnInfo;
|
||||
if (id != null) {
|
||||
columnInfo = new IdInfo(columnName, field.getName(), field.getType(), id);
|
||||
idInfos.add((IdInfo) columnInfo);
|
||||
} else {
|
||||
columnInfo = new ColumnInfo();
|
||||
columnInfoList.add(columnInfo);
|
||||
}
|
||||
|
||||
columnInfo.setColumn(columnName);
|
||||
columnInfo.setProperty(field.getName());
|
||||
columnInfo.setPropertyType(field.getType());
|
||||
|
||||
if (FlexConsts.DEFAULT_PRIMARY_FIELD.equals(field.getName())) {
|
||||
idField = field;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (idInfos.isEmpty() && idField != null) {
|
||||
int index = -1;
|
||||
for (int i = 0; i < columnInfoList.size(); i++) {
|
||||
ColumnInfo columnInfo = columnInfoList.get(i);
|
||||
if (FlexConsts.DEFAULT_PRIMARY_FIELD.equals(columnInfo.getProperty())) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index >= 0) {
|
||||
ColumnInfo removedColumnInfo = columnInfoList.remove(index);
|
||||
idInfos.add(new IdInfo(removedColumnInfo));
|
||||
}
|
||||
}
|
||||
|
||||
tableInfo.setColumnInfoList(columnInfoList);
|
||||
tableInfo.setPrimaryKeyList(idInfos);
|
||||
|
||||
return tableInfo;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 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.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ArrayUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 判断数组是否为空
|
||||
*
|
||||
* @param array
|
||||
* @param <T>
|
||||
* @return 空 true
|
||||
*/
|
||||
public static <T> boolean isEmpty(T[] array) {
|
||||
return array == null || array.length == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断数组是否不为空
|
||||
*
|
||||
* @param array
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> boolean isNotEmpty(T[] array) {
|
||||
return !isEmpty(array);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 合并两个数组为一个新的数组
|
||||
*
|
||||
* @param first 第一个数组
|
||||
* @param second 第二个数组
|
||||
* @param <T>
|
||||
* @return 新的数组
|
||||
*/
|
||||
public static <T> T[] concat(T[] first, T[] second) {
|
||||
if (first == null && second == null) {
|
||||
throw new IllegalArgumentException("not allow first and second are null.");
|
||||
} else if (isEmpty(first)) {
|
||||
return second;
|
||||
} else if (isEmpty(second)) {
|
||||
return first;
|
||||
} else {
|
||||
T[] result = Arrays.copyOf(first, first.length + second.length);
|
||||
System.arraycopy(second, 0, result, first.length, second.length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查看数组中是否包含某一个值
|
||||
*
|
||||
* @param arrays 数组
|
||||
* @param object 用于检测的值
|
||||
* @param <T>
|
||||
* @return true 包含
|
||||
*/
|
||||
public static <T> boolean contains(T[] arrays, T object) {
|
||||
if (isEmpty(arrays)) {
|
||||
return false;
|
||||
}
|
||||
for (T array : arrays) {
|
||||
if (Objects.equals(array, object)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 急速构建数组
|
||||
*/
|
||||
public static <T> T[] asArray(T... elements) {
|
||||
return elements;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,177 @@
|
||||
/**
|
||||
* 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.lang.reflect.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 类实例创建者创建者
|
||||
* Created by michael on 17/3/21.
|
||||
*/
|
||||
public class ClassUtil {
|
||||
|
||||
|
||||
//proxy frameworks
|
||||
private static final List<String> PROXY_CLASS_NAMES = Arrays.asList("net.sf.cglib.proxy.Factory"
|
||||
// cglib
|
||||
, "org.springframework.cglib.proxy.Factory"
|
||||
, "javassist.util.proxy.ProxyObject"
|
||||
// javassist
|
||||
, "org.apache.ibatis.javassist.util.proxy.ProxyObject");
|
||||
|
||||
public static boolean isProxy(Class<?> clazz) {
|
||||
for (Class<?> cls : clazz.getInterfaces()) {
|
||||
if (PROXY_CLASS_NAMES.contains(cls.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//java proxy
|
||||
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) {
|
||||
if (isProxy(clazz)) {
|
||||
return (Class<T>) clazz.getSuperclass();
|
||||
}
|
||||
|
||||
//ControllerTest$ServiceTest$$EnhancerByGuice$$40471411#hello -------> Guice
|
||||
//com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158 ----> CGLIB
|
||||
//io.jboot.test.app.TestAppListener_$$_jvstb9f_0 ------> javassist
|
||||
|
||||
final String name = clazz.getName();
|
||||
if (name.contains(ENHANCER_BY) || name.contains(JAVASSIST_BY)) {
|
||||
return (Class<T>) clazz.getSuperclass();
|
||||
}
|
||||
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public static <T> T newInstance(Class<T> clazz) {
|
||||
try {
|
||||
Constructor constructor = clazz.getDeclaredConstructor();
|
||||
return (T) constructor.newInstance();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static <T> T newInstance(Class<T> clazz, Object... paras) {
|
||||
try {
|
||||
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
|
||||
for (Constructor<?> constructor : constructors) {
|
||||
if (isMatchedParas(constructor, paras)) {
|
||||
Object ret = constructor.newInstance(paras);
|
||||
return (T) ret;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Can not matched constructor by paras" + Arrays.toString(paras) + " for class: " + clazz.getName());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static boolean isMatchedParas(Constructor<?> constructor, Object[] paras) {
|
||||
if (constructor.getParameterCount() == 0) {
|
||||
return paras == null || paras.length == 0;
|
||||
}
|
||||
|
||||
if (constructor.getParameterCount() > 0
|
||||
&& (paras == null || paras.length != constructor.getParameterCount())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
Class<?> parameterType = parameterTypes[i];
|
||||
Object paraObject = paras[i];
|
||||
if (paraObject != null && !parameterType.isAssignableFrom(paraObject.getClass())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static String buildMethodString(Method method) {
|
||||
StringBuilder sb = new StringBuilder()
|
||||
.append(method.getDeclaringClass().getName())
|
||||
.append(".")
|
||||
.append(method.getName())
|
||||
.append("(");
|
||||
|
||||
Class<?>[] params = method.getParameterTypes();
|
||||
int in = 0;
|
||||
for (Class<?> clazz : params) {
|
||||
sb.append(clazz.getName());
|
||||
if (++in < params.length) {
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.append(")").toString();
|
||||
}
|
||||
|
||||
|
||||
public static boolean hasClass(String className) {
|
||||
try {
|
||||
Class.forName(className, false, getClassLoader());
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static ClassLoader getClassLoader() {
|
||||
ClassLoader ret = Thread.currentThread().getContextClassLoader();
|
||||
return ret != null ? ret : ClassUtil.class.getClassLoader();
|
||||
}
|
||||
|
||||
public static List<Field> getAllFields(Class<?> cl) {
|
||||
List<Field> fields = new ArrayList<>();
|
||||
doGetFields(cl, fields);
|
||||
return fields;
|
||||
}
|
||||
|
||||
private static void doGetFields(Class<?> cl, List<Field> fields) {
|
||||
if (cl == null || cl == Object.class) {
|
||||
return;
|
||||
}
|
||||
|
||||
Field[] declaredFields = cl.getDeclaredFields();
|
||||
for (Field declaredField : declaredFields) {
|
||||
fields.add(declaredField);
|
||||
}
|
||||
|
||||
doGetFields(cl.getSuperclass(), fields);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 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.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
||||
public class CollectionUtil {
|
||||
|
||||
|
||||
public static boolean isEmpty(Collection<?> coll) {
|
||||
return (coll == null || coll.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
public static boolean isNotEmpty(Collection<?> coll) {
|
||||
return !isEmpty(coll);
|
||||
}
|
||||
|
||||
|
||||
public static boolean isEmpty(Map<?, ?> map) {
|
||||
return (map == null || map.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
public static boolean isNotEmpty(Map<?, ?> map) {
|
||||
return !isEmpty(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并 list
|
||||
*/
|
||||
public static <T> List<T> merge(List<T> list, List<T> other) {
|
||||
if (list == null && other == null) {
|
||||
return new ArrayList<>();
|
||||
} else if (isEmpty(other)) {
|
||||
return list;
|
||||
} else if (isEmpty(list)) {
|
||||
return other;
|
||||
}
|
||||
List<T> newList = new ArrayList<>(list);
|
||||
newList.addAll(other);
|
||||
return newList;
|
||||
}
|
||||
|
||||
|
||||
public static <K, V> HashMap<K, V> newHashMap() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 主要是用于修复 concurrentHashMap 在 jdk1.8 下的死循环问题
|
||||
*
|
||||
* @see <a href="https://bugs.openjdk.org/browse/JDK-8161372">https://bugs.openjdk.org/browse/JDK-8161372</a>
|
||||
*/
|
||||
public static <K, V> V computeIfAbsent(Map<K, V> concurrentHashMap, K key, Function<? super K, ? extends V> mappingFunction) {
|
||||
V v = concurrentHashMap.get(key);
|
||||
if (v != null) {
|
||||
return v;
|
||||
}
|
||||
return concurrentHashMap.computeIfAbsent(key, mappingFunction);
|
||||
}
|
||||
|
||||
public static <T> Set<T> newHashSet(T... elements) {
|
||||
return new HashSet<>(Arrays.asList(elements));
|
||||
}
|
||||
|
||||
public static <T> List<T> newArrayList(T... elements) {
|
||||
return new ArrayList<>(Arrays.asList(elements));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public class ObjectUtil {
|
||||
|
||||
public static <T> T requireNonNullElse(T t1, T t2) {
|
||||
return t1 == null ? t2 : t1;
|
||||
}
|
||||
|
||||
public static boolean areNotNull(Object ... objs){
|
||||
for (Object obj : objs) {
|
||||
if (obj == null){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean areNull(Object ... objs){
|
||||
for (Object obj : objs) {
|
||||
if (obj != null){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,251 @@
|
||||
/**
|
||||
* 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.util.Collection;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class StringUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 第一个字符转换为小写
|
||||
*
|
||||
* @param string
|
||||
* @return
|
||||
*/
|
||||
public static String firstCharToLowerCase(String string) {
|
||||
char firstChar = string.charAt(0);
|
||||
if (firstChar >= 'A' && firstChar <= 'Z') {
|
||||
char[] arr = string.toCharArray();
|
||||
arr[0] += ('a' - 'A');
|
||||
return new String(arr);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 第一个字符转换为大写
|
||||
*
|
||||
* @param string
|
||||
*/
|
||||
public static String firstCharToUpperCase(String string) {
|
||||
char firstChar = string.charAt(0);
|
||||
if (firstChar >= 'a' && firstChar <= 'z') {
|
||||
char[] arr = string.toCharArray();
|
||||
arr[0] -= ('a' - 'A');
|
||||
return new String(arr);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 驼峰转下划线格式
|
||||
*
|
||||
* @param string
|
||||
*/
|
||||
public static String camelToUnderline(String string) {
|
||||
if (isBlank(string)) {
|
||||
return "";
|
||||
}
|
||||
int len = string.length();
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = string.charAt(i);
|
||||
if (Character.isUpperCase(c) && i > 0) {
|
||||
sb.append('_');
|
||||
}
|
||||
sb.append(Character.toLowerCase(c));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 下划线转驼峰格式
|
||||
*
|
||||
* @param string
|
||||
*/
|
||||
public static String underlineToCamel(String string) {
|
||||
if (isBlank(string)) {
|
||||
return "";
|
||||
}
|
||||
String temp = string.toLowerCase();
|
||||
int len = temp.length();
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = temp.charAt(i);
|
||||
if (c == '_') {
|
||||
if (++i < len) {
|
||||
sb.append(Character.toUpperCase(temp.charAt(i)));
|
||||
}
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串为 null 或者内部字符全部为 ' ', '\t', '\n', '\r' 这四类字符时返回 true
|
||||
*/
|
||||
public static boolean isBlank(String str) {
|
||||
if (str == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0, len = str.length(); i < len; i++) {
|
||||
if (str.charAt(i) > ' ') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isAnyBlank(String... strings) {
|
||||
if (strings == null || strings.length == 0) {
|
||||
throw new IllegalArgumentException("args is empty.");
|
||||
}
|
||||
|
||||
for (String str : strings) {
|
||||
if (isBlank(str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isNotBlank(String str) {
|
||||
return !isBlank(str);
|
||||
}
|
||||
|
||||
|
||||
public static boolean areNotBlank(String... strings) {
|
||||
return !isAnyBlank();
|
||||
}
|
||||
|
||||
|
||||
public static boolean startsWithAny(String str, String... prefixes) {
|
||||
if (isBlank(str) || prefixes == null || prefixes.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (String prefix : prefixes) {
|
||||
if (str.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static boolean endsWithAny(String str, String... suffixes) {
|
||||
if (isBlank(str) || suffixes == null || suffixes.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (String suffix : suffixes) {
|
||||
if (str.endsWith(suffix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 正则匹配
|
||||
*
|
||||
* @param regex
|
||||
* @param input
|
||||
* @return
|
||||
*/
|
||||
public static boolean matches(String regex, String input) {
|
||||
if (null == regex || null == input) {
|
||||
return false;
|
||||
}
|
||||
return Pattern.matches(regex, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并字符串,优化 String.join() 方法
|
||||
*
|
||||
* @param delimiter
|
||||
* @param elements
|
||||
* @return 新拼接好的字符串
|
||||
* @see String#join(CharSequence, CharSequence...)
|
||||
*/
|
||||
public static String join(String delimiter, CharSequence... elements) {
|
||||
if (ArrayUtil.isEmpty(elements)) {
|
||||
return "";
|
||||
} else if (elements.length == 1) {
|
||||
return String.valueOf(elements[0]);
|
||||
} else {
|
||||
return String.join(delimiter, elements);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 合并字符串,优化 String.join() 方法
|
||||
*
|
||||
* @param delimiter
|
||||
* @param elements
|
||||
* @return 新拼接好的字符串
|
||||
* @see String#join(CharSequence, CharSequence...)
|
||||
*/
|
||||
public static String join(String delimiter, Collection<? extends CharSequence> elements) {
|
||||
if (CollectionUtil.isEmpty(elements)) {
|
||||
return "";
|
||||
} else if (elements.size() == 1) {
|
||||
return String.valueOf(elements.iterator().next());
|
||||
} else {
|
||||
return String.join(delimiter, elements);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并字符串,优化 String.join() 方法
|
||||
*
|
||||
* @param delimiter
|
||||
* @param objs
|
||||
* @param function
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> String join(String delimiter, Collection<T> objs, Function<T, String> function) {
|
||||
if (CollectionUtil.isEmpty(objs)) {
|
||||
return "";
|
||||
} else if (objs.size() == 1) {
|
||||
T next = objs.iterator().next();
|
||||
return String.valueOf(function.apply(next));
|
||||
} else {
|
||||
String[] strings = new String[objs.size()];
|
||||
int index = 0;
|
||||
for (T obj : objs) {
|
||||
strings[index++] = function.apply(obj);
|
||||
}
|
||||
return String.join(delimiter, strings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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.core.javassist.ModifyAttrsRecordProxyFactory;
|
||||
import org.apache.ibatis.reflection.DefaultReflectorFactory;
|
||||
import org.apache.ibatis.reflection.Reflector;
|
||||
import org.apache.ibatis.reflection.ReflectorFactory;
|
||||
|
||||
public class UpdateEntity {
|
||||
|
||||
private static ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
|
||||
|
||||
|
||||
|
||||
public static <T> T wrap(Class<T> clazz) {
|
||||
return ModifyAttrsRecordProxyFactory.getInstance().get(clazz);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static <T> T ofNotNull(T entity) {
|
||||
Class<?> usefulClass = ClassUtil.getUsefulClass(entity.getClass());
|
||||
|
||||
T newEntity = (T) wrap(usefulClass);
|
||||
|
||||
Reflector reflector = reflectorFactory.findForClass(usefulClass);
|
||||
String[] propertyNames = reflector.getGetablePropertyNames();
|
||||
|
||||
for (String propertyName : propertyNames) {
|
||||
try {
|
||||
Object value = reflector.getGetInvoker(propertyName)
|
||||
.invoke(entity, null);
|
||||
if (value != null) {
|
||||
reflector.getSetInvoker(propertyName).invoke(newEntity, new Object[]{value});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//ignore();
|
||||
}
|
||||
}
|
||||
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package com.mybatisflex.test;
|
||||
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.Table;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Table("tb_account")
|
||||
public class Account {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String userName;
|
||||
|
||||
private Date birthday;
|
||||
|
||||
private int sex;
|
||||
|
||||
private boolean isNormal;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public Date getBirthday() {
|
||||
return birthday;
|
||||
}
|
||||
|
||||
public void setBirthday(Date birthday) {
|
||||
this.birthday = birthday;
|
||||
}
|
||||
|
||||
public int getSex() {
|
||||
return sex;
|
||||
}
|
||||
|
||||
public void setSex(int sex) {
|
||||
this.sex = sex;
|
||||
}
|
||||
|
||||
public boolean isNormal() {
|
||||
return isNormal;
|
||||
}
|
||||
|
||||
public void setNormal(boolean normal) {
|
||||
isNormal = normal;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package com.mybatisflex.test;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.dialect.CommonsDialectImpl;
|
||||
import com.mybatisflex.core.querywrapper.CPI;
|
||||
import com.mybatisflex.core.querywrapper.QueryWrapper;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.mybatisflex.core.querywrapper.QueryMethods.*;
|
||||
import static com.mybatisflex.test.table.Tables.ACCOUNT;
|
||||
import static com.mybatisflex.test.table.Tables.ARTICLE;
|
||||
|
||||
public class AccountSqlTester {
|
||||
|
||||
@Test
|
||||
public void testJava(){
|
||||
StringBuilder sql = new StringBuilder("select * from xxx");
|
||||
// sql.(7," 你好 ");
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testrSelectLimitSql() {
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.select(ACCOUNT.ALL_COLUMNS)
|
||||
.select(ARTICLE.ID.as("article_id"))
|
||||
.select(distinct(ARTICLE.ID))
|
||||
.select(max(ACCOUNT.SEX))
|
||||
.select(count(distinct(ARTICLE.ID)))
|
||||
.from(ACCOUNT).as("a1")
|
||||
// .leftJoin(newWrapper().select().from(ARTICLE).where(ARTICLE.ID.ge(100))).as("aaa")
|
||||
.leftJoin(ARTICLE).as("b1")
|
||||
.on(ARTICLE.ACCOUNT_ID.eq(ACCOUNT.ID))
|
||||
.where(ACCOUNT.ID.ge(select(ARTICLE.ID).from(ARTICLE).as("cc").where(ARTICLE.ID.eq(111))))
|
||||
.and((true ? noCondition() : ARTICLE.ID.ge(22211)).and(ACCOUNT.ID.eq(10011)).when(false))
|
||||
.and(ACCOUNT.USER_NAME.like("michael"))
|
||||
.and(ARTICLE.ID.in(select(ARTICLE.ID).from("aaa")))
|
||||
.and(
|
||||
notExist(
|
||||
selectOne().from("aaa").where(ARTICLE.ID.ge(333))
|
||||
)
|
||||
)
|
||||
.groupBy(ACCOUNT.ID).having(ARTICLE.ID.ge(0))
|
||||
// .and("bbb.id > ?",100)
|
||||
.orderBy(ACCOUNT.ID.desc())
|
||||
.limit(10, 10);
|
||||
|
||||
String mysqlSql = new CommonsDialectImpl().forSelectListByQuery(queryWrapper);
|
||||
System.out.println(">>>>> mysql: \n" + mysqlSql);
|
||||
System.out.println(">>>>> mysql: \n" + Arrays.toString(CPI.getValueArray(queryWrapper)));
|
||||
|
||||
// String oracleSql = new OracleDialect().forSelectListByQuery(CPI.getQueryTable(queryWrapper).getName(), queryWrapper);
|
||||
// System.out.println(">>>>> oracle: " + oracleSql);
|
||||
//
|
||||
// String informixSql = new InformixDialect().forSelectListByQuery(CPI.getQueryTable(queryWrapper).getName(), queryWrapper);
|
||||
// System.out.println(">>>>> informix: " + informixSql);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.mybatisflex.test;
|
||||
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import com.mybatisflex.core.enums.KeyType;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Table("tb_article")
|
||||
public class Article {
|
||||
|
||||
@Id(keyType = KeyType.Auto)
|
||||
private Long id;
|
||||
|
||||
private Long accountId;
|
||||
|
||||
private String title;
|
||||
|
||||
@Column(isLarge = true)
|
||||
private String content;
|
||||
|
||||
@Column(insert = "now()")
|
||||
private Date created;
|
||||
|
||||
@Column(update = "now()")
|
||||
private Date modified;
|
||||
|
||||
}
|
||||
67
mybatis-flex-spring-boot-starter/pom.xml
Normal file
67
mybatis-flex-spring-boot-starter/pom.xml
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.0.0-beta.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-spring</artifactId>
|
||||
<version>1.0.0-beta.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mybatis.scripting</groupId>
|
||||
<artifactId>mybatis-freemarker</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mybatis.scripting</groupId>
|
||||
<artifactId>mybatis-velocity</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mybatis.scripting</groupId>
|
||||
<artifactId>mybatis-thymeleaf</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>
|
||||
*.properties
|
||||
</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.spring.boot;
|
||||
|
||||
import com.mybatisflex.core.mybatis.FlexConfiguration;
|
||||
|
||||
/**
|
||||
* 为 FlexConfiguration 做自定义的配置支持 {@link FlexConfiguration}
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ConfigurationCustomizer {
|
||||
|
||||
void customize(FlexConfiguration configuration);
|
||||
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.spring.boot;
|
||||
|
||||
import com.mybatisflex.core.row.Db;
|
||||
import com.mybatisflex.spring.SpringRowSessionManager;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@ConditionalOnClass(Db.class)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureAfter({MybatisFlexAutoConfiguration.class})
|
||||
public class DbAutoConfiguration {
|
||||
|
||||
public DbAutoConfiguration() {
|
||||
Db.invoker().setRowSessionManager(new SpringRowSessionManager());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,325 @@
|
||||
/**
|
||||
* 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.spring.boot;
|
||||
|
||||
import com.mybatisflex.core.mybatis.FlexConfiguration;
|
||||
import com.mybatisflex.spring.FlexSqlSessionFactoryBean;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.mapping.DatabaseIdProvider;
|
||||
import org.apache.ibatis.plugin.Interceptor;
|
||||
import org.apache.ibatis.scripting.LanguageDriver;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
import org.mybatis.spring.SqlSessionFactoryBean;
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.mybatis.spring.mapper.MapperFactoryBean;
|
||||
import org.mybatis.spring.mapper.MapperScannerConfigurer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.beans.factory.*;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
/**
|
||||
* Mybatis-Flex 的核心配置
|
||||
* <p>
|
||||
* <p>
|
||||
* 参考 {@link <a href="https://github.com/mybatis/spring-boot-starter/blob/master/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.java">
|
||||
* https://github.com/mybatis/spring-boot-starter/blob/master/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.java</a>}
|
||||
* <p>
|
||||
* 为 Mybatis-Flex 开启自动配置功能,主要修改一下几个方面:
|
||||
* <p>
|
||||
* 1、替换配置为 mybatis-flex 的配置前缀
|
||||
* 2、修改 SqlSessionFactory 为 FlexSqlSessionFactoryBean
|
||||
* 3、修改 Configuration 为 FlexConfiguration
|
||||
*/
|
||||
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
|
||||
@ConditionalOnSingleCandidate(DataSource.class)
|
||||
@EnableConfigurationProperties(MybatisFlexProperties.class)
|
||||
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
|
||||
public class MybatisFlexAutoConfiguration implements InitializingBean {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MybatisFlexAutoConfiguration.class);
|
||||
|
||||
private final MybatisFlexProperties properties;
|
||||
|
||||
private final Interceptor[] interceptors;
|
||||
|
||||
private final TypeHandler[] typeHandlers;
|
||||
|
||||
private final LanguageDriver[] languageDrivers;
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private final DatabaseIdProvider databaseIdProvider;
|
||||
|
||||
private final List<ConfigurationCustomizer> configurationCustomizers;
|
||||
|
||||
private final List<SqlSessionFactoryBeanCustomizer> sqlSessionFactoryBeanCustomizers;
|
||||
|
||||
public MybatisFlexAutoConfiguration(MybatisFlexProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
|
||||
ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,
|
||||
ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
|
||||
ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,
|
||||
ObjectProvider<List<SqlSessionFactoryBeanCustomizer>> sqlSessionFactoryBeanCustomizers) {
|
||||
this.properties = properties;
|
||||
this.interceptors = interceptorsProvider.getIfAvailable();
|
||||
this.typeHandlers = typeHandlersProvider.getIfAvailable();
|
||||
this.languageDrivers = languageDriversProvider.getIfAvailable();
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
|
||||
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
|
||||
this.sqlSessionFactoryBeanCustomizers = sqlSessionFactoryBeanCustomizers.getIfAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
checkConfigFileExists();
|
||||
}
|
||||
|
||||
private void checkConfigFileExists() {
|
||||
if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
|
||||
Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
|
||||
Assert.state(resource.exists(),
|
||||
"Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
|
||||
// SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
|
||||
SqlSessionFactoryBean factory = new FlexSqlSessionFactoryBean();
|
||||
factory.setDataSource(dataSource);
|
||||
if (properties.getConfiguration() == null || properties.getConfiguration().getVfsImpl() == null) {
|
||||
factory.setVfs(SpringBootVFS.class);
|
||||
}
|
||||
if (StringUtils.hasText(this.properties.getConfigLocation())) {
|
||||
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
|
||||
}
|
||||
applyConfiguration(factory);
|
||||
if (this.properties.getConfigurationProperties() != null) {
|
||||
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(this.interceptors)) {
|
||||
factory.setPlugins(this.interceptors);
|
||||
}
|
||||
if (this.databaseIdProvider != null) {
|
||||
factory.setDatabaseIdProvider(this.databaseIdProvider);
|
||||
}
|
||||
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
|
||||
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
|
||||
}
|
||||
if (this.properties.getTypeAliasesSuperType() != null) {
|
||||
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
|
||||
}
|
||||
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
|
||||
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(this.typeHandlers)) {
|
||||
factory.setTypeHandlers(this.typeHandlers);
|
||||
}
|
||||
Resource[] mapperLocations = this.properties.resolveMapperLocations();
|
||||
if (!ObjectUtils.isEmpty(mapperLocations)) {
|
||||
factory.setMapperLocations(mapperLocations);
|
||||
}
|
||||
Set<String> factoryPropertyNames = Stream
|
||||
.of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
|
||||
.collect(Collectors.toSet());
|
||||
Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
|
||||
if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
|
||||
// Need to mybatis-spring 2.0.2+
|
||||
factory.setScriptingLanguageDrivers(this.languageDrivers);
|
||||
if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
|
||||
defaultLanguageDriver = this.languageDrivers[0].getClass();
|
||||
}
|
||||
}
|
||||
if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
|
||||
// Need to mybatis-spring 2.0.2+
|
||||
factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
|
||||
}
|
||||
applySqlSessionFactoryBeanCustomizers(factory);
|
||||
return factory.getObject();
|
||||
}
|
||||
|
||||
private void applyConfiguration(SqlSessionFactoryBean factory) {
|
||||
MybatisFlexProperties.CoreConfiguration coreConfiguration = this.properties.getConfiguration();
|
||||
// Configuration configuration = null;
|
||||
FlexConfiguration configuration = null;
|
||||
if (coreConfiguration != null || !StringUtils.hasText(this.properties.getConfigLocation())) {
|
||||
configuration = new FlexConfiguration();
|
||||
}
|
||||
if (configuration != null && coreConfiguration != null) {
|
||||
coreConfiguration.applyTo(configuration);
|
||||
}
|
||||
if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
|
||||
for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
|
||||
customizer.customize(configuration);
|
||||
}
|
||||
}
|
||||
factory.setConfiguration(configuration);
|
||||
}
|
||||
|
||||
private void applySqlSessionFactoryBeanCustomizers(SqlSessionFactoryBean factory) {
|
||||
if (!CollectionUtils.isEmpty(this.sqlSessionFactoryBeanCustomizers)) {
|
||||
for (SqlSessionFactoryBeanCustomizer customizer : this.sqlSessionFactoryBeanCustomizers) {
|
||||
customizer.customize(factory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
|
||||
ExecutorType executorType = this.properties.getExecutorType();
|
||||
if (executorType != null) {
|
||||
return new SqlSessionTemplate(sqlSessionFactory, executorType);
|
||||
} else {
|
||||
return new SqlSessionTemplate(sqlSessionFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will just scan the same base package as Spring Boot does. If you want more power, you can explicitly use
|
||||
* {@link org.mybatis.spring.annotation.MapperScan} but this will get typed mappers working correctly, out-of-the-box,
|
||||
* similar to using Spring Data JPA repositories.
|
||||
*/
|
||||
public static class AutoConfiguredMapperScannerRegistrar
|
||||
implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
private Environment environment;
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
|
||||
if (!AutoConfigurationPackages.has(this.beanFactory)) {
|
||||
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Searching for mappers annotated with @Mapper");
|
||||
|
||||
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
|
||||
if (logger.isDebugEnabled()) {
|
||||
packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
|
||||
builder.addPropertyValue("processPropertyPlaceHolders", true);
|
||||
builder.addPropertyValue("annotationClass", Mapper.class);
|
||||
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
|
||||
BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
|
||||
Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)
|
||||
.collect(Collectors.toSet());
|
||||
if (propertyNames.contains("lazyInitialization")) {
|
||||
// Need to mybatis-spring 2.0.2+
|
||||
builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
|
||||
}
|
||||
if (propertyNames.contains("defaultScope")) {
|
||||
// Need to mybatis-spring 2.0.6+
|
||||
builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
|
||||
}
|
||||
|
||||
// for spring-native
|
||||
boolean injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class,
|
||||
Boolean.TRUE);
|
||||
if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) {
|
||||
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;
|
||||
Optional<String> sqlSessionTemplateBeanName = Optional
|
||||
.ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));
|
||||
Optional<String> sqlSessionFactoryBeanName = Optional
|
||||
.ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));
|
||||
if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) {
|
||||
builder.addPropertyValue("sqlSessionTemplateBeanName",
|
||||
sqlSessionTemplateBeanName.orElse("sqlSessionTemplate"));
|
||||
} else {
|
||||
builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.get());
|
||||
}
|
||||
}
|
||||
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
private String getBeanNameForType(Class<?> type, ListableBeanFactory factory) {
|
||||
String[] beanNames = factory.getBeanNamesForType(type);
|
||||
return beanNames.length > 0 ? beanNames[0] : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If mapper registering configuration or mapper scanning configuration not present, this configuration allow to scan
|
||||
* mappers based on the same component-scanning path as Spring Boot itself.
|
||||
*/
|
||||
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
|
||||
@Import(AutoConfiguredMapperScannerRegistrar.class)
|
||||
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
|
||||
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
logger.debug(
|
||||
"Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.spring.boot;
|
||||
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDependsOnDatabaseInitializationDetector;
|
||||
import org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link DependsOnDatabaseInitializationDetector} for Mybatis-Flex.
|
||||
*/
|
||||
class MybatisFlexDependsOnDatabaseInitializationDetector
|
||||
extends AbstractBeansOfTypeDependsOnDatabaseInitializationDetector {
|
||||
|
||||
@Override
|
||||
protected Set<Class<?>> getDependsOnDatabaseInitializationBeanTypes() {
|
||||
return Collections.singleton(SqlSessionTemplate.class);
|
||||
}
|
||||
|
||||
}
|
||||
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