feat: init the project
This commit is contained in:
commit
bd1dfdca65
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
target
|
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.
|
107
README.md
Normal file
107
README.md
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
# fluent-sql
|
||||||
|
|
||||||
|
基于Fluent Api实现的SQL构建器,秒杀mybatis plus的存在,易用性的API让你爽到飞起。
|
||||||
|
|
||||||
|
## 特性介绍
|
||||||
|
|
||||||
|
1. 实现了SQL底层的语法解析,创新使用片段的构建模式,忽略嵌套层级任意嵌套
|
||||||
|
2. 高质量的代码,没有一点点冗余的设计,为您的代码保驾护航
|
||||||
|
3. 天生自带防SQL注入和条件空策略解析,后续会增加更加精细的配置
|
||||||
|
4. 支持任意多表的关联查询和数据绑定
|
||||||
|
5. 支持返回实体映射,基于注解式开发,更解耦,更面向对象
|
||||||
|
6. 智能别名策略,写查询再也不用担心多张表的别名问题,代码简介易懂,用java跟sql体验直接拉满
|
||||||
|
7. 高精度api控制,sql构建每个步骤严格把关,保证输入一个api立即能写出来接下来的步骤还不出错
|
||||||
|
|
||||||
|
## 快速使用
|
||||||
|
|
||||||
|
本小组件主要解决的是sql的书写问题,旨在用更加优雅的方式实现sql,并且不用再担心数据库方言(SQL Dialect)
|
||||||
|
变化导致的频繁变更SQL问题。
|
||||||
|
|
||||||
|
如果要实现下面一段SQL
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT t1.`id` AS `id`,
|
||||||
|
t1.`name` AS `name`,
|
||||||
|
t1.`identifier` AS `identifier`,
|
||||||
|
`max_tenant` AS `maxTenant`,
|
||||||
|
t2.`related_id` AS `relatedId`
|
||||||
|
FROM saas_tenant `t1`
|
||||||
|
INNER JOIN saas_quota `t2` ON t2.`related_id` = t1.`id`
|
||||||
|
AND t1.`identifier` = ?
|
||||||
|
WHERE t1.`id` = ?
|
||||||
|
AND t1.`name` LIKE CONCAT(%,?,%)
|
||||||
|
AND t2.`related_id` IN (?, ?)
|
||||||
|
AND t2.`related_type` = ?
|
||||||
|
ORDER BY t1.`create_time` DESC,
|
||||||
|
t1.`id` ASC
|
||||||
|
```
|
||||||
|
|
||||||
|
您只需要写以下java代码:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class TestSql {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行sql测试
|
||||||
|
* @return 最终组装的实体
|
||||||
|
*/
|
||||||
|
public TenantContext executeSql() {
|
||||||
|
// 查询开始
|
||||||
|
return select(
|
||||||
|
// 某张表的几个字段
|
||||||
|
composite(SaasTenant::getId, SaasTenant::getName, SaasTenant::getIdentifier),
|
||||||
|
// 某张表的全量字段
|
||||||
|
all(SaasProperties.class),
|
||||||
|
// 其他表的字段
|
||||||
|
composite(SaasQuota::getId, SaasQuota::getRelatedType),
|
||||||
|
// 指定别名的字段
|
||||||
|
composite(SaasQuota::getRelatedId, "relatedOtherId"))
|
||||||
|
.from(SaasTenant.class)
|
||||||
|
.join(SaasQuota.class).on(where(SaasQuota::getRelatedId).eq(SaasTenant::getId)
|
||||||
|
.and(SaasTenant::getIdentifier).eq(5))
|
||||||
|
.join(SaasProperties.class)
|
||||||
|
.then()
|
||||||
|
.matching(where(SaasTenant::getId).eq("1")
|
||||||
|
.and(SaasTenant::getName).like("王大锤")
|
||||||
|
.and(SaasQuota::getRelatedId).in(Arrays.asList("5", "10"))
|
||||||
|
.and(SaasQuota::getRelatedType).eq(SaasQuota.RelatedType.TENANT)
|
||||||
|
)
|
||||||
|
.order(by(SaasTenant::getCreateTime).desc(), by(SaasTenant::getId).asc())
|
||||||
|
.one(TenantContext.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常规步骤
|
||||||
|
|
||||||
|
建议使用静态导入,美观代码,如下:
|
||||||
|
|
||||||
|
1. SQL.select => select
|
||||||
|
2. SelectComposite.composite => composite
|
||||||
|
3. SelectComposite.all => all
|
||||||
|
4. Order.by => by
|
||||||
|
5. Query.where => where
|
||||||
|
|
||||||
|
为了方便演示,我们下面的代码都基于静态导入函数:
|
||||||
|
|
||||||
|
1. 绑定您执行sql的数据源。如下:
|
||||||
|
|
||||||
|
```java
|
||||||
|
class SQLConfig {
|
||||||
|
|
||||||
|
void doConfig() {
|
||||||
|
// 创建或者获取您的数据源
|
||||||
|
DataSource = createDataSource(...)
|
||||||
|
// 基于spring jdbc template实例化
|
||||||
|
SQL.bind(new JdbcTemplate(dataSource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 写一个结果对象Vo用来接取执行结果,如快速开始中的`TenantContext`
|
||||||
|
3. 用java写sql,然后执行`.one()`或者`.list()`返回一条或者多条。
|
||||||
|
4. 查询单表所有字段,请使用`select().from(TableClass.class)`
|
||||||
|
5. 查询多表中某个单表的所有字段,请使用`select(all(TableClass.class)).from(TableClass.class).join(Other.class).then()`
|
||||||
|
6.
|
||||||
|
多张表中查询某些字段,并使用别名,请参考`select(composite(TableA::getName, "nameA"), composite(TableB::getName, "nameB")).from(TableA.class).join(TableB.class)...`
|
37
fluent-sql-core/pom.xml
Normal file
37
fluent-sql-core/pom.xml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?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>fluent-sql</artifactId>
|
||||||
|
<groupId>group.flyfish.framework</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>fluent-sql-core</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-jdbc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.persistence</groupId>
|
||||||
|
<artifactId>persistence-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@ -0,0 +1,19 @@
|
|||||||
|
package group.flyfish.fluent.binding;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结果映射指定别名
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface Alias {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 别名
|
||||||
|
*/
|
||||||
|
String value();
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package group.flyfish.fluent.binding;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* json注入
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface JSONInject {
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package group.flyfish.fluent.chain;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序链式声明
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface Order extends SQLSegment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以字段排序
|
||||||
|
*
|
||||||
|
* @param field 字段
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @return 链式调用
|
||||||
|
*/
|
||||||
|
static <T> Order by(SFunction<T, ?> field) {
|
||||||
|
return new OrderImpl(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
Order asc();
|
||||||
|
|
||||||
|
Order desc();
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package group.flyfish.fluent.chain;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序支持
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
final class OrderImpl implements Order, SQLSegment {
|
||||||
|
|
||||||
|
private final SFunction<?, ?> field;
|
||||||
|
|
||||||
|
private String order = "asc";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OrderImpl asc() {
|
||||||
|
order = "asc";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OrderImpl desc() {
|
||||||
|
order = "desc";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 得到sql片段
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return String.join(" ", field.getName(), order);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package group.flyfish.fluent.chain;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.common.PreSqlChain;
|
||||||
|
import group.flyfish.fluent.chain.select.SelectComposite;
|
||||||
|
import group.flyfish.fluent.update.Update;
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
import org.springframework.jdbc.core.JdbcOperations;
|
||||||
|
|
||||||
|
import static group.flyfish.fluent.utils.sql.SqlNameUtils.cast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 链式查询入口
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface SQL {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询起手式
|
||||||
|
*
|
||||||
|
* @return 自身
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
static <T> PreSqlChain select(SFunction<T, ?>... fields) {
|
||||||
|
return SQLFactory.produce().select(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询起手式
|
||||||
|
*
|
||||||
|
* @return 自身
|
||||||
|
*/
|
||||||
|
static PreSqlChain select(SelectComposite<?>... composites) {
|
||||||
|
return SQLFactory.produce().select(SelectComposite.combine(composites));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询起手式,查询全部字段
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
static PreSqlChain select() {
|
||||||
|
return SQLFactory.produce().select(cast(new SFunction[]{}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新表起手
|
||||||
|
*
|
||||||
|
* @param clazz 表
|
||||||
|
* @return 更新链式
|
||||||
|
*/
|
||||||
|
static Update update(Class<?> clazz) {
|
||||||
|
return SQLFactory.produce().update(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定数据源上下文,基于jdbc template
|
||||||
|
*
|
||||||
|
* @param operations jdbc操作
|
||||||
|
*/
|
||||||
|
static void bind(JdbcOperations operations) {
|
||||||
|
SQLImpl.bind(operations);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package group.flyfish.fluent.chain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sql工厂
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
* 普通静态工厂,用于生产实现实例
|
||||||
|
*/
|
||||||
|
public interface SQLFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生产实例
|
||||||
|
*
|
||||||
|
* @return sql操作
|
||||||
|
*/
|
||||||
|
static SQLOperations produce() {
|
||||||
|
return new SQLImpl();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,255 @@
|
|||||||
|
package group.flyfish.fluent.chain;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.common.AfterJoinSqlChain;
|
||||||
|
import group.flyfish.fluent.chain.common.HandleSqlChain;
|
||||||
|
import group.flyfish.fluent.chain.common.PreSqlChain;
|
||||||
|
import group.flyfish.fluent.chain.select.AfterOrderSqlChain;
|
||||||
|
import group.flyfish.fluent.chain.select.AfterWhereSqlChain;
|
||||||
|
import group.flyfish.fluent.chain.update.AfterSetSqlChain;
|
||||||
|
import group.flyfish.fluent.mapping.SQLMappedRowMapper;
|
||||||
|
import group.flyfish.fluent.query.JoinCandidate;
|
||||||
|
import group.flyfish.fluent.query.Parameterized;
|
||||||
|
import group.flyfish.fluent.query.Query;
|
||||||
|
import group.flyfish.fluent.update.Update;
|
||||||
|
import group.flyfish.fluent.update.UpdateImpl;
|
||||||
|
import group.flyfish.fluent.utils.context.AliasComposite;
|
||||||
|
import group.flyfish.fluent.utils.data.ParameterUtils;
|
||||||
|
import group.flyfish.fluent.utils.sql.ConcatSegment;
|
||||||
|
import group.flyfish.fluent.utils.sql.EntityNameUtils;
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
import group.flyfish.fluent.utils.sql.SqlNameUtils;
|
||||||
|
import org.springframework.dao.EmptyResultDataAccessException;
|
||||||
|
import org.springframework.jdbc.core.JdbcOperations;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询工具类
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
final class SQLImpl extends ConcatSegment<SQLImpl> implements SQLOperations, PreSqlChain, HandleSqlChain, AfterJoinSqlChain, AfterSetSqlChain {
|
||||||
|
|
||||||
|
// 共享的操作
|
||||||
|
private static JdbcOperations SHARED_OPERATIONS;
|
||||||
|
// 参数map,有序
|
||||||
|
private final List<Object> parameters = new ArrayList<>();
|
||||||
|
|
||||||
|
// 调试标识
|
||||||
|
private final boolean debug = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于jdbc template
|
||||||
|
*
|
||||||
|
* @param operations jdbc操作
|
||||||
|
*/
|
||||||
|
public static void bind(JdbcOperations operations) {
|
||||||
|
SHARED_OPERATIONS = operations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询起手
|
||||||
|
*
|
||||||
|
* @param fields 字段列表,不传代表所有字段
|
||||||
|
* @return 预查询链
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
@Override
|
||||||
|
public final <T> PreSqlChain select(SFunction<T, ?>... fields) {
|
||||||
|
String linker = !segments.isEmpty() ? "," : "SELECT";
|
||||||
|
if (fields != null && fields.length != 0) {
|
||||||
|
return this.concat(linker)
|
||||||
|
.concat(() -> Arrays.stream(fields).map(SFunction::getSelect).collect(Collectors.joining(",")));
|
||||||
|
}
|
||||||
|
return this.concat(linker).concat("*");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新起手
|
||||||
|
*
|
||||||
|
* @param clazz 具体表
|
||||||
|
* @return 链式调用
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Update update(Class<T> clazz) {
|
||||||
|
return new UpdateImpl(update -> {
|
||||||
|
if (withoutParameter(update)) return this;
|
||||||
|
return this.concat("UPDATE")
|
||||||
|
.concat(() -> EntityNameUtils.getTableName(clazz))
|
||||||
|
.concat("SET")
|
||||||
|
.concat(update);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从表里查
|
||||||
|
*
|
||||||
|
* @param type 类型
|
||||||
|
* @return 链式调用
|
||||||
|
*/
|
||||||
|
public HandleSqlChain from(Class<?> type) {
|
||||||
|
return from(type, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从指定表查询,附加别名
|
||||||
|
* 该接口适用于同一张表多次from的情况,可以从自表进行多次查询
|
||||||
|
* 大部分情况下,您都不需要指定别名
|
||||||
|
*
|
||||||
|
* @param type 类型
|
||||||
|
* @param alias 别名
|
||||||
|
* @return 处理环节
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public HandleSqlChain from(Class<?> type, String alias) {
|
||||||
|
String mapped = AliasComposite.add(type, alias);
|
||||||
|
return concat("FROM")
|
||||||
|
.concat(() -> EntityNameUtils.getTableName(type))
|
||||||
|
.concat(() -> SqlNameUtils.wrap(mapped));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全量的join连接支持
|
||||||
|
*
|
||||||
|
* @param type 连接类型
|
||||||
|
* @param clazz 目标表
|
||||||
|
* @param alias 别名
|
||||||
|
* @return join后的操作
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AfterJoinSqlChain join(JoinCandidate type, Class<?> clazz, String alias) {
|
||||||
|
String mapped = AliasComposite.add(clazz, alias);
|
||||||
|
return concat(type)
|
||||||
|
.concat(() -> EntityNameUtils.getTableName(clazz))
|
||||||
|
.concat(() -> SqlNameUtils.wrap(mapped));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接条件
|
||||||
|
*
|
||||||
|
* @param query 查询
|
||||||
|
* @return 处理链
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public HandleSqlChain on(Query query) {
|
||||||
|
if (withoutParameter(query)) return this;
|
||||||
|
return concat("ON").concat(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不带连接条件
|
||||||
|
*
|
||||||
|
* @return 处理链
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public HandleSqlChain then() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接查询条件
|
||||||
|
*
|
||||||
|
* @param query 条件
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public AfterWhereSqlChain matching(Query query) {
|
||||||
|
if (withoutParameter(query)) return this;
|
||||||
|
return concat("WHERE").concat(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接排序条件
|
||||||
|
*
|
||||||
|
* @param orders 排序
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AfterOrderSqlChain order(Order... orders) {
|
||||||
|
if (null != orders && orders.length != 0) {
|
||||||
|
return concat("ORDER BY")
|
||||||
|
.concat(() -> Arrays.stream(orders).map(SQLSegment::get).collect(Collectors.joining(",")));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行并获取结果
|
||||||
|
*
|
||||||
|
* @param clazz 结果类
|
||||||
|
* @param <T> 泛型
|
||||||
|
*/
|
||||||
|
public <T> T one(Class<T> clazz) {
|
||||||
|
try {
|
||||||
|
return SHARED_OPERATIONS.queryForObject(sql().concat(" limit 1"),
|
||||||
|
new SQLMappedRowMapper<>(clazz), parsedParameters());
|
||||||
|
} catch (EmptyResultDataAccessException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行并获取多条结果
|
||||||
|
*
|
||||||
|
* @param clazz 结果类
|
||||||
|
* @return 结果列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> List<T> list(Class<T> clazz) {
|
||||||
|
return SHARED_OPERATIONS.query(sql(), new SQLMappedRowMapper<>(clazz), parsedParameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行并获取更新条数
|
||||||
|
*
|
||||||
|
* @return 更新条数
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int execute() {
|
||||||
|
return SHARED_OPERATIONS.update(sql(), parsedParameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建sql
|
||||||
|
*
|
||||||
|
* @return 构建结果
|
||||||
|
*/
|
||||||
|
private String sql() {
|
||||||
|
Assert.notNull(SHARED_OPERATIONS, "未指定执行数据源!");
|
||||||
|
String sql = segments.stream().map(SQLSegment::get).collect(Collectors.joining(" "));
|
||||||
|
if (debug) {
|
||||||
|
System.out.println("prepared sql: " + sql);
|
||||||
|
System.out.println("prepared args:" + parameters.stream().map(ParameterUtils::convert).map(String::valueOf)
|
||||||
|
.collect(Collectors.joining(",")));
|
||||||
|
}
|
||||||
|
AliasComposite.flush();
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析后的参数
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
private Object[] parsedParameters() {
|
||||||
|
return parameters.stream().map(ParameterUtils::convert).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 没有参数值
|
||||||
|
*
|
||||||
|
* @param params 参数
|
||||||
|
* @return 为true,代表没有参数,不添加该片段
|
||||||
|
*/
|
||||||
|
private boolean withoutParameter(Parameterized params) {
|
||||||
|
if (params.isEmpty() || null == params.getParameters()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
parameters.addAll(params.getParameters());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package group.flyfish.fluent.chain;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.common.PreSqlChain;
|
||||||
|
import group.flyfish.fluent.update.Update;
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQL操作
|
||||||
|
*/
|
||||||
|
public interface SQLOperations {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询起手
|
||||||
|
*
|
||||||
|
* @param fields 字段列表,不传代表所有字段
|
||||||
|
* @param <T> 实体泛型
|
||||||
|
* @return 预查询链
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
<T> PreSqlChain select(SFunction<T, ?>... fields);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新起手
|
||||||
|
*
|
||||||
|
* @param clazz 具体表
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @return 链式调用
|
||||||
|
*/
|
||||||
|
<T> Update update(Class<T> clazz);
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package group.flyfish.fluent.chain;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.utils.sql.SqlNameUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sql片段
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SQLSegment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 得到sql片段
|
||||||
|
*/
|
||||||
|
String get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型强转,请慎用,除非你知道真实类型
|
||||||
|
*
|
||||||
|
* @param value 任意类型值
|
||||||
|
* @param <T> 入参泛型
|
||||||
|
* @param <R> 出参泛型
|
||||||
|
* @return 转换类型值
|
||||||
|
*/
|
||||||
|
default <T, R> R cast(T value) {
|
||||||
|
return SqlNameUtils.cast(value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package group.flyfish.fluent.chain.common;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.query.Query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* join连接后可执行的操作
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface AfterJoinSqlChain {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接条件
|
||||||
|
*
|
||||||
|
* @param query 查询
|
||||||
|
* @return 处理链
|
||||||
|
*/
|
||||||
|
HandleSqlChain on(Query query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不带连接条件
|
||||||
|
*
|
||||||
|
* @return 处理链
|
||||||
|
*/
|
||||||
|
HandleSqlChain then();
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package group.flyfish.fluent.chain.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可执行的sql
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface ExecutableSql {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行并获取更新条数
|
||||||
|
*
|
||||||
|
* @return 更新条数
|
||||||
|
*/
|
||||||
|
int execute();
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package group.flyfish.fluent.chain.common;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.select.AfterWhereSqlChain;
|
||||||
|
import group.flyfish.fluent.query.Query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理阶段的sql链
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface HandleSqlChain extends JoinOperations, AfterWhereSqlChain {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接查询条件
|
||||||
|
*
|
||||||
|
* @param query 条件
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
AfterWhereSqlChain matching(Query query);
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package group.flyfish.fluent.chain.common;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.query.JoinCandidate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表连接操作
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface JoinOperations {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全量的join连接支持
|
||||||
|
*
|
||||||
|
* @param type 连接类型
|
||||||
|
* @param clazz 目标表
|
||||||
|
* @param alias 别名
|
||||||
|
* @return join后的操作
|
||||||
|
*/
|
||||||
|
AfterJoinSqlChain join(JoinCandidate type, Class<?> clazz, String alias);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用内连接连接其他表
|
||||||
|
*
|
||||||
|
* @param clazz 其他表实体
|
||||||
|
* @return join后的操作
|
||||||
|
*/
|
||||||
|
default AfterJoinSqlChain join(Class<?> clazz) {
|
||||||
|
return join(JoinCandidate.INNER_JOIN, clazz, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用内连接连接其他表
|
||||||
|
*
|
||||||
|
* @param clazz 其他表实体
|
||||||
|
* @param alias 别名
|
||||||
|
* @return join后的操作
|
||||||
|
*/
|
||||||
|
default AfterJoinSqlChain join(Class<?> clazz, String alias) {
|
||||||
|
return join(JoinCandidate.INNER_JOIN, clazz, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用内连接连接其他表
|
||||||
|
*
|
||||||
|
* @param clazz 其他表实体
|
||||||
|
* @return join后的操作
|
||||||
|
*/
|
||||||
|
default AfterJoinSqlChain leftJoin(Class<?> clazz) {
|
||||||
|
return join(JoinCandidate.LEFT_JOIN, clazz, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用内连接连接其他表
|
||||||
|
*
|
||||||
|
* @param clazz 其他表实体
|
||||||
|
* @param alias 别名
|
||||||
|
* @return join后的操作
|
||||||
|
*/
|
||||||
|
default AfterJoinSqlChain leftJoin(Class<?> clazz, String alias) {
|
||||||
|
return join(JoinCandidate.LEFT_JOIN, clazz, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用内连接连接其他表
|
||||||
|
*
|
||||||
|
* @param clazz 其他表实体
|
||||||
|
* @return join后的操作
|
||||||
|
*/
|
||||||
|
default AfterJoinSqlChain rightJoin(Class<?> clazz) {
|
||||||
|
return join(JoinCandidate.RIGHT_JOIN, clazz, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用内连接连接其他表
|
||||||
|
*
|
||||||
|
* @param clazz 其他表实体
|
||||||
|
* @param alias 别名
|
||||||
|
* @return join后的操作
|
||||||
|
*/
|
||||||
|
default AfterJoinSqlChain rightJoin(Class<?> clazz, String alias) {
|
||||||
|
return join(JoinCandidate.RIGHT_JOIN, clazz, alias);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package group.flyfish.fluent.chain.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刚设置了查询字段后的链
|
||||||
|
*/
|
||||||
|
public interface PreSqlChain {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从指定表查询
|
||||||
|
*
|
||||||
|
* @param type 表实体
|
||||||
|
* @return 处理环节
|
||||||
|
*/
|
||||||
|
HandleSqlChain from(Class<?> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从指定表查询,附加别名
|
||||||
|
* 该接口适用于同一张表多次from的情况,可以从自表进行多次查询
|
||||||
|
* 大部分情况下,您都不需要指定别名
|
||||||
|
*
|
||||||
|
* @param type 类型
|
||||||
|
* @param alias 别名
|
||||||
|
* @return 处理环节
|
||||||
|
*/
|
||||||
|
HandleSqlChain from(Class<?> type, String alias);
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package group.flyfish.fluent.chain.select;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.common.ExecutableSql;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* order做完后支持的操作
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface AfterOrderSqlChain extends ExecutableSql {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行并获取结果
|
||||||
|
*
|
||||||
|
* @param clazz 结果类
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @return 单一结果值
|
||||||
|
*/
|
||||||
|
<T> T one(Class<T> clazz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行并获取多条结果
|
||||||
|
*
|
||||||
|
* @param clazz 结果类
|
||||||
|
* @param <T> 结果泛型
|
||||||
|
* @return 结果列表
|
||||||
|
*/
|
||||||
|
<T> List<T> list(Class<T> clazz);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package group.flyfish.fluent.chain.select;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.Order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* where条件后支持的操作
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface AfterWhereSqlChain extends AfterOrderSqlChain {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接排序条件
|
||||||
|
*
|
||||||
|
* @param orders 排序
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
AfterOrderSqlChain order(Order... orders);
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package group.flyfish.fluent.chain.select;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.utils.context.AliasComposite;
|
||||||
|
import group.flyfish.fluent.utils.sql.EntityNameUtils;
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
import group.flyfish.fluent.utils.sql.SqlNameUtils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择语句泛型包装
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SelectComposite<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于泛型的包装,以实体类为T,可以组合任意字段集合
|
||||||
|
*
|
||||||
|
* @param getter 字段属性getter
|
||||||
|
* @param <T> 实体泛型
|
||||||
|
* @return 包装集合
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
static <T> SelectComposite<T> composite(SFunction<T, Object>... getter) {
|
||||||
|
return () -> Arrays.stream(getter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于单字段composite,指定别名
|
||||||
|
*
|
||||||
|
* @param getter 字段属性getter
|
||||||
|
* @param alias 别名
|
||||||
|
* @param <T> 实体泛型
|
||||||
|
* @return 包装集合
|
||||||
|
*/
|
||||||
|
static <T> SelectComposite<T> composite(SFunction<T, Object> getter, String alias) {
|
||||||
|
AliasComposite.add(getter, alias);
|
||||||
|
return () -> Stream.of(getter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组合多个组合条件,并进行解包,忽略泛型
|
||||||
|
*
|
||||||
|
* @param composites 多个包
|
||||||
|
* @param <T> 泛型,任意返回
|
||||||
|
* @return 扁平化的字段列表
|
||||||
|
*/
|
||||||
|
static <T> T combine(SelectComposite<?>... composites) {
|
||||||
|
return SqlNameUtils.cast(Arrays.stream(composites).flatMap(SelectComposite::stream).toArray(SFunction[]::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询表下面的所有字段
|
||||||
|
*
|
||||||
|
* @param clazz 实体类
|
||||||
|
* @param <T> 实体类泛型
|
||||||
|
* @return 字段集合
|
||||||
|
*/
|
||||||
|
static <T> SelectComposite<T> all(Class<T> clazz) {
|
||||||
|
return () -> EntityNameUtils.getFields(clazz).entrySet().stream()
|
||||||
|
.map(entry -> new SFunction.StaticRef<>(clazz, entry.getKey(), entry.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回stream
|
||||||
|
*
|
||||||
|
* @return 结果流对象
|
||||||
|
*/
|
||||||
|
Stream<SFunction<T, Object>> stream();
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package group.flyfish.fluent.chain.update;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.common.ExecutableSql;
|
||||||
|
import group.flyfish.fluent.chain.common.PreSqlChain;
|
||||||
|
import group.flyfish.fluent.chain.select.AfterWhereSqlChain;
|
||||||
|
import group.flyfish.fluent.query.Query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set之后能干的事儿
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface AfterSetSqlChain extends PreSqlChain, ExecutableSql {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询条件
|
||||||
|
*
|
||||||
|
* @param query 查询
|
||||||
|
* @return 链式调用
|
||||||
|
*/
|
||||||
|
AfterWhereSqlChain matching(Query query);
|
||||||
|
}
|
@ -0,0 +1,362 @@
|
|||||||
|
package group.flyfish.fluent.mapping;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import group.flyfish.fluent.binding.Alias;
|
||||||
|
import group.flyfish.fluent.binding.JSONInject;
|
||||||
|
import group.flyfish.fluent.utils.data.ObjectMappers;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.*;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotations;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.support.DefaultConversionService;
|
||||||
|
import org.springframework.dao.DataRetrievalFailureException;
|
||||||
|
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||||
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
import org.springframework.jdbc.support.JdbcUtils;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.ResultSetMetaData;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于SQL映射的行映射器
|
||||||
|
*
|
||||||
|
* @param <T> 响应实体泛型
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class SQLMappedRowMapper<T> implements RowMapper<T> {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper = ObjectMappers.shared();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class we are mapping to.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private Class<T> mappedClass;
|
||||||
|
/**
|
||||||
|
* ConversionService for binding JDBC values to bean properties.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private ConversionService conversionService = DefaultConversionService.getSharedInstance();
|
||||||
|
/**
|
||||||
|
* Map of the fields we provide mapping for.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private Map<String, PropertyDescriptor> mappedFields;
|
||||||
|
/**
|
||||||
|
* Map of the fields which need json convert
|
||||||
|
*/
|
||||||
|
private Map<String, Class<?>> jsonFields;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@code BeanPropertyRowMapper}, accepting unpopulated
|
||||||
|
* properties in the target bean.
|
||||||
|
*
|
||||||
|
* @param mappedClass the class that each row should be mapped to
|
||||||
|
*/
|
||||||
|
public SQLMappedRowMapper(Class<T> mappedClass) {
|
||||||
|
initialize(mappedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static factory method to create a new {@code BeanPropertyRowMapper}.
|
||||||
|
*
|
||||||
|
* @param mappedClass the class that each row should be mapped to
|
||||||
|
* @see #newInstance(Class, ConversionService)
|
||||||
|
*/
|
||||||
|
public static <T> SQLMappedRowMapper<T> newInstance(Class<T> mappedClass) {
|
||||||
|
return new SQLMappedRowMapper<>(mappedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static factory method to create a new {@code BeanPropertyRowMapper}.
|
||||||
|
*
|
||||||
|
* @param mappedClass the class that each row should be mapped to
|
||||||
|
* @param conversionService the {@link ConversionService} for binding
|
||||||
|
* JDBC values to bean properties, or {@code null} for none
|
||||||
|
* @see #newInstance(Class)
|
||||||
|
* @see #setConversionService
|
||||||
|
* @since 5.2.3
|
||||||
|
*/
|
||||||
|
public static <T> SQLMappedRowMapper<T> newInstance(
|
||||||
|
Class<T> mappedClass, @Nullable ConversionService conversionService) {
|
||||||
|
|
||||||
|
SQLMappedRowMapper<T> rowMapper = newInstance(mappedClass);
|
||||||
|
rowMapper.setConversionService(conversionService);
|
||||||
|
return rowMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the class that we are mapping to.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public final Class<T> getMappedClass() {
|
||||||
|
return this.mappedClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the class that each row should be mapped to.
|
||||||
|
*/
|
||||||
|
public void setMappedClass(Class<T> mappedClass) {
|
||||||
|
if (this.mappedClass == null) {
|
||||||
|
initialize(mappedClass);
|
||||||
|
} else {
|
||||||
|
if (this.mappedClass != mappedClass) {
|
||||||
|
throw new InvalidDataAccessApiUsageException("The mapped class can not be reassigned to map to " +
|
||||||
|
mappedClass + " since it is already providing mapping for " + this.mappedClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a {@link ConversionService} for binding JDBC values to bean properties,
|
||||||
|
* or {@code null} if none.
|
||||||
|
*
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public ConversionService getConversionService() {
|
||||||
|
return this.conversionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a {@link ConversionService} for binding JDBC values to bean properties,
|
||||||
|
* or {@code null} for none.
|
||||||
|
* <p>Default is a {@link DefaultConversionService}, as of Spring 4.3. This
|
||||||
|
* provides support for {@code java.time} conversion and other special types.
|
||||||
|
*
|
||||||
|
* @see #initBeanWrapper(BeanWrapper)
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
public void setConversionService(@Nullable ConversionService conversionService) {
|
||||||
|
this.conversionService = conversionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the mapping meta-data for the given class.
|
||||||
|
*
|
||||||
|
* @param mappedClass the mapped class
|
||||||
|
*/
|
||||||
|
protected void initialize(Class<T> mappedClass) {
|
||||||
|
this.mappedClass = mappedClass;
|
||||||
|
this.mappedFields = new HashMap<>();
|
||||||
|
this.jsonFields = new HashMap<>();
|
||||||
|
|
||||||
|
Map<String, MergedAnnotations> fieldAnnotations = new HashMap<>();
|
||||||
|
ReflectionUtils.doWithFields(mappedClass, field -> fieldAnnotations.put(field.getName(), MergedAnnotations.from(field)));
|
||||||
|
|
||||||
|
for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(mappedClass)) {
|
||||||
|
if (pd.getWriteMethod() != null) {
|
||||||
|
MergedAnnotations annotations = fieldAnnotations.get(pd.getName());
|
||||||
|
String lowerCaseName;
|
||||||
|
if (annotations.isPresent(Alias.class)) {
|
||||||
|
String rawName = annotations.get(Alias.class).synthesize().value();
|
||||||
|
lowerCaseName = lowerCaseName(rawName.replace("_", ""));
|
||||||
|
} else {
|
||||||
|
lowerCaseName = lowerCaseName(pd.getName());
|
||||||
|
}
|
||||||
|
this.mappedFields.put(lowerCaseName, pd);
|
||||||
|
String underscoreName = underscoreName(pd.getName());
|
||||||
|
if (!lowerCaseName.equals(underscoreName)) {
|
||||||
|
this.mappedFields.put(underscoreName, pd);
|
||||||
|
}
|
||||||
|
// 添加json字段
|
||||||
|
if (annotations.isPresent(JSONInject.class)) {
|
||||||
|
this.jsonFields.put(pd.getName(), pd.getPropertyType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified property from the mapped fields.
|
||||||
|
*
|
||||||
|
* @param propertyName the property name (as used by property descriptors)
|
||||||
|
* @since 5.3.9
|
||||||
|
*/
|
||||||
|
protected void suppressProperty(String propertyName) {
|
||||||
|
if (this.mappedFields != null) {
|
||||||
|
this.mappedFields.remove(lowerCaseName(propertyName));
|
||||||
|
this.mappedFields.remove(underscoreName(propertyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the given name to lower case.
|
||||||
|
* By default, conversions will happen within the US locale.
|
||||||
|
*
|
||||||
|
* @param name the original name
|
||||||
|
* @return the converted name
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
protected String lowerCaseName(String name) {
|
||||||
|
return name.toLowerCase(Locale.US);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a name in camelCase to an underscored name in lower case.
|
||||||
|
* Any upper case letters are converted to lower case with a preceding underscore.
|
||||||
|
*
|
||||||
|
* @param name the original name
|
||||||
|
* @return the converted name
|
||||||
|
* @see #lowerCaseName
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
protected String underscoreName(String name) {
|
||||||
|
if (!StringUtils.hasLength(name)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
result.append(Character.toLowerCase(name.charAt(0)));
|
||||||
|
for (int i = 1; i < name.length(); i++) {
|
||||||
|
char c = name.charAt(i);
|
||||||
|
if (Character.isUpperCase(c)) {
|
||||||
|
result.append('_').append(Character.toLowerCase(c));
|
||||||
|
} else {
|
||||||
|
result.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the values for all columns in the current row.
|
||||||
|
* <p>Utilizes public setters and result set meta-data.
|
||||||
|
*
|
||||||
|
* @see java.sql.ResultSetMetaData
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
|
||||||
|
BeanWrapperImpl bw = new BeanWrapperImpl();
|
||||||
|
initBeanWrapper(bw);
|
||||||
|
|
||||||
|
T mappedObject = constructMappedInstance(rs, bw);
|
||||||
|
bw.setBeanInstance(mappedObject);
|
||||||
|
|
||||||
|
ResultSetMetaData rsmd = rs.getMetaData();
|
||||||
|
int columnCount = rsmd.getColumnCount();
|
||||||
|
|
||||||
|
for (int index = 1; index <= columnCount; index++) {
|
||||||
|
String column = JdbcUtils.lookupColumnName(rsmd, index);
|
||||||
|
String field = lowerCaseName(StringUtils.delete(column, " "));
|
||||||
|
PropertyDescriptor pd = (this.mappedFields != null ? this.mappedFields.get(field) : null);
|
||||||
|
if (pd != null) {
|
||||||
|
try {
|
||||||
|
Object value = getColumnValue(rs, index, pd);
|
||||||
|
if (rowNumber == 0 && log.isDebugEnabled()) {
|
||||||
|
log.debug("Mapping column '" + column + "' to property '" + pd.getName() +
|
||||||
|
"' of type '" + ClassUtils.getQualifiedName(pd.getPropertyType()) + "'");
|
||||||
|
}
|
||||||
|
if (jsonFields.containsKey(pd.getName())) {
|
||||||
|
value = convert(value, jsonFields.get(pd.getName()));
|
||||||
|
}
|
||||||
|
bw.setPropertyValue(pd.getName(), value);
|
||||||
|
} catch (NotWritablePropertyException ex) {
|
||||||
|
throw new DataRetrievalFailureException(
|
||||||
|
"Unable to map column '" + column + "' to property '" + pd.getName() + "'", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappedObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an instance of the mapped class for the current row.
|
||||||
|
*
|
||||||
|
* @param rs the ResultSet to map (pre-initialized for the current row)
|
||||||
|
* @param tc a TypeConverter with this RowMapper's conversion service
|
||||||
|
* @return a corresponding instance of the mapped class
|
||||||
|
* @throws SQLException if an SQLException is encountered
|
||||||
|
* @since 5.3
|
||||||
|
*/
|
||||||
|
protected T constructMappedInstance(ResultSet rs, TypeConverter tc) throws SQLException {
|
||||||
|
Assert.state(this.mappedClass != null, "Mapped class was not specified");
|
||||||
|
return BeanUtils.instantiateClass(this.mappedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the given BeanWrapper to be used for row mapping.
|
||||||
|
* To be called for each row.
|
||||||
|
* <p>The default implementation applies the configured {@link ConversionService},
|
||||||
|
* if any. Can be overridden in subclasses.
|
||||||
|
*
|
||||||
|
* @param bw the BeanWrapper to initialize
|
||||||
|
* @see #getConversionService()
|
||||||
|
* @see BeanWrapper#setConversionService
|
||||||
|
*/
|
||||||
|
protected void initBeanWrapper(BeanWrapper bw) {
|
||||||
|
ConversionService cs = getConversionService();
|
||||||
|
if (cs != null) {
|
||||||
|
bw.setConversionService(cs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a JDBC object value for the specified column.
|
||||||
|
* <p>The default implementation delegates to
|
||||||
|
* {@link #getColumnValue(ResultSet, int, Class)}.
|
||||||
|
*
|
||||||
|
* @param rs is the ResultSet holding the data
|
||||||
|
* @param index is the column index
|
||||||
|
* @param pd the bean property that each result object is expected to match
|
||||||
|
* @return the Object value
|
||||||
|
* @throws SQLException in case of extraction failure
|
||||||
|
* @see #getColumnValue(ResultSet, int, Class)
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
protected Object getColumnValue(ResultSet rs, int index, PropertyDescriptor pd) throws SQLException {
|
||||||
|
return JdbcUtils.getResultSetValue(rs, index, pd.getPropertyType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a JDBC object value for the specified column.
|
||||||
|
* <p>The default implementation calls
|
||||||
|
* {@link JdbcUtils#getResultSetValue(java.sql.ResultSet, int, Class)}.
|
||||||
|
* Subclasses may override this to check specific value types upfront,
|
||||||
|
* or to post-process values return from {@code getResultSetValue}.
|
||||||
|
*
|
||||||
|
* @param rs is the ResultSet holding the data
|
||||||
|
* @param index is the column index
|
||||||
|
* @param paramType the target parameter type
|
||||||
|
* @return the Object value
|
||||||
|
* @throws SQLException in case of extraction failure
|
||||||
|
* @see org.springframework.jdbc.support.JdbcUtils#getResultSetValue(java.sql.ResultSet, int, Class)
|
||||||
|
* @since 5.3
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
protected Object getColumnValue(ResultSet rs, int index, Class<?> paramType) throws SQLException {
|
||||||
|
return JdbcUtils.getResultSetValue(rs, index, paramType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换json对象
|
||||||
|
*
|
||||||
|
* @param type 目标类型
|
||||||
|
* @param value 值
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
private Object convert(Object value, Class<?> type) {
|
||||||
|
if (value instanceof String) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue((String) value, type);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.error("转换json为对象时出错!{}", e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package group.flyfish.fluent.query;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.SQLSegment;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接用的候选
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum ConcatCandidate implements SQLSegment {
|
||||||
|
|
||||||
|
AND("且"), OR("或");
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 得到sql片段
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return name();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package group.flyfish.fluent.query;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.SQLSegment;
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件sql链
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface Condition extends Parameterized, SQLSegment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等于条件
|
||||||
|
*
|
||||||
|
* @param value 值
|
||||||
|
* @return 查询链
|
||||||
|
*/
|
||||||
|
Query eq(Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等于另外一个字段
|
||||||
|
*
|
||||||
|
* @param ref 引用
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @return 查询链
|
||||||
|
*/
|
||||||
|
<T> Query eq(SFunction<T, ?> ref);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模糊查询
|
||||||
|
*
|
||||||
|
* @param pattern 关键字
|
||||||
|
* @return 查询链
|
||||||
|
*/
|
||||||
|
Query like(String pattern);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在集合内
|
||||||
|
*
|
||||||
|
* @param collection 任意内容集合
|
||||||
|
* @return 查询链
|
||||||
|
*/
|
||||||
|
Query in(Collection<?> collection);
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package group.flyfish.fluent.query;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.utils.text.TemplateCompiler;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询条件候选模板
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
enum ConditionCandidate {
|
||||||
|
|
||||||
|
EQ("字段等于值", "{column} = ?"),
|
||||||
|
NE("字段不等于值", "{column} != ?"),
|
||||||
|
GT("字段大于值", "{column} > ?"),
|
||||||
|
GTE("字段大于等于值", "{column} >= ?"),
|
||||||
|
LT("字段小于值", "{column} < ?"),
|
||||||
|
LTE("字段小于等于值", "{column} <= ?"),
|
||||||
|
LIKE("字段模糊匹配值", "{column} LIKE CONCAT('%', ?, '%')"),
|
||||||
|
LIKE_LEFT("字段匹配左半部分值", "{column} LIKE CONCAT(?, '%')"),
|
||||||
|
LIKE_RIGHT("字段匹配右半部分值", "{column} LIKE CONCAT('%', ?)"),
|
||||||
|
IN("字段在值列表内", "{column} IN ({?})", ConditionCandidate::multiple),
|
||||||
|
NIN("字段不在值列表内", "{column} NOT IN ({?))", ConditionCandidate::multiple),
|
||||||
|
NOT_NULL("字段不为空", "{column} IS NOT NULL"),
|
||||||
|
IS_NULL("字段为空", "{column} IS NULL"),
|
||||||
|
BETWEEN("字段介于列表下标0和1的值之间", "{column} BETWEEN ? and ?"),
|
||||||
|
DATE_GTE("日期字段大于值", "{column} > ?"),
|
||||||
|
DATE_LTE("日期字段小于值", "{column} < ?");
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final TemplateCompiler.DynamicValue template;
|
||||||
|
|
||||||
|
private final Function<Object, ?> mapper;
|
||||||
|
|
||||||
|
ConditionCandidate(String name, String template) {
|
||||||
|
this(name, template, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConditionCandidate(String name, String template, Function<Object, ?> mapper) {
|
||||||
|
this.name = name;
|
||||||
|
this.template = TemplateCompiler.compile(template);
|
||||||
|
this.mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String multiple(Object value) {
|
||||||
|
return value instanceof Collection ?
|
||||||
|
((Collection<?>) value).stream().map(v -> "?").collect(Collectors.joining(", ")) : "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编译并取得值
|
||||||
|
*
|
||||||
|
* @param field 字段
|
||||||
|
* @param value 值
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public String compile(String field, Object value) {
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("column", field);
|
||||||
|
params.put("value", value);
|
||||||
|
params.put("?", null != mapper ? mapper.apply(value) : "?");
|
||||||
|
return template.apply(params);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package group.flyfish.fluent.query;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.SQLSegment;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum JoinCandidate implements SQLSegment {
|
||||||
|
|
||||||
|
INNER_JOIN("内连接", "INNER JOIN"),
|
||||||
|
LEFT_JOIN("左连接", "LEFT JOIN"),
|
||||||
|
RIGHT_JOIN("右连接", "RIGHT JOIN");
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 得到sql片段
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package group.flyfish.fluent.query;
|
||||||
|
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表示带参数的类
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface Parameterized {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前对象包含的参数
|
||||||
|
* 返回空集合,代表不需要参数
|
||||||
|
* 返回null,代表参数为空,不处理该条件或查询
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Collection<Object> getParameters();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为空,参数为null则为空
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
default boolean isEmpty() {
|
||||||
|
return null == getParameters();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package group.flyfish.fluent.query;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.SQLSegment;
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询构建入口
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface Query extends Parameterized, SQLSegment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从where开始
|
||||||
|
*
|
||||||
|
* @param getter 字段lambda
|
||||||
|
* @param <T> 实体泛型
|
||||||
|
* @return 构建条件
|
||||||
|
*/
|
||||||
|
static <T> Condition where(SFunction<T, ?> getter) {
|
||||||
|
return new SimpleCondition(getter, new SimpleQuery());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以且连接下一个条件
|
||||||
|
*
|
||||||
|
* @param getter 字段lambda
|
||||||
|
* @return 构建操作
|
||||||
|
*/
|
||||||
|
<T> Condition and(SFunction<T, ?> getter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以且直接连接其他条件
|
||||||
|
*
|
||||||
|
* @param condition 其他条件
|
||||||
|
* @return 查询操作
|
||||||
|
*/
|
||||||
|
Query and(Condition condition);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以且嵌套其他查询条件
|
||||||
|
*
|
||||||
|
* @param query 查询条件
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
Query and(Query query);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以或连接下一个条件
|
||||||
|
*
|
||||||
|
* @param getter 字段lambda
|
||||||
|
* @return 构建操作
|
||||||
|
*/
|
||||||
|
<T> Condition or(SFunction<T, ?> getter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以或直接连接其他条件
|
||||||
|
*
|
||||||
|
* @param condition 其他条件
|
||||||
|
* @return 查询操作
|
||||||
|
*/
|
||||||
|
Query or(Condition condition);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以或嵌套其他查询条件
|
||||||
|
*
|
||||||
|
* @param query 查询条件
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
Query or(Query query);
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
package group.flyfish.fluent.query;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.utils.sql.EntityNameUtils;
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询条件
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
class SimpleCondition implements Condition {
|
||||||
|
|
||||||
|
private final SFunction<?, ?> target;
|
||||||
|
|
||||||
|
private final Function<Condition, Query> callback;
|
||||||
|
|
||||||
|
private Object value;
|
||||||
|
|
||||||
|
private ConditionCandidate candidate;
|
||||||
|
|
||||||
|
public SimpleCondition(SFunction<?, ?> target, SimpleQuery query) {
|
||||||
|
this(target, query::and);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleCondition(SFunction<?, ?> target, Function<Condition, Query> callback) {
|
||||||
|
this.target = target;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public Collection<Object> getParameters() {
|
||||||
|
if (ObjectUtils.isEmpty(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (value instanceof Collection) {
|
||||||
|
return cast(value);
|
||||||
|
}
|
||||||
|
if (value instanceof SFunction) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return Collections.singletonList(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 得到sql片段
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
String compiled = candidate.compile(target.getName(), value);
|
||||||
|
// 值属于引用时,替换参数为引用
|
||||||
|
if (value instanceof SFunction) {
|
||||||
|
return compiled.replace("?", EntityNameUtils.toName(cast(value)));
|
||||||
|
}
|
||||||
|
return compiled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等于条件
|
||||||
|
*
|
||||||
|
* @param value 值
|
||||||
|
* @return 查询链
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Query eq(Object value) {
|
||||||
|
this.value = value;
|
||||||
|
this.candidate = ConditionCandidate.EQ;
|
||||||
|
return callback.apply(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等于另外一个字段
|
||||||
|
*
|
||||||
|
* @param ref 引用
|
||||||
|
* @return 查询链
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Query eq(SFunction<T, ?> ref) {
|
||||||
|
this.value = ref;
|
||||||
|
this.candidate = ConditionCandidate.EQ;
|
||||||
|
return callback.apply(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模糊查询
|
||||||
|
*
|
||||||
|
* @param pattern 关键字
|
||||||
|
* @return 查询链
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Query like(String pattern) {
|
||||||
|
this.value = pattern;
|
||||||
|
this.candidate = ConditionCandidate.LIKE;
|
||||||
|
return callback.apply(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在集合内
|
||||||
|
*
|
||||||
|
* @param collection 任意内容集合
|
||||||
|
* @return 查询链
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Query in(Collection<?> collection) {
|
||||||
|
this.value = collection;
|
||||||
|
this.candidate = ConditionCandidate.IN;
|
||||||
|
return callback.apply(this);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,142 @@
|
|||||||
|
package group.flyfish.fluent.query;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.SQLSegment;
|
||||||
|
import group.flyfish.fluent.utils.sql.ConcatSegment;
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static group.flyfish.fluent.query.ConcatCandidate.AND;
|
||||||
|
import static group.flyfish.fluent.query.ConcatCandidate.OR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询构建器api
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
class SimpleQuery extends ConcatSegment<SimpleQuery> implements Query {
|
||||||
|
|
||||||
|
// 参数源
|
||||||
|
private final Collection<Object> parameters = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 得到sql片段
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return segments.stream().map(SQLSegment::get).collect(Collectors.joining(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取参数源
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public Collection<Object> getParameters() {
|
||||||
|
if (segments.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以与连接下一个条件
|
||||||
|
*
|
||||||
|
* @param getter 字段lambda
|
||||||
|
* @return 构建操作
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Condition and(SFunction<T, ?> getter) {
|
||||||
|
return new SimpleCondition(getter, this::and);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接连接其他条件
|
||||||
|
*
|
||||||
|
* @param condition 其他条件
|
||||||
|
* @return 查询操作
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Query and(Condition condition) {
|
||||||
|
if (condition.isEmpty()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
addParameters(condition);
|
||||||
|
return concat(AND).concat(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 嵌套其他查询条件
|
||||||
|
*
|
||||||
|
* @param query 查询条件
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Query and(Query query) {
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
addParameters(query);
|
||||||
|
return concat(AND).concat(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以或连接下一个条件
|
||||||
|
*
|
||||||
|
* @param getter 字段lambda
|
||||||
|
* @return 构建操作
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Condition or(SFunction<T, ?> getter) {
|
||||||
|
return new SimpleCondition(getter, this::or);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以或直接连接其他条件
|
||||||
|
*
|
||||||
|
* @param condition 其他条件
|
||||||
|
* @return 查询操作
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Query or(Condition condition) {
|
||||||
|
if (condition.isEmpty()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
addParameters(condition);
|
||||||
|
return concat(OR).concat(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以或嵌套其他查询条件
|
||||||
|
*
|
||||||
|
* @param query 查询条件
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Query or(Query query) {
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
addParameters(query);
|
||||||
|
return concat(OR).concat(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加参数
|
||||||
|
*
|
||||||
|
* @param parameterized 带参数的对象
|
||||||
|
*/
|
||||||
|
private void addParameters(Parameterized parameterized) {
|
||||||
|
Collection<Object> params = parameterized.getParameters();
|
||||||
|
if (!CollectionUtils.isEmpty(params)) {
|
||||||
|
// 优先拼接参数
|
||||||
|
parameters.addAll(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package group.flyfish.fluent.update;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.SQLSegment;
|
||||||
|
import group.flyfish.fluent.chain.update.AfterSetSqlChain;
|
||||||
|
import group.flyfish.fluent.query.Parameterized;
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新内容类
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface Update extends SQLSegment, Parameterized {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于对象设置所有同名值
|
||||||
|
*
|
||||||
|
* @param value 值
|
||||||
|
* @return 链式
|
||||||
|
*/
|
||||||
|
default Update setAll(Object value) {
|
||||||
|
throw new UnsupportedOperationException("当前未实现该操作!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置值,来自具体值
|
||||||
|
*
|
||||||
|
* @param target 目标字段
|
||||||
|
* @param value 具体值
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @return 链式
|
||||||
|
*/
|
||||||
|
<T> Update set(SFunction<T, ?> target, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置值,来自其他表字段
|
||||||
|
*
|
||||||
|
* @param target 目标字段
|
||||||
|
* @param source 源端字段
|
||||||
|
* @param <T> 目标泛型
|
||||||
|
* @param <V> 源端泛型
|
||||||
|
* @return 链式
|
||||||
|
*/
|
||||||
|
<T, V> Update set(SFunction<T, ?> target, SFunction<V, ?> source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接下来要做的事
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
AfterSetSqlChain then();
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
package group.flyfish.fluent.update;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.SQLSegment;
|
||||||
|
import group.flyfish.fluent.chain.update.AfterSetSqlChain;
|
||||||
|
import group.flyfish.fluent.utils.sql.ConcatSegment;
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新实现
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UpdateImpl implements Update {
|
||||||
|
|
||||||
|
private final Function<Update, AfterSetSqlChain> chain;
|
||||||
|
|
||||||
|
// 参数源
|
||||||
|
private final Collection<Object> parameters = new ArrayList<>();
|
||||||
|
|
||||||
|
// 片段
|
||||||
|
private final List<SQLSegment> segments = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置值,来自具体值
|
||||||
|
*
|
||||||
|
* @param target 目标字段
|
||||||
|
* @param value 具体值
|
||||||
|
* @return 链式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Update set(SFunction<T, ?> target, Object value) {
|
||||||
|
if (ObjectUtils.isEmpty(value)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
this.parameters.add(value);
|
||||||
|
segments.add(new UpdatePart().concat(target::getName).concat("=").concat("?"));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置值,来自其他表字段
|
||||||
|
*
|
||||||
|
* @param target 目标字段
|
||||||
|
* @param source 源端字段
|
||||||
|
* @return 链式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T, V> Update set(SFunction<T, ?> target, SFunction<V, ?> source) {
|
||||||
|
segments.add(new UpdatePart().concat(target::getName).concat("=").concat(source::getName));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接下来要做的事
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AfterSetSqlChain then() {
|
||||||
|
return chain.apply(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 得到sql片段
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return segments.stream().map(SQLSegment::get).collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前对象包含的参数
|
||||||
|
* 返回空集合,代表不需要参数
|
||||||
|
* 返回null,代表参数为空,不处理该条件或查询
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public Collection<Object> getParameters() {
|
||||||
|
if (segments.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内部的部分
|
||||||
|
*/
|
||||||
|
private static class UpdatePart extends ConcatSegment<UpdatePart> implements SQLSegment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 得到sql片段
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return this.segments.stream().map(SQLSegment::get).collect(Collectors.joining(" "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
fluent-sql-core/src/main/java/group/flyfish/fluent/utils/cache/LRUCache.java
vendored
Normal file
36
fluent-sql-core/src/main/java/group/flyfish/fluent/utils/cache/LRUCache.java
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package group.flyfish.fluent.utils.cache;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LRU (least recently used)最近最久未使用缓存<br>
|
||||||
|
* 根据使用时间来判定对象是否被持续缓存<br>
|
||||||
|
* 当对象被访问时放入缓存,当缓存满了,最久未被使用的对象将被移除。<br>
|
||||||
|
* 此缓存基于LinkedHashMap,因此当被缓存的对象每被访问一次,这个对象的key就到链表头部。<br>
|
||||||
|
* 这个算法简单并且非常快,他比FIFO有一个显著优势是经常使用的对象不太可能被移除缓存。<br>
|
||||||
|
* 缺点是当缓存满时,不能被很快的访问。
|
||||||
|
*
|
||||||
|
* @param <K> 键类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final int maxSize;
|
||||||
|
|
||||||
|
public LRUCache(int maxSize) {
|
||||||
|
this(maxSize, 16, 0.75f, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LRUCache(int maxSize, int initialCapacity, float loadFactor, boolean accessOrder) {
|
||||||
|
super(initialCapacity, loadFactor, accessOrder);
|
||||||
|
this.maxSize = maxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||||
|
return this.size() > this.maxSize;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,182 @@
|
|||||||
|
package group.flyfish.fluent.utils.context;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 别名管理器
|
||||||
|
* 因sql构建器运行时线程确定,故该类使用线程局部变量
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public final class AliasComposite {
|
||||||
|
|
||||||
|
private static final String PREFIX = "t";
|
||||||
|
|
||||||
|
// 表、列别名存储,本质上是一个简单map。线程局部缓存,阅后即焚
|
||||||
|
private static final ThreadLocal<AliasCache> ALIAS = new ThreadLocal<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加别名
|
||||||
|
*
|
||||||
|
* @param key 别名key
|
||||||
|
* @param alias 别名
|
||||||
|
*/
|
||||||
|
public static String add(Class<?> key, String alias) {
|
||||||
|
return sharedCache().add(key, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加别名
|
||||||
|
*
|
||||||
|
* @param key 别名key
|
||||||
|
* @param alias 别名
|
||||||
|
*/
|
||||||
|
public static <T> String add(SFunction<T, ?> key, String alias) {
|
||||||
|
return sharedCache().add(key, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断表是否有别名
|
||||||
|
*
|
||||||
|
* @param key 实体类
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
public static boolean has(Class<?> key) {
|
||||||
|
return sharedCache().has(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断表是否有别名
|
||||||
|
*
|
||||||
|
* @param key 实体类
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
public static <T> boolean has(SFunction<T, ?> key) {
|
||||||
|
return sharedCache().has(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取别名
|
||||||
|
*
|
||||||
|
* @param key 键
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static String get(Class<?> key) {
|
||||||
|
return sharedCache().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取别名
|
||||||
|
*
|
||||||
|
* @param key 键
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static <T> String get(SFunction<T, ?> key) {
|
||||||
|
return sharedCache().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空别名缓存
|
||||||
|
*/
|
||||||
|
public static void flush() {
|
||||||
|
ALIAS.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AliasCache sharedCache() {
|
||||||
|
AliasCache cache = ALIAS.get();
|
||||||
|
if (null == cache) {
|
||||||
|
cache = new AliasCache();
|
||||||
|
ALIAS.set(cache);
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AliasCache {
|
||||||
|
|
||||||
|
// 表别名缓存map
|
||||||
|
private final Map<Class<?>, String> instance = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// 表别名内置计数
|
||||||
|
private final AtomicInteger counter = new AtomicInteger(0);
|
||||||
|
|
||||||
|
// 列别名缓存map
|
||||||
|
private final Map<SFunction<?, ?>, String> columns = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加缓存,基本规则:
|
||||||
|
* 1. 新缓存指定手动别名,优先以手动别名为准
|
||||||
|
* 2. 未指定手动别名,且存在缓存的,不更新别名
|
||||||
|
*
|
||||||
|
* @param key 缓存key
|
||||||
|
* @param alias 缓存别名,可为空
|
||||||
|
* @return 返回缓存的别名
|
||||||
|
*/
|
||||||
|
public String add(Class<?> key, @Nullable String alias) {
|
||||||
|
if (StringUtils.hasText(alias)) {
|
||||||
|
instance.put(key, alias);
|
||||||
|
return alias;
|
||||||
|
} else {
|
||||||
|
return get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean has(Class<?> key) {
|
||||||
|
return instance.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get(Class<?> key) {
|
||||||
|
return instance.computeIfAbsent(key, this::generate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成key
|
||||||
|
*
|
||||||
|
* @param key 类型key
|
||||||
|
* @return 生成的key
|
||||||
|
*/
|
||||||
|
public String generate(Class<?> key) {
|
||||||
|
return PREFIX + counter.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否含有某个列引用的别名
|
||||||
|
*
|
||||||
|
* @param column 列引用
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
public <T> boolean has(SFunction<T, ?> column) {
|
||||||
|
return columns.containsKey(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列缓存
|
||||||
|
*
|
||||||
|
* @param column 列引用
|
||||||
|
* @param alias 别名
|
||||||
|
* @param <T> 实体泛型
|
||||||
|
*/
|
||||||
|
public <T> String add(SFunction<T, ?> column, String alias) {
|
||||||
|
columns.put(column, alias);
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取列引用所绑定的别名
|
||||||
|
*
|
||||||
|
* @param column 列引用
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @return 别名,可能为空
|
||||||
|
*/
|
||||||
|
public <T> String get(SFunction<T, ?> column) {
|
||||||
|
return columns.get(column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package group.flyfish.fluent.utils.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目唯一的jackson工具
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public final class ObjectMappers {
|
||||||
|
|
||||||
|
private static final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
static {
|
||||||
|
objectMapper = new ObjectMapper();
|
||||||
|
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectMappers() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ObjectMapper shared() {
|
||||||
|
return objectMapper;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package group.flyfish.fluent.utils.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数工具类
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public final class ParameterUtils {
|
||||||
|
|
||||||
|
public static Object convert(Object value) {
|
||||||
|
if (null == value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (value instanceof Enum) {
|
||||||
|
return String.valueOf(value);
|
||||||
|
}
|
||||||
|
if (BeanUtils.isSimpleProperty(value.getClass())) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return ObjectMappers.shared().writeValueAsString(value);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new InvalidParameterException("不是一个json数据,或者是未识别的类!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package group.flyfish.fluent.utils.sql;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.SQLSegment;
|
||||||
|
import group.flyfish.fluent.query.ConcatCandidate;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可连接的片段
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public abstract class ConcatSegment<T extends ConcatSegment<T>> {
|
||||||
|
|
||||||
|
// 处理链集合
|
||||||
|
protected final List<SQLSegment> segments = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接sql片段
|
||||||
|
*
|
||||||
|
* @param segment 片段
|
||||||
|
* @return 链式调用
|
||||||
|
*/
|
||||||
|
public T concat(SQLSegment segment) {
|
||||||
|
if (segments.isEmpty() && segment instanceof ConcatCandidate) {
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
this.segments.add(segment);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接静态sql片段
|
||||||
|
*
|
||||||
|
* @param content 内容
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public T concat(String content) {
|
||||||
|
this.segments.add(new StaticSegment(content));
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private T self() {
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
private static class StaticSegment implements SQLSegment {
|
||||||
|
|
||||||
|
private final String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 得到sql片段
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,157 @@
|
|||||||
|
package group.flyfish.fluent.utils.sql;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.utils.cache.LRUCache;
|
||||||
|
import group.flyfish.fluent.utils.context.AliasComposite;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.BinaryOperator;
|
||||||
|
|
||||||
|
import static group.flyfish.fluent.utils.sql.SqlNameUtils.wrap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属性名字处理器
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public final class EntityNameUtils {
|
||||||
|
|
||||||
|
// SerializedLambda 反序列化缓存
|
||||||
|
private static final Map<String, WeakReference<SerializedLambda>> FUNC_CACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// 列别名缓存
|
||||||
|
private static final Map<Class<?>, Map<String, String>> COLUMN_CACHE = new LRUCache<>(5);
|
||||||
|
|
||||||
|
// 表名缓存
|
||||||
|
private static final Map<Class<?>, String> TABLE_CACHE = new LRUCache<>(3);
|
||||||
|
|
||||||
|
public static <T> String toName(SFunction<T, ?> func) {
|
||||||
|
return resolve(func, (column, property) -> wrap(column));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> String toSelect(SFunction<T, ?> func) {
|
||||||
|
return resolve(func,
|
||||||
|
(column, property) -> String.join(" ", wrap(column), "as", wrap(property)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> getFields(Class<?> clazz) {
|
||||||
|
tryCache(clazz);
|
||||||
|
return COLUMN_CACHE.get(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从一个实体类中取得表名
|
||||||
|
*
|
||||||
|
* @param entityClass 实体类
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static String getTableName(Class<?> entityClass) {
|
||||||
|
return TABLE_CACHE.computeIfAbsent(entityClass, k -> {
|
||||||
|
Table table = entityClass.getAnnotation(Table.class);
|
||||||
|
if (null != table && StringUtils.hasText(table.name())) {
|
||||||
|
return table.name();
|
||||||
|
}
|
||||||
|
return wrap(SqlNameUtils.camelToUnderline(entityClass.getSimpleName()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析列并使用处理函数处理
|
||||||
|
*
|
||||||
|
* @param func 方法引用
|
||||||
|
* @param handler 处理器
|
||||||
|
* @param <T> 实体泛型
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
private static <T> String resolve(SFunction<T, ?> func, BinaryOperator<String> handler) {
|
||||||
|
SerializedLambda lambda = resolve(func);
|
||||||
|
String property = SqlNameUtils.methodToProperty(lambda.getImplMethodName());
|
||||||
|
Class<?> beanClass = resolveEntityClass(lambda);
|
||||||
|
String column = COLUMN_CACHE.get(beanClass).getOrDefault(property, SqlNameUtils.camelToUnderline(property));
|
||||||
|
// 取得别名缓存
|
||||||
|
AliasComposite.AliasCache cache = AliasComposite.sharedCache();
|
||||||
|
// 确定最终名称
|
||||||
|
String finalName = cache.has(func) ? cache.get(func) : property;
|
||||||
|
if (cache.has(beanClass)) {
|
||||||
|
return cache.get(beanClass) + "." + handler.apply(column, finalName);
|
||||||
|
}
|
||||||
|
return handler.apply(column, finalName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析方法引用为序列化lambda实例
|
||||||
|
* 该方式会使用缓存
|
||||||
|
*
|
||||||
|
* @param func 方法引用
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @return 解析结果
|
||||||
|
*/
|
||||||
|
private static <T> SerializedLambda resolve(SFunction<T, ?> func) {
|
||||||
|
Class<?> clazz = func.getClass();
|
||||||
|
String name = clazz.getName();
|
||||||
|
return Optional.ofNullable(FUNC_CACHE.get(name))
|
||||||
|
.map(WeakReference::get)
|
||||||
|
.orElseGet(() -> {
|
||||||
|
SerializedLambda lambda = SerializedLambda.resolve(func);
|
||||||
|
FUNC_CACHE.put(name, new WeakReference<>(lambda));
|
||||||
|
return lambda;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析获得实体类
|
||||||
|
*
|
||||||
|
* @param lambda 序列化的lambda
|
||||||
|
* @return 最终获取的类
|
||||||
|
*/
|
||||||
|
private static Class<?> resolveEntityClass(SerializedLambda lambda) {
|
||||||
|
return tryCache(lambda.getInstantiatedType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建字段缓存
|
||||||
|
*
|
||||||
|
* @param type 类型
|
||||||
|
* @return 构建后的缓存
|
||||||
|
*/
|
||||||
|
private static Map<String, String> buildFieldsCache(Class<?> type) {
|
||||||
|
Map<String, String> fields = new HashMap<>();
|
||||||
|
ReflectionUtils.doWithFields(type, field -> fields.put(field.getName(), resolveFinalName(field)));
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析字段注解或直接取用下划线逻辑
|
||||||
|
*
|
||||||
|
* @return 解析结果
|
||||||
|
*/
|
||||||
|
private static String resolveFinalName(Field field) {
|
||||||
|
Column column = field.getAnnotation(Column.class);
|
||||||
|
if (null != column && StringUtils.hasText(column.name())) {
|
||||||
|
return column.name();
|
||||||
|
}
|
||||||
|
return SqlNameUtils.camelToUnderline(field.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试缓存
|
||||||
|
*
|
||||||
|
* @param entityClass bean的类型
|
||||||
|
*/
|
||||||
|
private static Class<?> tryCache(Class<?> entityClass) {
|
||||||
|
COLUMN_CACHE.computeIfAbsent(entityClass, EntityNameUtils::buildFieldsCache);
|
||||||
|
return entityClass;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package group.flyfish.fluent.utils.sql;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.utils.context.AliasComposite;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import static group.flyfish.fluent.utils.sql.SqlNameUtils.wrap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持序列化的function
|
||||||
|
*
|
||||||
|
* @param <T> 泛型入参
|
||||||
|
* @param <R> 泛型返回值
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SFunction<T, R> extends Function<T, R>, Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速获取名称
|
||||||
|
*
|
||||||
|
* @return 序列化后的名称
|
||||||
|
*/
|
||||||
|
default String getName() {
|
||||||
|
return EntityNameUtils.toName(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速获取选择语句,附带别名
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
default String getSelect() {
|
||||||
|
return EntityNameUtils.toSelect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
default <V, P> SFunction<V, P> cast() {
|
||||||
|
return (SFunction<V, P>) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 覆盖写法
|
||||||
|
*
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @param <R> 泛型
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
class StaticRef<T, R> implements SFunction<T, R> {
|
||||||
|
|
||||||
|
private final Class<?> type;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final String column;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return handle(() -> wrap(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSelect() {
|
||||||
|
return handle(() -> String.join(" ", wrap(column), "as", wrap(name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String handle(Supplier<String> handler) {
|
||||||
|
if (AliasComposite.has(type)) {
|
||||||
|
return AliasComposite.get(type) + "." + handler.get();
|
||||||
|
}
|
||||||
|
return handler.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R apply(T t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011-2021, baomidou (jobob@qq.com).
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package group.flyfish.fluent.utils.sql;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.SerializationUtils;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这个类是从 {@link java.lang.invoke.SerializedLambda} 里面 copy 过来的,
|
||||||
|
* 字段信息完全一样
|
||||||
|
* <p>负责将一个支持序列的 Function 序列化为 SerializedLambda</p>
|
||||||
|
*
|
||||||
|
* @author mbp
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
class SerializedLambda implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 8025925345765570181L;
|
||||||
|
|
||||||
|
private Class<?> capturingClass;
|
||||||
|
private String functionalInterfaceClass;
|
||||||
|
private String functionalInterfaceMethodName;
|
||||||
|
private String functionalInterfaceMethodSignature;
|
||||||
|
private String implClass;
|
||||||
|
private String implMethodName;
|
||||||
|
private String implMethodSignature;
|
||||||
|
private int implMethodKind;
|
||||||
|
private String instantiatedMethodType;
|
||||||
|
private Object[] capturedArgs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过反序列化转换 lambda 表达式,该方法只能序列化 lambda 表达式,不能序列化接口实现或者正常非 lambda 写法的对象
|
||||||
|
*
|
||||||
|
* @param lambda lambda对象
|
||||||
|
* @return 返回解析后的 SerializedLambda
|
||||||
|
*/
|
||||||
|
public static SerializedLambda resolve(SFunction<?, ?> lambda) {
|
||||||
|
Assert.isTrue(lambda.getClass().isSynthetic(), "该方法仅能传入 lambda 表达式产生的合成类");
|
||||||
|
byte[] stream = SerializationUtils.serialize(lambda);
|
||||||
|
Assert.notNull(stream, "序列化类失败!");
|
||||||
|
try (ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(stream)) {
|
||||||
|
@Override
|
||||||
|
protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
|
||||||
|
Class<?> clazz;
|
||||||
|
try {
|
||||||
|
clazz = forName(objectStreamClass.getName());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
clazz = super.resolveClass(objectStreamClass);
|
||||||
|
}
|
||||||
|
return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
return (SerializedLambda) objIn.readObject();
|
||||||
|
} catch (ClassNotFoundException | IOException e) {
|
||||||
|
throw new NullPointerException("不应该发生这件事");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以类名实例化类,会隐藏报错,需要在确定有该类的情况下调用
|
||||||
|
*
|
||||||
|
* @param name 类型
|
||||||
|
* @return 实例
|
||||||
|
*/
|
||||||
|
private static Class<?> forName(String name) {
|
||||||
|
try {
|
||||||
|
return ClassUtils.forName(name, null);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取接口 class
|
||||||
|
*
|
||||||
|
* @return 返回 class 名称
|
||||||
|
*/
|
||||||
|
public String getFunctionalInterfaceClassName() {
|
||||||
|
return normalizedName(functionalInterfaceClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取实现的 class
|
||||||
|
*
|
||||||
|
* @return 实现类
|
||||||
|
*/
|
||||||
|
public Class<?> getImplClass() {
|
||||||
|
return forName(getImplClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 class 的名称
|
||||||
|
*
|
||||||
|
* @return 类名
|
||||||
|
*/
|
||||||
|
public String getImplClassName() {
|
||||||
|
return normalizedName(implClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取实现者的方法名称
|
||||||
|
*
|
||||||
|
* @return 方法名称
|
||||||
|
*/
|
||||||
|
public String getImplMethodName() {
|
||||||
|
return implMethodName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正常化类名称,将类名称中的 / 替换为 .
|
||||||
|
*
|
||||||
|
* @param name 名称
|
||||||
|
* @return 正常的类名
|
||||||
|
*/
|
||||||
|
private String normalizedName(String name) {
|
||||||
|
return name.replace('/', '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 获取实例化方法的类型
|
||||||
|
*/
|
||||||
|
public Class<?> getInstantiatedType() {
|
||||||
|
String instantiatedTypeName = normalizedName(instantiatedMethodType.substring(2, instantiatedMethodType.indexOf(';')));
|
||||||
|
return forName(instantiatedTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 字符串形式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String interfaceName = getFunctionalInterfaceClassName();
|
||||||
|
String implName = getImplClassName();
|
||||||
|
return String.format("%s -> %s::%s",
|
||||||
|
interfaceName.substring(interfaceName.lastIndexOf('.') + 1),
|
||||||
|
implName.substring(implName.lastIndexOf('.') + 1),
|
||||||
|
implMethodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
package group.flyfish.fluent.utils.sql;
|
||||||
|
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class SqlNameUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法转为属性
|
||||||
|
*
|
||||||
|
* @param name 方法名称
|
||||||
|
* @return 最终属性
|
||||||
|
*/
|
||||||
|
public static String methodToProperty(String name) {
|
||||||
|
if (name.startsWith("is")) {
|
||||||
|
name = name.substring(2);
|
||||||
|
} else {
|
||||||
|
if (!name.startsWith("get") && !name.startsWith("set")) {
|
||||||
|
throw new InvalidParameterException("不是正确的getter或setter");
|
||||||
|
}
|
||||||
|
name = name.substring(3);
|
||||||
|
}
|
||||||
|
if (name.length() == 1 || name.length() > 1 && !Character.isUpperCase(name.charAt(1))) {
|
||||||
|
name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将下划线大写方式命名的字符串转换为驼峰式。
|
||||||
|
* 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
|
||||||
|
* 例如:hello_world->helloWorld
|
||||||
|
*
|
||||||
|
* @param name 转换前的下划线大写方式命名的字符串
|
||||||
|
* @return 转换后的驼峰式命名的字符串
|
||||||
|
*/
|
||||||
|
public static String camelName(String name) {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
// 快速检查
|
||||||
|
if (name == null || name.isEmpty()) {
|
||||||
|
// 没必要转换
|
||||||
|
return "";
|
||||||
|
} else if (!name.contains("_")) {
|
||||||
|
// 不含下划线,仅将首字母小写
|
||||||
|
//update-begin--Author:zhoujf Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
|
||||||
|
//update-begin--Author:zhoujf Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
|
||||||
|
return name.substring(0, 1).toLowerCase() + name.substring(1).toLowerCase();
|
||||||
|
//update-end--Author:zhoujf Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
|
||||||
|
}
|
||||||
|
// 用下划线将原始字符串分割
|
||||||
|
String[] camels = name.split("_");
|
||||||
|
for (String camel : camels) {
|
||||||
|
// 跳过原始字符串中开头、结尾的下换线或双重下划线
|
||||||
|
if (camel.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 处理真正的驼峰片段
|
||||||
|
if (result.length() == 0) {
|
||||||
|
// 第一个驼峰片段,全部字母都小写
|
||||||
|
result.append(camel.toLowerCase());
|
||||||
|
} else {
|
||||||
|
// 其他的驼峰片段,首字母大写
|
||||||
|
result.append(camel.substring(0, 1).toUpperCase());
|
||||||
|
result.append(camel.substring(1).toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将驼峰命名转化成下划线
|
||||||
|
*
|
||||||
|
* @param para
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String camelToUnderline(String para) {
|
||||||
|
if (para.length() < 3) {
|
||||||
|
return para.toLowerCase();
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder(para);
|
||||||
|
int temp = 0;//定位
|
||||||
|
//从第三个字符开始 避免命名不规范
|
||||||
|
for (int i = 2; i < para.length(); i++) {
|
||||||
|
if (Character.isUpperCase(para.charAt(i))) {
|
||||||
|
sb.insert(i + temp, "_");
|
||||||
|
temp += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String wrap(String identifier) {
|
||||||
|
if (identifier.startsWith("`")) {
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
return String.join("", "`", identifier, "`");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T, R> R cast(T t) {
|
||||||
|
return (R) t;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
package group.flyfish.fluent.utils.text;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模板编译器
|
||||||
|
* 基于匹配模式,将字符串替换为表达式,通过sb返回结果
|
||||||
|
*/
|
||||||
|
public class TemplateCompiler {
|
||||||
|
|
||||||
|
// 暂存静态文本
|
||||||
|
private final StringBuilder sb = new StringBuilder();
|
||||||
|
// 负责存储结果
|
||||||
|
private final List<Object> parts = new ArrayList<>();
|
||||||
|
// 栈。负责存储标记
|
||||||
|
private int start = -1;
|
||||||
|
|
||||||
|
private TemplateCompiler(String code) {
|
||||||
|
parse(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 静态编译,编译为Function
|
||||||
|
*
|
||||||
|
* @param template 模板
|
||||||
|
* @return 编译结果
|
||||||
|
*/
|
||||||
|
public static DynamicValue compile(String template) {
|
||||||
|
return new TemplateCompiler(template).getCompiled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态编译,直接获得结果
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static String explain(String template, Map<String, Object> args) {
|
||||||
|
return new TemplateCompiler(template).getCompiled().apply(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parse(String code) {
|
||||||
|
int length = code.length();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
char c = code.charAt(i);
|
||||||
|
// 在栈作用中
|
||||||
|
if (start != -1) {
|
||||||
|
if (c == '}') {
|
||||||
|
parts.add(resolveExpression(code.substring(start, i)));
|
||||||
|
start = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 匹配到开始标记
|
||||||
|
if (c == '{') {
|
||||||
|
start = i + 1;
|
||||||
|
// 文本缓存非空,拼接之前的文本
|
||||||
|
if (sb.length() != 0) {
|
||||||
|
parts.add(sb.toString());
|
||||||
|
sb.delete(0, sb.length());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 拼接完成,释放sb资源。
|
||||||
|
if (sb.length() != 0) {
|
||||||
|
parts.add(sb.toString());
|
||||||
|
sb.delete(0, sb.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取编译后的内容
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public DynamicValue getCompiled() {
|
||||||
|
return context -> parts.stream().map(obj -> {
|
||||||
|
if (obj instanceof DynamicValue) {
|
||||||
|
return ((DynamicValue) obj).apply(context);
|
||||||
|
}
|
||||||
|
return String.valueOf(obj);
|
||||||
|
}).collect(Collectors.joining());
|
||||||
|
}
|
||||||
|
|
||||||
|
private DynamicValue resolveExpression(String key) {
|
||||||
|
return context -> String.valueOf(context.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface DynamicValue extends Function<Map<String, Object>, String> {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
19
fluent-sql-jdbctemplate/pom.xml
Normal file
19
fluent-sql-jdbctemplate/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>fluent-sql</artifactId>
|
||||||
|
<groupId>group.flyfish.framework</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>fluent-sql-jdbctemplate</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
181
pom.xml
Normal file
181
pom.xml
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>group.flyfish.framework</groupId>
|
||||||
|
<artifactId>fluent-sql</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<modules>
|
||||||
|
<module>fluent-sql-core</module>
|
||||||
|
<module>fluent-sql-jdbctemplate</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<lombok.version>1.18.24</lombok.version>
|
||||||
|
<jackson.version>2.13.3</jackson.version>
|
||||||
|
<spring.version>5.3.22</spring.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<!-- 开发人员信息 -->
|
||||||
|
<developers>
|
||||||
|
<developer>
|
||||||
|
<name>wangyu</name>
|
||||||
|
<email>wybaby168@gmail.com</email>
|
||||||
|
<organization>http://flyfish.group</organization>
|
||||||
|
<timezone>+8</timezone>
|
||||||
|
</developer>
|
||||||
|
</developers>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>central</id>
|
||||||
|
<url>https://maven.aliyun.com/nexus/content/groups/public</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<distributionManagement>
|
||||||
|
<snapshotRepository>
|
||||||
|
<id>ossrh</id>
|
||||||
|
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||||
|
</snapshotRepository>
|
||||||
|
<repository>
|
||||||
|
<id>ossrh</id>
|
||||||
|
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||||
|
</repository>
|
||||||
|
</distributionManagement>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-jdbc</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>1.7.36</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.persistence</groupId>
|
||||||
|
<artifactId>persistence-api</artifactId>
|
||||||
|
<version>1.0.2</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>release</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<show>package</show>
|
||||||
|
<doclint>none</doclint>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-javadocs</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-gpg-plugin</artifactId>
|
||||||
|
<version>1.5</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>sign-artifacts</id>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>sign</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.sonatype.plugins</groupId>
|
||||||
|
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||||
|
<version>1.6.7</version>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<configuration>
|
||||||
|
<serverId>ossrh</serverId>
|
||||||
|
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||||
|
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-sources</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.7.0</version>
|
||||||
|
<configuration>
|
||||||
|
<source>${maven.compiler.source}</source>
|
||||||
|
<target>${maven.compiler.target}</target>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<version>3.1.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-dependencies</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||||
|
<overWriteReleases>false</overWriteReleases>
|
||||||
|
<overWriteSnapshots>true</overWriteSnapshots>
|
||||||
|
<overWriteIfNewer>true</overWriteIfNewer>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
Loading…
Reference in New Issue
Block a user