From ec7c8a7c7a041d8f4f302e72b7c2ea7e79c900b2 Mon Sep 17 00:00:00 2001 From: blinkfox Date: Sat, 2 Jan 2021 22:46:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 13 ++-- docs/compare-mybatis.md | 10 +-- docs/java/example.md | 8 +-- docs/java/main-method.md | 106 ++++++++++++++-------------- docs/queryfenix-introduction.md | 12 ++-- docs/quick-install.md | 29 ++++---- docs/quick-start.md | 19 ++--- docs/sp-api/example.md | 6 +- docs/sp-api/main-method.md | 56 +++++++-------- docs/sp-bean/annotations.md | 112 +++++++++++++++--------------- docs/sp-bean/custom-annotation.md | 12 ++-- docs/sp-bean/introduction.md | 18 ++--- docs/usage-example.md | 10 +-- docs/xml/custom-tag.md | 23 +++--- docs/xml/logic-control.md | 14 ++-- docs/xml/xml-tags.md | 94 ++++++++++++------------- 16 files changed, 277 insertions(+), 265 deletions(-) diff --git a/docs/README.md b/docs/README.md index d866a8f..3f73df9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,10 +1,13 @@ -# 🍑 简介 +# 🍑 简介 :id=introduction > [🔥 Fenix](https://github.com/blinkfox/fenix)(菲尼克斯)是一个为了解决复杂动态 SQL (`JPQL`) 而生的 `Spring Data JPA` 扩展库,目的是辅助开发者更方便快捷的书写复杂、动态且易于维护的 SQL,支持 `XML`、Java 链式 `API` 和动态条件注解等四种方式来书写动态 SQL。 +**🏕️ 使用示例**: + - [⛱️ 示例项目 (fenix-example)](https://github.com/blinkfox/fenix-example) +- [🏝️ Fenix 使用的单元测试](https://github.com/blinkfox/fenix/tree/develop/src/test/java/com/blinkfox/fenix/repository) -## 💎 特性 +## 💎 特性 :id=features - 简单、轻量级、无副作用的集成和使用,jar 包仅 `191 KB`; - 作为 JPA 的扩展和增强,兼容 Spring Data JPA 原有功能和各种特性; @@ -18,7 +21,7 @@ - SQL 执行结果可返回任意自定义的实体对象,比使用 JPA 自身的投影方式更加简单和自然; - 具有可扩展性,如:可自定义 `XML` 语义标签和对应的标签处理器来生成自定义逻辑的 SQL 片段和参数; -## 🎁 初衷 +## 🎁 初衷 :id=original-intention 随着 [Spring Data JPA](https://spring.io/projects/spring-data-jpa) 越来越流行,极大的方便了数据的“增删改”和简单查询的场景,但是在复杂、动态查询方面就显得有些“糟糕”了,相比 `MyBatis` 的 `XML` 动态 SQL 而言,缺少了一定优雅和可维护性,而使用原生的 `Specification` 又显得过于“臃肿”。 @@ -30,10 +33,10 @@ > **💡 注**:本 `Fenix` 扩展库开发的核心思想来源于我几年前写的动态 SQL 拼接库 [Zealot](https://github.com/blinkfox/zealot)。如果你熟悉星际争霸的话,大概能理解其中的关系。 -## 📝 开源许可证 +## 📝 开源许可证 :id=license 本 `Fenix` 的 Spring Data JPA 扩展库遵守 [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) 许可证。 -## 🙏 鸣谢 +## 🙏 鸣谢 :id=thanks 感谢 [JetBrains 公司](https://www.jetbrains.com/?from=fenix) 为本开源项目提供的免费正版 Intellij IDEA 的 License 支持。 diff --git a/docs/compare-mybatis.md b/docs/compare-mybatis.md index ae57e37..e992fed 100644 --- a/docs/compare-mybatis.md +++ b/docs/compare-mybatis.md @@ -1,6 +1,6 @@ -# 🍎 与 MyBatis XML 的 SQL 写法比较 +# 🍎 与 MyBatis XML 的 SQL 写法比较 :id=title -## 🌴 假设业务查询场景 +## 🌴 假设业务查询场景 :id=business-scenario 下面将通过一个多条件查询**操作日志**的功能,来初步了解和比较 `MyBatis` 与 `Fenix` 在写“**多条件模糊分页**”查询时 SQL 写法的一些差异和各自的特点。 @@ -13,7 +13,7 @@ - **操作结果**:数据库字段类型为 `int` 型,只能下拉选择一个选项值来进行**等值查询**(`=`); - **操作时间**:数据库字段类型为 `datetime` 型,可以选择开始时间或者结束时间来进行**区间查询**(`BETWEEN ? AND ?`、`>=`、`<=`); -## 🌼 使用 MyBatis XML 的 SQL 写法 +## 🌼 使用 MyBatis XML 的 SQL 写法 :id=mybatis-xml ```xml @@ -71,7 +71,7 @@ ``` -## 🌺 使用 Fenix XML 的 SQL 写法 +## 🌺 使用 Fenix XML 的 SQL 写法 :id=fenix-xml ```xml @@ -99,7 +99,7 @@ ``` -## 🌸 比较总结 +## 🌸 比较总结 :id=summary `MyBatis` 和 `Fenix` 的 SQL 有以下几个差异点: diff --git a/docs/java/example.md b/docs/java/example.md index ba3cc08..7f80b4a 100644 --- a/docs/java/example.md +++ b/docs/java/example.md @@ -1,10 +1,10 @@ -# 🍗 总体示例 +# 🍗 总体示例 :id=title 在 Fenix 中书写动态 SQL,也可以使用 Java 链式 `API` 代码来完成,使用方式和 `MyBatis` 的方式总体相似,但是 Fenix 的 Java 动态 SQL 的 `API` 更为强大,且大多数方法同 `XML` 中 SQL 语义化标签相对应。 下面直接通过示例来说明如何使用。 -## 🎠 一、创建提供 SqlInfo 的方法 +## 🎠 一、创建提供 SqlInfo 的方法 :id=provider-method 创建一个 `BlogSqlProvider` 类,在类中创建一个返回 `SqlInfo` 示例的方法,这里作为示例我取名为 `queryBlogsWithJava`,方法参数最好与 `BlogRepository` 接口中的查询方法保持一致,并用 `@Param` 注解来描述,方法的参数顺序可以调换,参数可以变少,但不应该增多(因为多余的参数值,也会是 `null`)。 @@ -51,7 +51,7 @@ public class BlogSqlProvider { } ``` -## 🎠 二、创建查询方法 +## 🎠 二、创建查询方法 :id=query-method 在 `BlogRepository.java` 接口中添加动态查询博客信息的方法,我这里也取名为 `queryBlogsWithJava`。注意,这里也使用到了 `@QueryFenix` 注解,但是注解元素用得是 `provider` 和 `method`。 @@ -76,7 +76,7 @@ List queryBlogsWithJava(@Param("blog") Blog blog, @Param("startTime") Date - `method`:表示 `SqlInfo` 提供类的方法名,值为方法名的字符串。**非必填**元素。如果不填写这个值,就默认视为提供的方法名就是 `repository` 接口的查询方法名。示例中它的值就是我们前面创建的 `queryBlogsWithJava` 方法。 - `countMethod()`:表示通过 Java 来拼接分页查询时查询总记录数 SQL 语句的提供类的方法名。 -## 🎢 三、创建并执行单元测试 +## 🎢 三、创建并执行单元测试 :id=unit-test 在 `BlogRepositoryTest` 单元测试类中,新增 `queryBlogsWithJava` 测试方法,代码如下: diff --git a/docs/java/main-method.md b/docs/java/main-method.md index 4554cd2..5959fe8 100644 --- a/docs/java/main-method.md +++ b/docs/java/main-method.md @@ -1,39 +1,41 @@ -## 🐫 一、无参静态方法 +# 🍖 API 方法 :id=title + +## 🐫 一、无参静态方法 :id=no-param-static-method !> **💡 注意**:这里的**无参静态方法**是指拼接 SQL 时仅仅拼接文本字符串,不会添加 SQL 的命名参数,且无动态判断是否生成该段 SQL 片段的能力。作用是和待拼接的字符串自动拼接在一起,省去了 `SQL 关键字` 的书写,目的是用来提高SQL的可读性。 -### 💻 1. 主要方法 +### 💻 1. 主要方法 :id=main-methods SQL中的关键字很多,`Fenix` 封装了大多数常用的关键字作为连接 SQL 字符串的方法,如上面总体示例所列出的 `select()`、`from()`、`select()` 等,在流式拼接的过程中,使得 SQL 的可读性和连贯性大大提高了。下面列出了大多数常用的关键字方法,来**用于拼接字符串文本,但不能传递 SQL 参数**。 -- insertInto(String text) -- values(String text) -- deleteFrom(String text) -- update(String text) -- select(String text) -- from(String text) -- and(String text) -- or(String text) -- as(String text) -- set(String text) -- innerJoin(String text) -- leftJoin(String text) -- rightJoin(String text) -- fullJoin(String text) -- on(String text) -- orderBy(String text) -- groupBy(String text) -- having(String text) -- limit(String text) -- offset(String text) -- asc() -- desc() -- union() -- unionAll() - -> **以上方法主要作用**:用方法名所代表的关键字后追加空格,再拼接上 `text` 文本参数,其方法名称已经体现了具体用途和使用场景,这里不在赘述。 - -### 🔌 2. 使用示例 +- `insertInto(String text)` +- `values(String text)` +- `deleteFrom(String text)` +- `update(String text)` +- `select(String text)` +- `from(String text)` +- `and(String text)` +- `or(String text)` +- `as(String text)` +- `set(String text)` +- `innerJoin(String text)` +- `leftJoin(String text)` +- `rightJoin(String text)` +- `fullJoin(String text)` +- `on(String text)` +- `orderBy(String text)` +- `groupBy(String text)` +- `having(String text)` +- `limit(String text)` +- `offset(String text)` +- `asc()` +- `desc()` +- `union()` +- `unionAll()` + +> **🔔 以上方法主要作用**:用方法名所代表的关键字后追加空格,再拼接上 `text` 文本参数,其方法名称已经体现了具体用途和使用场景,这里不在赘述。 + +### 🔌 2. 使用示例 :id=static-method-demo !> **💡 注**:下面的示例仅是为了演示相关 `API` 的使用,具体 SQL 运行时的正确性,你不用特别在意,实际业务场景中不会这样写。 @@ -77,11 +79,11 @@ public void testSelect() { } ``` -## 🦒 二、text +## 🦒 二、text :id=text `text()` 系列的方法作用同 `XML` 中的 [text 标签](xml/xml-tags?id=text)比较类似,是用来任意传递拼接 SQL 字符串和参数的,主要目的是为了提高 SQL 拼接的灵活性。 -### 💿 1. 主要方法 +### 💿 1. 主要方法 :id=text-methods 下面是 `text()` 系列的重载方法: @@ -126,11 +128,11 @@ public void testText() { } ``` -## 🐘 三、param 和 params +## 🐘 三、param 和 params :id=params `param()` 和 `params()` 方法的作用是为了任意传递 SQL 参数的,目的也是为了提高 SQL 拼接过程中 SQL 参数的灵活性。 -### 💾 1. 主要方法 +### 💾 1. 主要方法 :id=params-methods ```java // 在 SQL 的参数集合中添加命名参数,其中 key 是 JPQL 中的命名参数名称,value 是该参数对应的值. @@ -140,13 +142,13 @@ param(String key, Object value) params(Map paramMap) ``` -### 📺 2. 使用示例 +### 📺 2. 使用示例 :id=params-demo 关于 `param` 的使用示例可以直接参考 [text 的使用示例](java/main-method?id=text-example) 即可。 -## 🐭 四、equal +## 🐭 四、equal :id=equal -### 📷 1. 方法介绍 +### 📷 1. 方法介绍 :id=equal-methods `equal` 系列是用来拼接 SQL 中等值查询的系列方法,生成如:`u.email = :email` 这样的等值查询且附带绑定参数的功能,其主要包含如下方法: @@ -178,7 +180,7 @@ orEqual(String field, Object value, String name, boolean match) // v2.3.0 版本 - `value`,表示 Java 中的变量或常量值; - `match`,表示是否生成该 SQL 片段,值为 `true` 时生成,否则不生成; -## 🐁 五、与 equal 类似的方法 +## 🐁 五、与 equal 类似的方法 :id=equal-similar 同 `equal`(等于)类似的系列方法还有**不等于**、**大于**、**小于**、**大于等于**、**小于等于**、**模糊查询**,各系列分别如下: @@ -197,9 +199,9 @@ orEqual(String field, Object value, String name, boolean match) // v2.3.0 版本 !> 以上各系列的方法和参数也同 `equal`,这里就不再赘述了。 -## 🦛 六、between +## 🦛 六、between :id=between -### 🕯️ 1. 方法介绍 +### 🕯️ 1. 方法介绍 :id=between-methods `between` 系列方法是用来拼接 SQL 中区间查询的系列方法,生成如:`u.age BETWEEN :u_age_start AND :u_age_end`这样的区间查询功能,主要包含如下方法: @@ -233,7 +235,7 @@ orEqual(String field, Object value, String name, boolean match) // v2.3.0 版本 - `endValue`,表示区间查询的结束值; - `match`,表示是否生成该SQL片段,值为`true`时生成,否则不生成; -### 🔦 2. 使用示例 +### 🔦 2. 使用示例 :id=between-demo !> **注**:下面的示例仅是为了集中演示 `between` 的使用,具体 SQL 运行时的正确性,你不用特别在意。 @@ -267,9 +269,9 @@ assertEquals(3, sqlInfo.getParams().size()); !> **💡 注意**:Fenix 中会对 `start` 和 `end` 的值做 `null` 的空检测。区间查询中如果 `start` 为空,`end` 不为空,则会退化为大于等于查询;如果 `start` 为空,`end` 不为空,则会退化为小于等于查询;如果 `start`、`end` 均不为空,则是区间查询;两者会均为空则不生产此条 SQL。 -## 🐹 七、in +## 🐹 七、in :id=in -### 🏮 1. 方法介绍 +### 🏮 1. 方法介绍 :id=in-methods `in` 系列的方法是用来拼接 `SQL` 中范围查询的系列方法,生成如:`u.sex in :u_sex` 这样的范围查询功能,主要包含如下方法: @@ -343,7 +345,7 @@ orNotIn(String field, String name, Collection values, boolean match) // v2.3. - `values`,表示范围查询需要的参数的数组或集合; - `match`,表示是否生成该SQL片段,值为`true`时生成,否则不生成; -### 🪔 2. 使用示例 +### 🪔 2. 使用示例 :id=in-demo !> **💡 注**:下面的示例仅是为了集中演示 `in` 的使用,具体 SQL 运行时的正确性,你不用特别在意。 @@ -372,9 +374,9 @@ assertEquals("u.sex IN :u_sex u.city IN :u_city u.sex IN :u_sex AND u.sex IN :u_ assertEquals(2, sqlInfo.getParams().size()); ``` -## 🐿️ 八、isNull +## 🐿️ 八、isNull :id=is-null -### 📖 1. 方法介绍 +### 📖 1. 方法介绍 :id=is-null-methods `isNull` 系列的方法是用来拼接 SQL 中判断字段为 `null` 值或不为 `null` 值情况的系列方法,生成如:`u.state IS NULL` 这样 SQL 片段的功能,主要包含如下方法: @@ -410,7 +412,7 @@ orIsNotNull(String field, boolean match - `field`,表示数据库字段或实体属性; - `match`,表示是否生成该 SQL 片段,值为 `true` 时生成,否则不生成; -### 📕 2. 使用示例 +### 📕 2. 使用示例 :id=is-null-demo !> **注**:下面的示例仅是为了集中演示 `isNull` 的使用,具体 SQL 运行时的正确性,你不用特别在意。 @@ -441,7 +443,7 @@ public void testIsNull() { } ``` -## 🐻 九、doAny +## 🐻 九、doAny :id=do-any `doAny` 的两个方法主要用来方便你在链式拼接的过程中,来完成更多自定义、灵活的操作。`match` 意义和上面类似,值为 `true` 时才执行,`FenixAction` 是你自定义操作的函数式接口,执行时调用 `execute()` 方法,Java 8 及之后可以使用 `Lambda` 表达式来简化代码。 @@ -453,7 +455,7 @@ doAny(FenixAction action) doAny(boolean match, FenixAction action) ``` -### 📘 1. 使用示例 +### 📘 1. 使用示例 :id=do-any-demo 下面是 `doAny` 的执行示例,供你参考。 @@ -468,7 +470,7 @@ SqlInfo sqlInfo = Fenix.start() .end(); ``` -## 🐼 十、where +## 🐼 十、where :id=where `where` 方法有几个重载方法,其中 `where(Consumer consumer)` 方法同 XML 中的 `` 标签是用来处理动态 SQL 中的 `WHERE` 关键之后的 `AND` 或者 `OR` 关键字的情况。 @@ -497,7 +499,7 @@ where(Consumer consumer) whereDynamic() ``` -### 📗 1. 使用示例 +### 📗 1. 使用示例 :id=where-demo 下面是动态 where(`whereDynamic()` 和 `where(Consumer consumer)`)的使用示例,供你参考。 @@ -527,7 +529,7 @@ SqlInfo sqlInfo = Fenix.start() .end(); ``` -## 🦨 十一、综合性示例 +## 🦨 十一、综合性示例 :id=comprehensive-example 下面是一个综合性的示例,来演示通过 Fenix 的链式 API 来拼接动态 SQL 的使用。 diff --git a/docs/queryfenix-introduction.md b/docs/queryfenix-introduction.md index 6e58330..e07ab7f 100644 --- a/docs/queryfenix-introduction.md +++ b/docs/queryfenix-introduction.md @@ -1,4 +1,6 @@ -## 🚀 一、注解元数据介绍 +# 🍓 @QueryFenix 注解 :id=title + +## 🚀 一、注解元数据介绍 :id=metadata - **`value()`**: 完整的 `fenix id` 标记,该值由 XML 文件的命名空间(`namespace`)、点(`.`)号和 Fenix XML 标签中的 fenix id 组成。如果 `namespace` 的值写成对应 `repository` 接口的全路径名,则该值可以不用写 `namespace`和`.`号,且如果 fenix id 与对应的查询方法名一样,那么 `value()` 值就可以为空。 - **`countQuery()`**: 表示查询分页查询情况下查询总记录数时需要执行的 SQL。该值仅分页查询时用到,值的规则同上面的 `value()` 一样,如果 `namespace` 的值写成对应 `repository` 接口的全路径名,则该值可以不用写 `namespace`和`.`号。 @@ -7,7 +9,7 @@ - **`method()`**: 表示通过 Java 来拼接 SQL 语句的提供类的方法。 - **`countMethod()`**: 表示通过 Java 来拼接分页查询时查询总记录数 SQL 语句的提供类的方法。 -## 🚠 二、@QueryFenix 注解使用简化 +## 🚠 二、@QueryFenix 注解使用简化 :id=simplified 之前的示例中 `@QueryFenix("BlogRepository.queryMyBlogs")` 注解的内容分别代表 XML 文件对应的命名空间 `namespace` 和 XML 标签的 `id` 属性值。如果你将 XML 文件中的 `namespace` 写成 `BlogRepository.java` 的全路径名 `com.blinkfox.fenix.example.repository.BlogRepository`,那么 `@QueryFenix` 注解就可以再简化一些,只写对应的 `fenixId` 即可。 @@ -34,7 +36,7 @@ Page queryMyBlogs(@Param("ids") List ids, @Param("blog") Blog blog Page queryMyBlogs(@Param("ids") List ids, @Param("blog") Blog blog, Pageable pageable); ``` -## 🛰️ 三、自动的分页总记录数查询及自定义 +## 🛰️ 三、自动的分页总记录数查询及自定义 :id=auto-paging 上面的分页查询,我们没有设置自定义的查询总记录数的语句,依然可以正常分页。是因为 Fenix 帮你将上面 `SELECT` 语句块的查询结果换成了 `count(*)`,来查询总记录数。由于 JPA 中的分页和排序参数是单独设置的,所以,查询总记录数的 JPQL 语句中也不会有 `Order By` 这样的片段。 @@ -63,10 +65,10 @@ Page queryMyBlogs(@Param("ids") List ids, @Param("blog") Blog blog Page queryMyBlogs(@Param("ids") List ids, @Param("blog") Blog blog, Pageable pageable); ``` -## 🚁 四、nativeQuery 原生 SQL +## 🚁 四、nativeQuery 原生 SQL :id=native-query 同原生的 `@Query` 注解一样,你也可以在 `@QueryFenix` 注解中,通过将 `nativeQuery()` 的值来设置为 `true`,来表示你的 JPQL 语句是将使用原生 SQL 查询。 -## 🪐 五、使用 Java 代码拼接 SQL +## 🪐 五、使用 Java 代码拼接 SQL :id=java-sql 关于如何用 Java 代码来拼接动态 SQL,请参看[后续篇章](java/example)。 diff --git a/docs/quick-install.md b/docs/quick-install.md index 862f9c3..24768bc 100644 --- a/docs/quick-install.md +++ b/docs/quick-install.md @@ -1,4 +1,6 @@ -## 🏖️ 一、支持场景 +# 🍋 快速集成 :id=title + +## 🏖️ 一、支持场景 :id=support-scenarios 适用于 Java `Spring Data JPA` 项目,`JDK 1.8` 及以上,Spring Data JPA 的版本须保证 `2.1.8.RELEASE` 及以上;如果你是 Spring Boot 项目,则 Spring Boot 的版本须保证 `2.1.5.RELEASE` 及以上。因为后续版本的 Spring Data JPA 对其中 `QueryLookupStrategy` 的代码有较大改动。 @@ -8,7 +10,7 @@ !> **💡 注**:请确保你使用的 Spring Boot 版本是 **`v2.1.5.RELEASE` 及以上**,如果 Spring Boot 版本是 `v2.2.x.RELEASE` 及以上,则 Fenix 版本必须是 `v2.0.0` 版本及以上。 -### 🌾 1. Maven +### 🌾 1. Maven :id=spring-boot-maven ```xml @@ -18,13 +20,13 @@ ``` -### 🌵 2. Gradle +### 🌵 2. Gradle :id=spring-boot-gradle ```bash compile 'com.blinkfox:fenix-spring-boot-starter:2.4.1' ``` -### 🏕️ 3. 激活 Fenix (@EnableFenix) +### 🏕️ 3. 激活 Fenix (@EnableFenix) :id=enable-fenix 然后需要在你的 Spring Boot 应用中使用 `@EnableFenix` 激活 Fenix 的相关配置信息。 @@ -49,13 +51,14 @@ public class DemoApplication { } ``` -> **💡 注**: -> 1. `@EnableFenix` 注解中实质上是使用的是 `FenixJpaRepositoryFactoryBean`。而 `FenixJpaRepositoryFactoryBean` 继承自 Spring Data JPA 默认的 `JpaRepositoryFactoryBean`。所以,Fenix 与 JPA 的各种注解和特性完全兼容,并提供了更加强大的 `@QueryFenix` 注解和其他更多动态的能力。 -> 2. 如果你是多数据源,则你可以根据自身情况,在需要的数据源中使用 `@EnableFenix` 注解即可。或者你也可以在 `@EnableJpaRepositories` 注解中单独设置 `repositoryFactoryBeanClass` 的值为:`FenixJpaRepositoryFactoryBean.class`。示例如:`@EnableJpaRepositories(repositoryFactoryBeanClass = FenixJpaRepositoryFactoryBean.class)`。 +**💡 注意事项**: + +- 🔹 `@EnableFenix` 注解中实质上是使用的是 `FenixJpaRepositoryFactoryBean`。而 `FenixJpaRepositoryFactoryBean` 继承自 Spring Data JPA 默认的 `JpaRepositoryFactoryBean`。所以,Fenix 与 JPA 的各种注解和特性完全兼容,并提供了更加强大的 `@QueryFenix` 注解和其他更多动态的能力。 +- 🔹 如果你是多数据源,则你可以根据自身情况,在需要的数据源中使用 `@EnableFenix` 注解即可。或者你也可以在 `@EnableJpaRepositories` 注解中单独设置 `repositoryFactoryBeanClass` 的值为:`FenixJpaRepositoryFactoryBean.class`。示例如:`@EnableJpaRepositories(repositoryFactoryBeanClass = FenixJpaRepositoryFactoryBean.class)`。 -### 🏝️ 4. application.yml 配置(可选的) +### 🏝️ 4. application.yml 配置(可选的) :id=spring-boot-config -!> **注**: Fenix 采用了**约定优于配置**的方式,所以通常情况下,你可以不用做下面任何的 Fenix 配置,下面的配置信息供你参考使用。 +!> **💡 注**: Fenix 采用了**约定优于配置**的方式,所以通常情况下,你可以不用做下面任何的 Fenix 配置,下面的配置信息供你参考使用。 要修改 Fenix 的配置信息,你需要在你的 Spring Boot 项目中,在 `application.yml` 或者 `application.properties` 中去修改配置信息。 @@ -89,7 +92,7 @@ fenix: !> **注**:请确保你引入的 Spring Data JPA 版本是 **`2.1.8.RELEASE` 及以上**,如果 Spring Data JPA 版本是 `v2.2.x.RELEASE` 及以上,则 Fenix 版本必须是 `v2.0.0` 版本及以上。。 -### 🌼 1. Maven +### 🌼 1. Maven :id=project-maven ```xml @@ -99,13 +102,13 @@ fenix: ``` -### 🌻 2. Gradle +### 🌻 2. Gradle :id=project-gradle ```bash compile 'com.blinkfox:fenix:2.4.1' ``` -### 🏔️ 3. 激活 Fenix +### 🏔️ 3. 激活 Fenix :id=project-enable-fenix 跟前面 Spring Boot 激活 Fenix FactoryBean 一样,需要在启动类中使用 `@EnableFenix` 激活 Fenix,也可以直接在 `@EnableJpaRepositories` 注解中,配置 `repositoryFactoryBeanClass` 的属性值为 `FenixJpaRepositoryFactoryBean.class`。 @@ -119,7 +122,7 @@ compile 'com.blinkfox:fenix:2.4.1' @EnableJpaRepositories(repositoryFactoryBeanClass = FenixJpaRepositoryFactoryBean.class) ``` -### 🚣 4. 加载 Fenix 配置信息 +### 🚣 4. 加载 Fenix 配置信息 :id=project-config 最后,需要在你的应用启动过程中加载 Fenix 配置信息到内存中。我这里作为示例在 Spring Bean 中的 `@PostConstruct` 的方法中来加载 Fenix 配置。当然,你也可以在你想初始化的任何代码中去加执 Fenix 的初始化加载的代码。 diff --git a/docs/quick-start.md b/docs/quick-start.md index 51a3a97..e71d310 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -1,4 +1,4 @@ -# 🍇 快速开始 +# 🍇 快速开始 :id=title 对于很长的、复杂的动态或统计性的 SQL 采用注解 或者 Java 书写不仅冗长,且不易于调试和维护。因此,我更推荐你通过 `XML` 文件来书写 SQL,使得 SQL 和 Java 代码解耦,更易于维护和阅读。 @@ -6,11 +6,11 @@ !> **💡 提示**:本文档中的一些示例可以在 Fenix 示例项目源码 [fenix-example](https://github.com/blinkfox/fenix-example) 中查看,其他更多的使用示例可以在 Fenix 源码的[单元测试](https://github.com/blinkfox/fenix/tree/develop/src/test/java/com/blinkfox/fenix/repository)中找到。 -## 🚞 一、项目和数据准备 +## 🚞 一、项目和数据准备 :id=project-data-ready !> **💡 注**:下面“项目和数据准备”的内容,除了集成 Fenix 配置之外,基本上与 Fenix 无关,你大概体验和预览下内容就行。 -### 🚜 1. 创建项目 +### 🚜 1. 创建项目 :id=create-project 在 [start.spring.io](https://start.spring.io/) 中创建一个自己的 SpringBoot2.x 项目,目前最新稳定版本是 `2.1.7`(后续会陆续升级)。选出了一些组件来生成项目,我这里仅选了如下几个: @@ -21,7 +21,7 @@ 生成之后直接导入 IDE 开发工具,然后根据前面的 Fenix [Spring Boot 项目集成](quick-install?id=spring-boot-integrations) 的文档集成 Fenix 库到项目中即可,这里不再赘述。你也可以 [点击这里下载](https://github.com/blinkfox/fenix-example) 本示例项目的源码查看。 -### 🏎️ 2. Blog 实体类 +### 🏎️ 2. Blog 实体类 :id=blog-engity 以下将以一个简单的博客信息(`Blog`)作为实体来演示在 Fenix 中 `XML` 动态 SQL 的使用。`Blog` 实体类代码如下: @@ -94,7 +94,7 @@ public class Blog { } ``` -### 🛵 3. 初始化结构和数据 +### 🛵 3. 初始化结构和数据 :id=init-data 我这里准备了一些初始化的数据表结构和数据脚本,当然也可用直接通过 JPA 特性和 API 代码来初始化数据。这里,我还是通过脚本的方式来初始化一些后续查询需要用到的数据。 @@ -138,7 +138,7 @@ INSERT INTO test.t_blog VALUES ('9', '4', '马六-Maliu', 'Vue 项目实战',' INSERT INTO test.t_blog VALUES ('10', '5', '马六-Maliu', 'JavaScript 精粹','这是 JavaScript 精粹的内容', '2019-08-13 00:41:33', '2019-08-13 00:41:36'); ``` -### 🏍️ 4. application.yml 配置 +### 🏍️ 4. application.yml 配置 :id=application-config 以下是 `application.yml` 配置文件的内容,供你参考: @@ -160,6 +160,7 @@ spring: # Fenix 的几个配置,都有默认值. 所以通常不需要配置,下面的配置代码也都可以删掉,你视具体情况配置即可. fenix: + debug: false print-banner: true print-sql: xml-locations: @@ -167,7 +168,7 @@ fenix: predicate-handlers: ``` -## 🚌 二、创建 BlogRepository.java +## 🚌 二、创建 BlogRepository.java :id=blog-repository-java 定义博客信息操作的持久层代码 `BlogRepository` 接口,这里使用 `@QueryFenix` 注解来演示根据散参数、博客信息 Bean(可以是其它Bean 或者 Map)的参数来**多条件模糊分页查询**博客信息。注解的值是 Fenix XML 文件对应的命名空间 `namespace` 和 `` XML 标签的 `id` 属性值。 @@ -204,7 +205,7 @@ public interface BlogRepository extends JpaRepository { } ``` -## 🚑 三、创建 BlogRepository.xml +## 🚑 三、创建 BlogRepository.xml :id=blog-repository-xml 在 `BlogRepository.java` 中的标注了 `@QueryFenix("BlogRepository.queryMyBlogs")` 注解,就需要根据此注解的信息找到 XML 文件中的 JPQL 语句块儿,才能生成 JPQL 语句和参数,并执行 JPQL 语句。 @@ -231,7 +232,7 @@ public interface BlogRepository extends JpaRepository { ``` -## 🚒 四、创建并执行单元测试 +## 🚒 四、创建并执行单元测试 :id=unit-test 最后,创建一个用于测试查询博客信息的单元测试类 `BlogRepositoryTest`,代码如下: diff --git a/docs/sp-api/example.md b/docs/sp-api/example.md index 304b687..8bab24c 100644 --- a/docs/sp-api/example.md +++ b/docs/sp-api/example.md @@ -1,10 +1,10 @@ -# 🍕 总体示例 +# 🍕 总体示例 :id=title 在 Fenix 中书写动态查询,也可以使用 Spring Data JPA 中提供的 `Specification` 方式,Fenix 中对此进行了动态查询能力的链式封装。 下面直接通过示例来说明如何使用。 -## 📒 一、继承 FenixJpaSpecificationExecutor 接口 +## 📒 一、继承 FenixJpaSpecificationExecutor 接口 :id=extends-executor 在你的 `BlogRepository` 接口中,继承 `FenixJpaSpecificationExecutor` 接口,该接口实质上是继承自 `JpaSpecificationExecutor` 接口,但提供了更多的**默认接口方法**,且在 API 使用上,可以不用写 `FenixSpecification.of()` 的中间层,更为简单直接。 @@ -14,7 +14,7 @@ public interface BlogRepository extends JpaRepository, FenixJpaSpe } ``` -## 🏷️ 二、Service 中直接调用 +## 🏷️ 二、Service 中直接调用 :id=service-invoke 基于 `Specification` 的方式,不需要定义额外的查询方法,也不需要写 `JPQL` (或 SQL) 语句,简单直接。 diff --git a/docs/sp-api/main-method.md b/docs/sp-api/main-method.md index bb5a072..2b3286f 100644 --- a/docs/sp-api/main-method.md +++ b/docs/sp-api/main-method.md @@ -1,12 +1,12 @@ -# 🌭 API 方法 +# 🌭 API 方法 :id=title 以下将介绍基于 `Specification` 的主要 API 方法。这些 API 都在 `FenixPredicateBuilder` 类中,用来链式生成 `Predicate` 的条件集合。 -## 🦜 一、比较匹配类型的方法 +## 🦜 一、比较匹配类型的方法 :id=compare-match 比较类型的方法是指等于、不等于、大于、大于等于、小于、小于等于等方法,使用方式也几乎相同。主要 API 如下: -### 💰 1. 等于 (Equal) +### 💰 1. 等于 (Equal) :id=equal > **注**:由于 `equals()` 方法是 Java Object 类自带的方法,为了将其与本方法区分开来,Fenix 中的等值匹配系列的方法取名为 `equal`。 @@ -28,7 +28,7 @@ orNotEquals(String fieldName, Object value) orNotEquals(String fieldName, Object value, boolean match) ``` -### 📧 2. 大于 (GreaterThan) +### 📧 2. 大于 (GreaterThan) :id=greater-than ```java // 生成“与逻辑”的“大于匹配”的 Predicate 条件,如果没有 match 参数或者 match 值为 true 则生成该条件,否则不生成. @@ -40,7 +40,7 @@ orGreaterThan(String fieldName, Object value) orGreaterThan(String fieldName, Object value, boolean match) ``` -### 📩 3. 大于等于 (GreaterThanEqual) +### 📩 3. 大于等于 (GreaterThanEqual) :id=greater-than-equal ```java // 生成“与逻辑”的“大于等于匹配”的 Predicate 条件,如果没有 match 参数或者 match 值为 true 则生成该条件,否则不生成. @@ -52,7 +52,7 @@ orGreaterThanEqual(String fieldName, Object value) orGreaterThanEqual(String fieldName, Object value, boolean match) ``` -### 📥 4. 小于 (LessThan) +### 📥 4. 小于 (LessThan) :id=less-than ```java // 生成“与逻辑”的“小于匹配”的 Predicate 条件,如果没有 match 参数或者 match 值为 true 则生成该条件,否则不生成. @@ -64,7 +64,7 @@ orLessThan(String fieldName, Object value) orLessThan(String fieldName, Object value, boolean match) ``` -### 📫 5. 小于等于 (andLessThanEqual) +### 📫 5. 小于等于 (andLessThanEqual) :id=less-than-equal ```java // 生成“与逻辑”的“小于等于匹配”的 Predicate 条件,如果没有 match 参数或者 match 值为 true 则生成该条件,否则不生成. @@ -76,7 +76,7 @@ orLessThanEqual(String fieldName, Object value) orLessThanEqual(String fieldName, Object value, boolean match) ``` -### 📮 6. 使用示例 +### 📮 6. 使用示例 :id=demo ```java @Test @@ -107,11 +107,11 @@ public void testOrGreaterThanEqual() { } ``` -## 🐢 二、区间匹配的方法 (between) +## 🐢 二、区间匹配的方法 (between) :id=between 区间匹配本质上也是比较匹配类型的特殊形式,API 参数上表现为匹配的边界值有两个(开始值和结束值),参数会多一个,且在某一个边界值为 null 时,会退化成大于等于或者小于等于的匹配条件。所以,这里单独拿出来作介绍说明。 -### ✏️ 1. API 方法 +### ✏️ 1. API 方法 :id=between-methods ```java // 生成“与逻辑”的“匹配区间”的 Predicate 条件,如果没有 match 参数或者 match 值为 true 则生成该条件,否则不生成. @@ -131,14 +131,14 @@ orNotBetween(String fieldName, Object startValue, Object endValue) orNotBetween(String fieldName, Object startValue, Object endValue, boolean match) ``` -### 🖋️ 2. 退化情况说明 +### 🖋️ 2. 退化情况说明 :id=between-introduction - 当开始值或结束值均不为 `null` 时,会生成 `between ... and ...` 的区间条件,不发生退化; - 当开始值不为 `null`,结束值为 `null` 时,会生成大于等于(`>=`)的条件,发生退化; - 当开始值为 `null`,结束值不为 `null` 时,会生成小于等于(`<=`)的条件,发生退化; - 当开始值或结束值均为 `null` 时,将直接抛出异常; -### 🖍️ 3. 使用示例 +### 🖍️ 3. 使用示例 :id=between-demo ```java @Test @@ -175,7 +175,7 @@ public void testBetween() { } ``` -## 🦚 三、模糊匹配的方法 (LIKE) +## 🦚 三、模糊匹配的方法 (LIKE) :id=like Fenix 中的模糊匹配包含四种,分别是: @@ -184,7 +184,7 @@ Fenix 中的模糊匹配包含四种,分别是: - 后缀模糊匹配 `EndsWith` - 以及 SQL 语法通用的任意自定义模式匹配的 `LikePattern` -### 📝 1. 前后模糊匹配的 (Like) +### 📝 1. 前后模糊匹配的 (Like) :id=like-methods ```java // 生成“与逻辑”的“前后模糊匹配”时的 Predicate 条件,如果没有 match 参数或者 match 值为 true 则生成该条件,否则不生成. @@ -204,7 +204,7 @@ orNotLike(String fieldName, Object value) orNotLike(String fieldName, Object value, boolean match) ``` -### 🗂️ 2. 前缀模糊匹配 (StartsWith) +### 🗂️ 2. 前缀模糊匹配 (StartsWith) :id=starts-with-methods ```java // 生成“与逻辑”的“前缀模糊匹配”时的 Predicate 条件,如果没有 match 参数或者 match 值为 true 则生成该条件,否则不生成. @@ -224,7 +224,7 @@ orNotStartsWith(String fieldName, Object value) orNotStartsWith(String fieldName, Object value, boolean match) ``` -### 📈 3. 后缀模糊匹配 (EndsWith) +### 📈 3. 后缀模糊匹配 (EndsWith) :id=ends-with-methods ```java // 生成“与逻辑”的“后缀模糊匹配”时的 Predicate 条件,如果没有 match 参数或者 match 值为 true 则生成该条件,否则不生成. @@ -244,7 +244,7 @@ orNotEndsWith(String fieldName, Object value) orNotEndsWith(String fieldName, Object value, boolean match) ``` -### 📊 4. 自定义模式匹配 (LikePattern) +### 📊 4. 自定义模式匹配 (LikePattern) :id=like-pattern-methods ```java // 生成“与逻辑”的“自定义模式匹配”时的 Predicate 条件,如果没有 match 参数或者 match 值为 true 则生成该条件,否则不生成. @@ -264,7 +264,7 @@ orNotLikePattern(String fieldName, String pattern) orNotLikePattern(String fieldName, String pattern, boolean match) ``` -### 📌 5. 使用示例 +### 📌 5. 使用示例 :id=like-demo ```java @Test @@ -286,11 +286,11 @@ public void testOrLike() { } ``` -## 🦎 四、范围匹配的方法 (IN) +## 🦎 四、范围匹配的方法 (IN) :id=in 范围匹配是指生成 `IN` 范围查条件。 -### 📍 1. API 方法 +### 📍 1. API 方法 :id=in-methods ```java // 生成“与逻辑”的“范围匹配”时的 Predicate 条件,如果没有 match 参数或者 match 值为 true 则生成该条件,否则不生成. @@ -318,7 +318,7 @@ orNotIn(String fieldName, Object[] value) orNotIn(String fieldName, Object[] value, boolean match) ``` -### 📎 2. 使用示例 +### 📎 2. 使用示例 :id=in-demo ```java @Test @@ -332,9 +332,9 @@ public void testIn() { } ``` -## 🐲 五、NULL 匹配 (IS NULL) +## 🐲 五、NULL 匹配 (IS NULL) :id=is-null -### 📐 1. API 方法 +### 📐 1. API 方法 :id=is-null-methods NULL 匹配是指 SQL 中的字段 `IS NULL` 或者 `IS NOT NULL`。主要 API 方法如下: @@ -356,7 +356,7 @@ orIsNotNull(String fieldName) orIsNotNull(String fieldName, boolean match) ``` -### 📏 2.使用示例 +### 📏 2.使用示例 :id=is-null-demo ```java @Test @@ -369,14 +369,14 @@ public void testIsNull() { } ``` -## 🐍 六、自定义任意操作 +## 🐍 六、自定义任意操作 :id=custom Fenix 中仍然提供了让你自定义动态操作的 `doAny` 方法,该方法中需要传递 `AbstractPredicateHandler` 的子类对象,也可以传递 `PredicateHandler` 接口的实现类实例。 - 通常情况下,推荐直接使用 `PredicateHandler` 的匿名实现类的方式来完成,这样就可以简单的通过 `Lambda` 表达式来完成操作。 - 如果你的自定义操作,也想用于 Java Bean 条件注解的情况,那么建议你继承 `AbstractPredicateHandler` 抽象类即可。 -### ✂️ 1. API 方法 +### ✂️ 1. API 方法 :id=custom-methods ```java // 通过传递 AbstractPredicateHandler 的子类对象来完成任意自定义操作,在具体实现类中写相关的动态条件拼接逻辑. @@ -388,7 +388,7 @@ doAny(String fieldName, Object value, PredicateHandler handler) doAny(String fieldName, Object value, PredicateHandler handler, boolean match) ``` -### 🗑️ 2. 示例 +### 🗑️ 2. 示例 :id=custom-demo 以下通过一个简单的 `PredicateHandler` 的 Lambda 表达式来完成字符串第二、三、四个字符分别是 `ava` 的匹配代码。当然,其实你可以直接通过 `andLikePattern` 方法达到目的,但这里我只是做一个演示的示例供你参考。 @@ -411,7 +411,7 @@ public void testFindAllWithDoAny() { } ``` -## 🐊 七、获取 JPA 的 CriteriaBuilder 等对象 +## 🐊 七、获取 JPA 的 CriteriaBuilder 等对象 :id=criteria-builder 基于 `Specification` 的方式在构造动态查询条件的过程中,如果以上的诸多 API 方法仍然不满足你的需求,想通过原生的写法实现,Fenix 中也提供给你使用原生的方式。你可以在构建过程中获取到 Spring Data JPA 中的 `CriteriaBuilder`、`CriteriaQuery`、`From` 等对象实例,从而方便你来按你自己的需求写动态查询的代码。 diff --git a/docs/sp-bean/annotations.md b/docs/sp-bean/annotations.md index 6be3e55..edda315 100644 --- a/docs/sp-bean/annotations.md +++ b/docs/sp-bean/annotations.md @@ -1,8 +1,8 @@ -# 🥗 内置的条件注解 +# 🥗 内置的条件注解 :id=title Fenix 中内置了大量的(约 `44` 个)条件注解([点击这里查看](https://github.com/blinkfox/fenix/tree/develop/src/main/java/com/blinkfox/fenix/specification/annotation)),这些注解均作用在 Java Bean 的属性上。且这些注解的内部处理机制绝大多数都与**基于 Specification 的 Java 链式 API 方式**中的方法相对应,你可以相互对比使用和参考。 -## 🐳 一、比较匹配类型的注解 +## 🐳 一、比较匹配类型的注解 :id=compare-annotations 比较类型的注解是指等于、不等于、大于、大于等于、小于、小于等于等注解,使用方式也几乎相同,且大多都囊括了 `and`、`or`、`andNot`、`orNot` 等情况。 @@ -10,34 +10,34 @@ Fenix 中内置了大量的(约 `44` 个)条件注解([点击这里查看](htt **而被标注的属性值,可以是任何可比较的类型,如:`String`、`Date`、`Integer` 等等。这些类型本质上都实现了 Java 中的 `Comparable` 接口**。 -### 🔨 1. 等于 (@Equals) +### 🔨 1. 等于 (@Equals) :id=equals - `@Equals`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“等值匹配”的 `Predicate` 条件; - `@OrEquals`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“等值匹配”的 `Predicate` 条件; - `@NotEquals`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“等值不匹配”的 `Predicate` 条件; - `@OrNotEquals`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“等值不匹配”的 `Predicate` 条件; -### 🪓 2. 大于 (@GreaterThan) +### 🪓 2. 大于 (@GreaterThan) :id=greater-than - `@GreaterThan`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“大于匹配”的 `Predicate` 条件; - `@OrGreaterThan`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“大于匹配”的 `Predicate` 条件; -### ⛏️ 3. 大于等于 (@GreaterThanEqual) +### ⛏️ 3. 大于等于 (@GreaterThanEqual) :id=greater-than-equal - `@GreaterThanEqual`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“大于等于匹配”的 `Predicate` 条件; - `@OrGreaterThanEqual`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“大于等于匹配”的 `Predicate` 条件; -### 🛠️ 4. 小于 (@LessThan) +### 🛠️ 4. 小于 (@LessThan) :id=less-than - `@LessThan`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“小于匹配”的 `Predicate` 条件; - `@OrLessThan`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“小于匹配”的 `Predicate` 条件; -### 🗡️ 5. 小于等于 (@LessThanEqual) +### 🗡️ 5. 小于等于 (@LessThanEqual) :id=less-than-equal - `@LessThanEqual`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“小于等于匹配”的 `Predicate` 条件; - `@OrLessThanEqual`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“小于等于匹配”的 `Predicate` 条件; -### ⚔️ 6. 使用示例 +### ⚔️ 6. 使用示例 :id=demo ```java /** @@ -55,11 +55,11 @@ private String isbn; private Integer bookTotalPage; ``` -## 🐬 二、区间匹配的注解 (@Between) +## 🐬 二、区间匹配的注解 (@Between) :id=between 区间匹配本质上也是比较匹配类型的特殊形式,不同点在于标注注解的属性类型至少需要两个边界值(开始值和结束值)来表达,且在某一个边界值为 null 时,会退化成大于等于或者小于等于的匹配条件。所以,这里单独拿出来作介绍说明。 -### 🔫 1. 注解介绍 +### 🔫 1. 注解介绍 :id=between-annotation - `@Between`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“等值匹配”的 `Predicate` 条件; - `@OrBetween`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“等值匹配”的 `Predicate` 条件; @@ -70,14 +70,14 @@ private Integer bookTotalPage; **而被标注的属性值的类型,必须是任何可比较的类型,如:`String`、`Date`、`Integer` 等等。这些类型本质上都实现了 Java 中的 `Comparable` 接口**。为了表达开始值和结束值,值的类型只能是 `BetweenValue>`、数组和 List 有序集合三种类型中的某一种才行。如果是数组或者集合,则必须保证第一个值表示的是开始值,第二个值表示的是结束值,其中某一个值可以为 `null`。而 `BetweenValue` 是 Fenxi 中提供的一个二元组类型,用来封装开始值和结束值的类型,**建议使用 `BetweenValue` 作为区间查询的属性值类型**。 -### 🛡️ 2. 退化情况说明 +### 🛡️ 2. 退化情况说明 :id=between-introduction -- 当开始值或结束值均不为 `null` 时,会生成 `between ... and ...` 的区间条件,不发生退化; -- 当开始值不为 `null`,结束值为 `null` 时,会生成大于等于(`>=`)的条件,发生退化; -- 当开始值为 `null`,结束值不为 `null` 时,会生成小于等于(`<=`)的条件,发生退化; -- 当开始值或结束值均为 `null` 时,将直接抛出异常; +- 🔹 当开始值或结束值均不为 `null` 时,会生成 `between ... and ...` 的区间条件,不发生退化; +- 🔹 当开始值不为 `null`,结束值为 `null` 时,会生成大于等于(`>=`)的条件,发生退化; +- 🔹 当开始值为 `null`,结束值不为 `null` 时,会生成小于等于(`<=`)的条件,发生退化; +- 🔹 当开始值或结束值均为 `null` 时,将直接抛出异常; -### 🔧 3. 使用示例 +### 🔧 3. 使用示例 :id=between-demo ```java /** @@ -115,46 +115,46 @@ BetweenValue.ofStart(300) BetweenValue.ofEnd(600) ``` -## 🐠 三、模糊匹配的注解 +## 🐠 三、模糊匹配的注解 :id=like-annotations Fenix 中的模糊匹配包含四种,分别是: -- 前后模糊匹配的 `Like` -- 前缀模糊匹配 `StartsWith` -- 后缀模糊匹配 `EndsWith` -- 以及 SQL 语法通用的任意自定义模式匹配的 `LikePattern` +- 🔹 前后模糊匹配的 `Like` +- 🔹 前缀模糊匹配 `StartsWith` +- 🔹 后缀模糊匹配 `EndsWith` +- 🔹 以及 SQL 语法通用的任意自定义模式匹配的 `LikePattern` **以下的若干个注解,都仅有一个 `value()` 方法,表示数据库对应的实体类的属性字段名称(`fieldName`),如果不填写,则默认使用被标注的 Java Bean 的属性名作为 `fieldName`。而被标注的属性值的,通常是字符串的类型**。 -### 🔩 1. 前后模糊匹配的注解 (@Like) +### 🔩 1. 前后模糊匹配的注解 (@Like) :id=like -- `@Like`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“前后模糊匹配”的 `Predicate` 条件; -- `@OrLike`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“前后模糊匹配”的 `Predicate` 条件; -- `@NotLike`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“前后模糊不匹配”的 `Predicate` 条件; -- `@OrNotLike`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“前后模糊不匹配”的 `Predicate` 条件; +- 🔹 `@Like`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“前后模糊匹配”的 `Predicate` 条件; +- 🔹 `@OrLike`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“前后模糊匹配”的 `Predicate` 条件; +- 🔹 `@NotLike`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“前后模糊不匹配”的 `Predicate` 条件; +- 🔹 `@OrNotLike`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“前后模糊不匹配”的 `Predicate` 条件; -### ⚙️ 2. 前缀模糊匹配的注解 (@StartsWith) +### ⚙️ 2. 前缀模糊匹配的注解 (@StartsWith) :id=starts-with -- `@StartsWith`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“前缀模糊匹配”的 `Predicate` 条件; -- `@OrStartsWith`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“前缀模糊匹配”的 `Predicate` 条件; -- `@NotStartsWith`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“前缀模糊不匹配”的 `Predicate` 条件; -- `@OrNotStartsWith`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“前缀模糊不匹配”的 `Predicate` 条件; +- 🔹 `@StartsWith`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“前缀模糊匹配”的 `Predicate` 条件; +- 🔹 `@OrStartsWith`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“前缀模糊匹配”的 `Predicate` 条件; +- 🔹 `@NotStartsWith`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“前缀模糊不匹配”的 `Predicate` 条件; +- 🔹 `@OrNotStartsWith`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“前缀模糊不匹配”的 `Predicate` 条件; -### ⚖️ 3. 后缀模糊匹配的注解 (@EndsWith) +### ⚖️ 3. 后缀模糊匹配的注解 (@EndsWith) :id=ends-with -- `@EndsWith`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“后缀模糊匹配”的 `Predicate` 条件; -- `@OrEndsWith`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“后缀模糊匹配”的 `Predicate` 条件; -- `@NotEndsWith`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“后缀模糊不匹配”的 `Predicate` 条件; -- `@OrNotEndsWith`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“后缀模糊不匹配”的 `Predicate` 条件; +- 🔹 `@EndsWith`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“后缀模糊匹配”的 `Predicate` 条件; +- 🔹 `@OrEndsWith`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“后缀模糊匹配”的 `Predicate` 条件; +- 🔹 `@NotEndsWith`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“后缀模糊不匹配”的 `Predicate` 条件; +- 🔹 `@OrNotEndsWith`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“后缀模糊不匹配”的 `Predicate` 条件; -### 🔗 4. 自定义模式匹配的注解 (@LikePattern) +### 🔗 4. 自定义模式匹配的注解 (@LikePattern) :id=like-pattern -- `@EndsWith`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“自定义模式匹配”的 `Predicate` 条件; -- `@OrEndsWith`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“自定义模式匹配”的 `Predicate` 条件; -- `@NotEndsWith`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“自定义模式不匹配”的 `Predicate` 条件; -- `@OrNotEndsWith`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“自定义模式不匹配”的 `Predicate` 条件; +- 🔹 `@EndsWith`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“自定义模式匹配”的 `Predicate` 条件; +- 🔹 `@OrEndsWith`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“自定义模式匹配”的 `Predicate` 条件; +- 🔹 `@NotEndsWith`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“自定义模式不匹配”的 `Predicate` 条件; +- 🔹 `@OrNotEndsWith`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“自定义模式不匹配”的 `Predicate` 条件; -### ⛓️ 5. 使用示例 +### ⛓️ 5. 使用示例 :id=like-demo ```java /** @@ -172,22 +172,22 @@ private String name; private String orNotStartsWithName; ``` -## 🐡 四、范围匹配的注解 (@IN) +## 🐡 四、范围匹配的注解 (@IN) :id=in 范围匹配是指生成 `IN` 范围查条件。 -### 🧲 1. 注解介绍 +### 🧲 1. 注解介绍 :id=in-annotations -- `@In`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“等值匹配”的 `Predicate` 条件; -- `@OrIn`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“等值匹配”的 `Predicate` 条件; -- `@NotIn`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“等值不匹配”的 `Predicate` 条件; -- `@OrNotIn`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“等值不匹配”的 `Predicate` 条件; +- 🔹 `@In`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“等值匹配”的 `Predicate` 条件; +- 🔹 `@OrIn`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“等值匹配”的 `Predicate` 条件; +- 🔹 `@NotIn`: 表示使用标注的字段名称和属性值,生成“与逻辑”的“等值不匹配”的 `Predicate` 条件; +- 🔹 `@OrNotIn`: 表示使用标注的字段名称和属性值,生成“或逻辑”的“等值不匹配”的 `Predicate` 条件; **上面的四个个注解与其他注解不同,有两个方法,一个是 `value()` 方法,表示数据库对应的实体类的属性字段名称(`fieldName`),如果不填写,则默认使用被标注的 Java Bean 的属性名作为 `fieldName`,另一个是 `allowNull()` 方法,表示的是后允许匹配属性值集合或数组中的 `null` 值,默认为 `false`。**。 **被标注的属性值的类型可以是数组,也可以是 `Collection` 集合**。 -### 🧰 2. 使用示例 +### 🧰 2. 使用示例 :id=in-demo ```java /** @@ -205,18 +205,18 @@ private List id; private String[] orIds; ``` -## 🦈 五、NULL 匹配的注解 (@IsNull) +## 🦈 五、NULL 匹配的注解 (@IsNull) :id=is-null NULL 匹配是指 SQL 中的字段 `IS NULL` 或者 `IS NOT NULL`。 -### 🧬 1. 注解介绍 +### 🧬 1. 注解介绍 :id=is-null-annotations -- `@IsNull`: 表示使用标注的字段名称,生成“与逻辑”的“IS NULL 匹配”的 `Predicate` 条件; -- `@OrIsNull`: 表示使用标注的字段名称,生成“或逻辑”的“IS NULL 匹配”的 `Predicate` 条件; -- `@IsNotNull`: 表示使用标注的字段名称,生成“与逻辑”的“IS NOT NULL 匹配”的 `Predicate` 条件; -- `@OrIsNotNull`: 表示使用标注的字段名称,生成“或逻辑”的“IS NOT NULL 匹配”的 `Predicate` 条件; +- 🔹 `@IsNull`: 表示使用标注的字段名称,生成“与逻辑”的“IS NULL 匹配”的 `Predicate` 条件; +- 🔹 `@OrIsNull`: 表示使用标注的字段名称,生成“或逻辑”的“IS NULL 匹配”的 `Predicate` 条件; +- 🔹 `@IsNotNull`: 表示使用标注的字段名称,生成“与逻辑”的“IS NOT NULL 匹配”的 `Predicate` 条件; +- 🔹 `@OrIsNotNull`: 表示使用标注的字段名称,生成“或逻辑”的“IS NOT NULL 匹配”的 `Predicate` 条件; -### 🧪 2. 使用示例 +### 🧪 2. 使用示例 :id=is-null-demo ```java /** diff --git a/docs/sp-bean/custom-annotation.md b/docs/sp-bean/custom-annotation.md index 867f7a2..8659782 100644 --- a/docs/sp-bean/custom-annotation.md +++ b/docs/sp-bean/custom-annotation.md @@ -1,8 +1,8 @@ -# 🍜 自定义条件注解 +# 🍜 自定义条件注解 :id=title 如果 Fenix 中内置的注解不满足你的场景,你可以自定义注解和对应注解的处理器来达到自己的目的。 -## 🐙 一、定义你自己的注解 +## 🐙 一、定义你自己的注解 :id=annotation 假设你想定义一个我自己的等值条件的注解 `@MyEquals`,来达到自己的相等条件的处理的能力。注解的代码的定义如下: @@ -22,7 +22,7 @@ public @interface MyEquals { } ``` -## 🐚 二、创建你注解的处理器 +## 🐚 二、创建你注解的处理器 :id=handler 然后,需要创建一个你注解的处理器类 `MyEqualsPredicateHandler.java`,该类须要继承 `AbstractPredicateHandler` 抽象类。然后实现其中的 `getAnnotation()` 和 `buildPredicate()` 方法即可。 @@ -61,11 +61,11 @@ public class MyEqualsPredicateHandler extends AbstractPredicateHandler { } ``` -## 🐌 三、将处理器类加入到初始化配置中 +## 🐌 三、将处理器类加入到初始化配置中 :id=config 最后一步,就是将上述 `MyEqualsPredicateHandler` 的处理器类添加到 Fenix 配置中,使得的系统初始化时将该处理器的实例和注解的映射信息加载到内存中,方便后续使用。 -### 🔭 1. Spring Boot 项目的自定义注解处理器配置 +### 🔭 1. Spring Boot 项目的自定义注解处理器配置 :id=spring-boot-config 如果你是 Spring Boot 项目,那么只需要在 `fenix.predicate-handlers` 属性中添加该处理器的全路径名即可,示例如下: @@ -79,7 +79,7 @@ fenix: - com.xxx.yyy.handler.MyOtherPredicateHandler ``` -### 🔬 2. 非 Spring Boot 项目的自定义注解处理器配置 +### 🔬 2. 非 Spring Boot 项目的自定义注解处理器配置 :id=project-config 如果你的项目不是 Spring Boot 项目,那么在你的初始化代码中,通过 `FenixConfig.add(handler);` 代码来添加处理器的实例即可。示例代码如下: diff --git a/docs/sp-bean/introduction.md b/docs/sp-bean/introduction.md index 36f106a..f2eea34 100644 --- a/docs/sp-bean/introduction.md +++ b/docs/sp-bean/introduction.md @@ -1,12 +1,12 @@ -# 🥣 使用介绍 +# 🥣 使用介绍 :id=title 在 Fenix 中基于 `Specification` 方式书写动态查询,也可以使用普通的 Java Bean,以及将 Bean 中的属性标注上对应匹配条件的注解,就可以将 Java Bean 作为参数传递来完成动态查询了。 -## 🦖 一、总体示例 +## 🦖 一、总体示例 :id=example 下面直接通过示例来说明如何使用。 -### 🔑 1. 继承 FenixJpaSpecificationExecutor 接口 +### 🔑 1. 继承 FenixJpaSpecificationExecutor 接口 :id=extends-executor 同基于 `Specification` API 的方式一样,Java Bean 的注解方式也需要在你的 `BlogRepository` 接口中,继承 `FenixJpaSpecificationExecutor` 接口,该接口实质上是继承自 `JpaSpecificationExecutor` 接口,但提供了更多的**默认接口方法**,且在 API 使用上,可以不用写 `FenixSpecification.of()` 的中间层,更为简单直接。 @@ -16,7 +16,7 @@ public interface BlogRepository extends FenixJpaSpecificationExecutor { } ``` -### 🔐 2. 定义动态查询的 Java Bean 类和查询条件的注解 +### 🔐 2. 定义动态查询的 Java Bean 类和查询条件的注解 :id=annotation 然后,定义一个用于表示各种查询条件的普通 Java Bean 类 `BlogParam`,当然该类也可以是前台传递过来的对象参数,也可以单独定义。该类的各个属性对应某个查询字段,属性上的注解对应查询的匹配方式,某个字段是否生成查询条件的默认判断依据是该属性值是否为空。 @@ -66,7 +66,7 @@ public class BlogParam { } ``` -### 🗝️ 3. 调用方法和测试 +### 🗝️ 3. 调用方法和测试 :id=invoke-and-run 基于 `Specification` 的方式,不需要定义额外的查询方法,也不需要写 `JPQL` (或 SQL) 语句,简单直接。 @@ -101,15 +101,15 @@ public void queryBlogsWithAnnotaion() { 以上就是基于 `Specification` 通过 Java Bean 的条件注解来动态查询的简单使用示例。 -## 🦕 二、自定义属性注解条件的生成匹配逻辑 +## 🦕 二、自定义属性注解条件的生成匹配逻辑 :id=custom-match 匹配 Java Bean 中的属性是否生成对应的条件,默认是通过属性的值是否为 `空` 来识别的。 `空`的判断逻辑有以下几种: -- 普通 Java 对象是 `null`; -- 字符串是否是“大空字符串”,即 `isBlank` 的逻辑; -- 数组或集合是否是 `null` 或内容为空; +- 🔹 普通 Java 对象是 `null`; +- 🔹 字符串是否是“大空字符串”,即 `isBlank` 的逻辑; +- 🔹 数组或集合是否是 `null` 或内容为空; **某个对象如果符合以上逻辑,就表明是 `空` 的,就说明该属性标注的注解字段不会生成对应的查询条件,只有当该属性的值不为 `空` 时才生成。注意:如果属性是原始类型,如:`int`、`long` 等,由于他们没有 `null`,永远不会认为是 `空` 值,所以,建议你的 Java Bean 的对象中都使用包装类型 `Integer`、`Long` 等语义更为明确的类型,因为 `null` 和 `0` 值在特殊场合的含义不尽相同。**。 diff --git a/docs/usage-example.md b/docs/usage-example.md index 012335e..63ce955 100644 --- a/docs/usage-example.md +++ b/docs/usage-example.md @@ -1,8 +1,8 @@ -# 🍉 四种方式的使用示例 +# 🍉 四种方式的使用示例 :id=title 以下的四种方式的示例均以之前的博客信息数据作为示例,你可以根据自己的场景或喜欢的方式来选择动态查询的方式。 -## ✈️ 第一种:基于 JPQL (或 SQL) 的 XML 方式 +## ✈️ 第一种:基于 JPQL (或 SQL) 的 XML 方式 :id=xml-sql 在 `BlogRepository` 中的查询方法使用 `QueryFenix` 注解,用来分页查询博客信息数据: @@ -66,7 +66,7 @@ public void queryMyBlogs() { } ``` -## 🚢 第二种:基于 JPQL (或 SQL) 的 Java API 方式 +## 🚢 第二种:基于 JPQL (或 SQL) 的 Java API 方式 :id=java-sql 在 `BlogRepository` 中的查询方法使用 `QueryFenix` 注解,用来查询所有符合条件的博客信息数据: @@ -141,7 +141,7 @@ public void queryBlogsWithJava() { } ``` -## ⛵ 第三种:基于 Specification 的 Java API 方式 +## ⛵ 第三种:基于 Specification 的 Java API 方式 :id=specification-java-api 基于 `Specification` 的方式,只须要 `BlogRepository` 接口继承 `FenixJpaSpecificationExecutor` 接口即可,当然也可以继承原生的 `JpaSpecificationExecutor` 接口亦可,但更建议直接继承 `FenixJpaSpecificationExecutor` 接口,该接口也继承自 `JpaSpecificationExecutor` 接口,但提供了更多的默认接口方法,且在 API 使用上,可以不用写 `FenixSpecification.of()` 的中间层,更为简单直接。 @@ -184,7 +184,7 @@ public void queryBlogsWithSpecifition() { } ``` -## 🪂 第四种:基于 Specification 的 Java Bean 注解方式 +## 🪂 第四种:基于 Specification 的 Java Bean 注解方式 :id=specification-java-bean 本方式是指通过将 Java Bean 作为参数传递,在 Java Bean 对象的属性中通过查询的条件注解来表明是何种查询匹配方式。当然,同第三种方式一样,`BlogRepository` 接口也须要继承 `FenixJpaSpecificationExecutor` 接口。 diff --git a/docs/xml/custom-tag.md b/docs/xml/custom-tag.md index 7a2eb8a..a4b24d2 100644 --- a/docs/xml/custom-tag.md +++ b/docs/xml/custom-tag.md @@ -1,10 +1,10 @@ -# 🍄 自定义标签 +# 🍄 自定义标签 :id=title 语义化标签生成的动态 SQL 片段和参数大大简化了复杂、动态 SQL 的代码量,但是项目开发的过程中往往还有更多复杂的逻辑来生成某些 SQL,甚至那些逻辑还要被多处使用到,默认的一些标签可能不能够满足开发的需求,那么自定义自己的动态逻辑的SQL 语义化标签来实现就显得很重要了。 所谓自定义标签和处理器就是根据自己的需求来设计自定义的标签名称、匹配条件、参数或者数据库字段等,再通过开发者自定义的处理器来控制生成 SQL 的逻辑,这样就可以达到生成我们需要的动态逻辑的 SQL,这样的标签重大的意义在于 SQL 语义明确,且能够最大化简化 SQL 的书写和功能的复用。 -## 🌄 一、假设业务需求 +## 🌄 一、假设业务需求 :id=business-demand 假设有一个全国的业务管理系统,有这样的业务场景和需求,登录的用户只能查询到自己**管辖区域**的业务数据,而系统中几乎每个查询页面都要做这样的逻辑控制,具体的需求和查询逻辑如下: @@ -13,7 +13,7 @@ - 如果登录人的级别是**省级**用户,那么他能查询到他所管辖的省份下的所有数据; - 如果登录人的级别是**全国中央级**的用户,那么他能查询到所有数据; -## 🏙️ 二、解决方法 +## 🏙️ 二、解决方法 :id=solution 针对这样的需求,使用 Fenix 也有几种方案可以做到, @@ -24,7 +24,7 @@ Fenix 也为开发者扩展自己的 XML SQL 语义化标签提供了支持。 -## 🌅 三、设计 XML 语义化标签 +## 🌅 三、设计 XML 语义化标签 :id=design-xml-tag 根据前面的需求,我们需要传入一个用户 ID(`userId`)的参数和具体业务表的”地区“字段,每个业务表的地区字段可能名称不一样,所以这里也设计为参数。 @@ -37,7 +37,7 @@ Fenix 也为开发者扩展自己的 XML SQL 语义化标签提供了支持。 ``` -## 🌁 四、创建 XML 标签的处理器 +## 🌁 四、创建 XML 标签的处理器 :id=xml-handler 当你定义了自己的标签和属性之后,就需要创建一个能够识别、读取和按需要的逻辑拼接生成出 SQL 片段和参数的标签处理器。 @@ -126,7 +126,7 @@ public class RegionAuthHandler implements FenixHandler { } ``` -## 🌃 五、配置标签和处理器的映射关系 +## 🌃 五、配置标签和处理器的映射关系 :id=tag-handler-mapping 上面的 `RegionAuthHandler` 类中其实已经使用 `@Tagger` 注解配置了标签和处理器之间的映射关系了。`@Tagger` 是一个重复注解,可以在类中配置多个。 @@ -154,7 +154,7 @@ fenix: - `prefix()` 元素是前缀,如:`AND`、`OR` 等,默认值是空字符串,你也可以设置为其他值。 - `symbol()` 元素是操作符,我们的标签中没用这个值,你也可以把他当任何的参数来传,这样 `BuildSource` 参数中就能获取到这个值,你就可以任意拼接参数了。 -## 🌉 六、使用示例 +## 🌉 六、使用示例 :id=demo 由于我是模拟的业务场景,就不再真实的去创建表、初始化数据,并执行 SQL 了,以下就列出使用场景的示例,供你参考即可: @@ -184,7 +184,8 @@ WHERE ... ``` -> **💡 注**: -> 1. 上面的 `XxxEntity` 是假想中的业务实体类, -> 2. `x.region` 是该实体类中对应的地区字段值,这里假设为 `6` 位数的地区编码,前两位数表示省份,前四位数表示地市,全体 `6` 位数表示具体的区县编码。所以,`6` 位数都相同说明是一个区县,前 `4` 位数相同说明是在一个地市,前两位数相同说明是在一个省份。 -> 3. `searchMap` 假设为是传递过来的 `Map` 型参数,当然视具体情况,你也可以传递为 `Bean` 或者单个散参数。这里只是示例。 +**💡 注意事项**: + +- 🔹 上面的 `XxxEntity` 是假想中的业务实体类, +- 🔹 `x.region` 是该实体类中对应的地区字段值,这里假设为 `6` 位数的地区编码,前两位数表示省份,前四位数表示地市,全体 `6` 位数表示具体的区县编码。所以,`6` 位数都相同说明是一个区县,前 `4` 位数相同说明是在一个地市,前两位数相同说明是在一个省份。 +- 🔹 `searchMap` 假设为是传递过来的 `Map` 型参数,当然视具体情况,你也可以传递为 `Bean` 或者单个散参数。这里只是示例。 diff --git a/docs/xml/logic-control.md b/docs/xml/logic-control.md index 8ab792f..b2dd719 100644 --- a/docs/xml/logic-control.md +++ b/docs/xml/logic-control.md @@ -1,10 +1,10 @@ -# 🍆 逻辑控制语法 +# 🍆 逻辑控制语法 :id=title 前面的示例中使用到了 Fenix 的一些内置语义标签,其实 Fenix 一开始就是像 `Mybatis` 一样支持灵活的逻辑控制的,我没有像 `MyBatis` 一样选用 `ONGL` 来做逻辑控制,而是选用了更好的 [MVEL](http://mvel.documentnode.com/) 模版和表达式解析引擎来达到动态控制 SQL 的功能。 !> **💡 注意**:插值语法 `@{}`(或者 `${}`),不能实现绑定命名参数的方式来生成 `JPQL` 语句,某些情况下可能有 SQL 注入的风险,请视具体情况使用。如果要想使用 `JPQL` 绑定命名参数的特性,请使用 `#{}` 的插值语法。 -## 🦅 一、使用示例 +## 🦅 一、使用示例 :id=example Fenix 中的**语义化标签和流程控制语法是可以混合使用的**,请看下面的 Fenix SQL 书写方式,即可容易理解: @@ -36,11 +36,11 @@ Fenix 中的**语义化标签和流程控制语法是可以混合使用的**, !> **注**:`?blog.title != empty` 中的 `?` 是一种更安全的对象属性访问语法,如果你传递的参数是 `Map`,可能 `Map` 中没有这个属性,那么就会报错,如果加上 `?` 的话,可以直接判定为 `false`,而不是报错。 -## 🦆 二、常用标签 +## 🦆 二、常用标签 :id=tags Fenix 的流程控制语法使用的是 `MVEL` 模板引擎,所以,支持所有 `MVEL2.0` 及以上的模板标签,这也预示着 Fenix 动态 SQL 的强大特性。关于 `MVEL2.x` 的表达式语法和模板语法[请参考这里](http://mvel.documentnode.com/)。 -### 🦢 1. @{} 表达式 +### 🦢 1. @{} 表达式 :id=orb-tag `@{}` 表达式是最基本的插值语法,当然 `${}` 语法也可以,但我更建议你使用官方的 `@{}` 语法。它包含对一个对字符串求值的值表达式,并附加到输出的模板中。例如: @@ -50,7 +50,7 @@ Fenix 的流程控制语法使用的是 `MVEL` 模板引擎,所以,支持所 SELECT u FROM User AS u WHERE u.id = '@{user.id}' ``` -### 🦉 2. #{} 表达式 +### 🦉 2. #{} 表达式 :id=hash-tag `#{}` 表达式不是 `MVEL` 提供的语法,是 `Fenix` 增强的语法,主要用来渲染生成 `JPQL` 语句中的命名参数,可以防止 SQL 注入。例如: @@ -60,7 +60,7 @@ SELECT u FROM User AS u WHERE u.id = '@{user.id}' SELECT u FROM User AS u WHERE u.id = #{user.id} ``` -### 🦩 3. @code{} 静默代码标签 +### 🦩 3. @code{} 静默代码标签 :id=code-tag 静默代码标记允许您在模板中执行 `MVEL`表达式代码。它不返回值,并且不以任何方式影响模板的格式。 @@ -87,7 +87,7 @@ SELECT u FROM User AS u WHERE u.id = #{user.id} `MVEL` 模板中的所有块必须用 `@end{}` 标签来终止,除非是 `if-then-else` 结构,其中 `@else{}` 标记表示前一个控制语句的终止。 -### 🐥 5. @foreach{} 循环迭代 +### 🐥 5. @foreach{} 循环迭代 :id=foreach `foreach`标签允许您在模板中迭代集合或数组。 diff --git a/docs/xml/xml-tags.md b/docs/xml/xml-tags.md index 09f1821..8dee473 100644 --- a/docs/xml/xml-tags.md +++ b/docs/xml/xml-tags.md @@ -1,4 +1,4 @@ -# 🌶️ SQL 语义化标签 +# 🌶️ SQL 语义化标签 :id=title Fenix 的核心功能就在于将 SQL 单独写到 XML 文件中,为了增强写动态 SQL 的能力,引入了 `MVEL` 模版引擎,使得我们可以写出动态的 SQL。但是这样的 SQL 中就像 `MyBatis` 一样充斥着 `if/else` 或者 `foreach` 循环等等语句。不仅冗长、而且可读性也不好。 @@ -22,9 +22,9 @@ Fenix 中提供了大量常见场景下的 XML 标签供开发者使用,且这 - [choose](/xml/xml-tags?id=choose) - [set](/xml/xml-tags?id=set) -## 🐈 一、equal +## 🐈 一、equal :id=equal -### 🛀 1. 标签 +### 🛀 1. 标签 :id=equal-tag ```xml @@ -32,14 +32,14 @@ Fenix 中提供了大量常见场景下的 XML 标签供开发者使用,且这 ``` -### 🛌 2. 属性介绍 +### 🛌 2. 属性介绍 :id=equal-property - **field**,表示对应数据库或实体的字段,也可以是数据库的表达式、函数等。**必填**属性。 - **name**,表示JPA 中生成的 JPQL 语句中的命名参数名称,`v2.3.0` 版本新增的属性。当不填写或者内容为空时,将默认根据 `value` 的值来生成命名参数名称。**非必填**属性。该字段通常用来解决 `value` 值比较复杂,为表达式时,生成的命名参数不对的问题。 - **value**,表示参数值,对应 `MVEL` 表达式,也可以是基础数据类型,如:数字、字符串等。**必填**属性。 - **match**,表示匹配条件。**非必填**属性,如果不填此属性,或者内容为空,则视为必然生成此条件 SQL(`JPQL`) 片段;否则解析出的匹配结果为 `true` 时才生成,匹配结果为 `false` 时不生成。 -### 🔪 3. 使用示例 +### 🔪 3. 使用示例 :id=equal-demo ```xml @@ -76,9 +76,9 @@ AND email = :myEmail - `andLessThanEqual`:带 `and` 前缀的小于等于 - `orLessThanEqual`:带 `or` 前缀的小于等于 -## 🐶 三、like +## 🐶 三、like :id=like -### 🧭 1. 标签 +### 🧭 1. 标签 :id=like-tag ```xml @@ -91,7 +91,7 @@ AND email = :myEmail ``` -### 🧱 2. 属性介绍 +### 🧱 2. 属性介绍 :id=like-property - **field**,表示对应数据库或实体的字段,也可以是数据库的表达式、函数等。**必填**属性。 - **name**,表示JPA 中生成的 JPQL 语句中的命名参数名称,`v2.3.0` 版本新增的属性。当不填写或者内容为空时,将默认根据 `value` 的值来生成命名参数名称。**非必填**属性。该字段通常用来解决 `value` 值比较复杂,为表达式时,生成的命名参数不对的问题。 @@ -99,7 +99,7 @@ AND email = :myEmail - **pattern**,表示 `like` 匹配的模式,如:`abc%`、`_bc`等,只能是静态文本内容。**条件必填**属性。`pattern` 和 `value` 只能存在一个,`pattern` 用来指定自定义的匹配模式。 - **match**,表示匹配条件。**非必填**属性,如果不填此属性,或者内容为空,则视为必然生成此条件 SQL 片段;否则匹配结果为 `true` 时才生成,匹配结果为 `false` 时不生成。 -### 💈 3. 使用示例 +### 💈 3. 使用示例 :id=like-demo ```xml @@ -115,11 +115,11 @@ u.email NOT LIKE '%@gmail.com' AND u.email NOT LIKE :myEmail ``` -## 🦊 四、startsWith +## 🦊 四、startsWith :id=starts-with `startsWith` 是 `like` 标签的特殊形式,表示按前缀来做模糊匹配。 -### 🛎️ 1. 标签 +### 🛎️ 1. 标签 :id=starts-with-tag ```xml @@ -131,14 +131,14 @@ AND u.email NOT LIKE :myEmail ``` -### ⌛ 2. 属性介绍 +### ⌛ 2. 属性介绍 :id=starts-with-property - **field**,表示对应数据库或实体的字段,也可以是数据库的表达式、函数等。**必填**属性。 - **name**,表示JPA 中生成的 JPQL 语句中的命名参数名称,`v2.3.0` 版本新增的属性。当不填写或者内容为空时,将默认根据 `value` 的值来生成命名参数名称。**非必填**属性。该字段通常用来解决 `value` 值比较复杂,为表达式时,生成的命名参数不对的问题。 - **value**,表示参数值,对应 `MVEL` 表达式,也可以是基础数据类型,如:数字、字符串等。**必填**属性。生成的 SQL 片段是按前缀来匹配的,即:`xxx%`。 - **match**,表示匹配条件。**非必填**属性,如果不填此属性,或者内容为空,则视为必然生成此条件 SQL 片段;否则匹配结果为 `true` 时才生成,匹配结果为 `false` 时不生成。 -### ⌚ 3. 使用示例 +### ⌚ 3. 使用示例 :id=starts-with-demo ```xml @@ -154,11 +154,11 @@ AND u.name NOT LIKE :user_name AND u.name LIKE :myName ``` -## 🐺 五、endsWith +## 🐺 五、endsWith :id=ends-with `endsWith` 也是 `like` 标签的特殊形式,同 `startsWith` 标签相反,表示按后缀来做模糊匹配。 -### ⏰ 1. 标签 +### ⏰ 1. 标签 :id=ends-with-tag ```xml @@ -170,14 +170,14 @@ AND u.name LIKE :myName ``` -### ⛱️ 2. 属性介绍 +### ⛱️ 2. 属性介绍 :id=ends-with-property - **field**,表示对应数据库或实体的字段,也可以是数据库的表达式、函数等。**必填**属性。 - **name**,表示JPA 中生成的 JPQL 语句中的命名参数名称,`v2.3.0` 版本新增的属性。当不填写或者内容为空时,将默认根据 `value` 的值来生成命名参数名称。**非必填**属性。该字段通常用来解决 `value` 值比较复杂,为表达式时,生成的命名参数不对的问题。 - **value**,表示参数值,对应 `MVEL` 表达式,也可以是基础数据类型,如:数字、字符串等。**必填**属性。生成的 SQL 片段是按后缀来匹配的,即:`%xxx`。 - **match**,表示匹配条件。**非必填**属性,如果不填此属性,或者内容为空,则视为必然生成此条件 SQL 片段;否则匹配结果为 `true` 时才生成,匹配结果为 `false` 时不生成。 -### 🌡️ 3. 使用示例 +### 🌡️ 3. 使用示例 :id=ends-with-demo ```xml @@ -193,9 +193,9 @@ AND u.name NOT LIKE :user_name AND u.name LIKE :myName ``` -## 🐯 六、between +## 🐯 六、between :id=between -### 🧨 1. 标签 +### 🧨 1. 标签 :id=between-tag ```xml @@ -203,7 +203,7 @@ AND u.name LIKE :myName ``` -### 🎈 2. 属性介绍 +### 🎈 2. 属性介绍 :id=between-property - **field**,表示对应数据库或实体的字段,可以是数据库的表达式、函数等。**必填**属性。 - **startName**,表示JPA 中生成的开始值的 JPQL 语句中的命名参数名称,`v2.3.0` 版本新增的属性。当不填写或者内容为空时,将默认根据 `start` 的值来生成命名参数名称。**非必填**属性。该字段通常用来解决 `start` 值比较复杂,为表达式时,生成的命名参数不对的问题。 @@ -214,7 +214,7 @@ AND u.name LIKE :myName !> **注意**:Fenix 中对 start 和 end 的空判断是检测是否是 `null`,而不是空字符串,`0`等情况。所以,如果 `start` 和 `end` 的某一个值为 `null` 时,区间查询将退化为大于等于(`>=`)或者小于等于(`<=`)的查询。 -### 🎉 3. 使用示例 +### 🎉 3. 使用示例 :id=between-demo ```xml @@ -230,9 +230,9 @@ AND u.name LIKE :myName - 当 `start` 不为 `null`,`end`不为`null`,则生成的 SQL 片段为:`AND u.age BETWEEN :startAge AND :endAge`; - 当 `start` 为 `null`,`end`为`null`,则不生成SQL片段; -## 🦁 七、in +## 🦁 七、in :id=in -### 🎊 1. 标签 +### 🎊 1. 标签 :id=in-tag ```xml @@ -245,14 +245,14 @@ AND u.name LIKE :myName ``` -### 🎏 2. 属性介绍 +### 🎏 2. 属性介绍 :id=in-property - **field**,表示对应数据库或实体的字段,可以是数据库的表达式、函数等。**必填**属性。 - **name**,表示JPA 中生成的 JPQL 语句中的命名参数名称,`v2.3.0` 版本新增的属性。当不填写或者内容为空时,将默认根据 `value` 的值来生成命名参数名称。**非必填**属性。该字段通常用来解决 `value` 值比较复杂,为表达式时,生成的命名参数不对的问题。 - **value**,表示参数的集合,值可以是数组,也可以是 `Collection` 集合,还可以是单个的值。**必填**属性。 - **match**,表示匹配条件。**非必填**属性,如果不填此属性,或者内容为空,则视为必然生成此条件 SQL 片段;否则匹配结果为 `true` 时才生成,匹配结果为 `false`时不生成。 -### 🎀 3. 使用示例 +### 🎀 3. 使用示例 :id=in-demo ```xml @@ -266,7 +266,7 @@ AND u.sex in :mySex ## 🐱 八、is null :id=is-null -### 🎁 1. 标签 +### 🎁 1. 标签 :id=is-null-tag ```xml @@ -280,12 +280,12 @@ AND u.sex in :mySex ``` -### 🤿 2. 属性介绍 +### 🤿 2. 属性介绍 :id=is-null-property - **field**,表示对应数据库或实体的字段,可以是数据库的表达式、函数等。**必填**属性。 - **match**,表示匹配条件。**非必填**属性,如果不填此属性,或者内容为空,则视为必然生成此条件 SQL 片段;否则匹配结果为 `true`时才生成,匹配结果为 `false`时不生成。 -### 🪁 3. 使用示例 +### 🪁 3. 使用示例 :id=is-null-demo ```xml @@ -293,11 +293,11 @@ AND u.sex in :mySex AND u.n_age IS NULL ``` -## 🐎 九、where +## 🐎 九、where :id=where `where` 标签主要用于在全动态 SQL 的场景中消除 `WHERE` 关键字后面的 `AND` 或者 `OR` 关键字,防止拼接出的动态 SQL 语法不对。 -### 🔮 1. 标签 +### 🔮 1. 标签 :id=where-tag 下面是 `where` 标签的使用方式,两种方式是等价的,看情况选用一种方式即可。 @@ -311,7 +311,7 @@ AND u.n_age IS NULL ``` -### 🧿 2. 使用示例 +### 🧿 2. 使用示例 :id=where-demo ```xml @@ -361,11 +361,11 @@ AND u.n_age IS NULL ``` -## 🐅 十、text +## 🐅 十、text :id=text `text` 标签主要用于在标签内部自定义任何需要的文本和传递的参数,为 SQL 书写提供更多的灵活性。 -### 🕹️ 1. 标签 +### 🕹️ 1. 标签 :id=text-tag ```xml @@ -373,12 +373,12 @@ AND u.n_age IS NULL ``` -### 🧸 2. 属性介绍 +### 🧸 2. 属性介绍 :id=text-property - **value**,表示 `text` 块中需要传递的 `Map` 型参数。**非必填**属性。`Map` 中的 `key` 必须是“死”字符串,用于和 `JPQL` 的命名参数相呼应,`value` 的值才可以被动态解析; - **match**,表示匹配条件。**非必填**属性,如果不填此属性,或者内容为空,则视为必然生成此条件 SQL 片段;否则匹配结果为 `true`时才生成,匹配结果为 `false`时不生成。 -### 🖼️ 3. 使用示例 +### 🖼️ 3. 使用示例 :id=text-demo ```xml @@ -392,11 +392,11 @@ AND u.n_age IS NULL ``` -## 🐴 十一、import +## 🐴 十一、import :id=import `import` 标签主要用于在 Fenix 标签中导入其它的 `` 节点,便于 SQL 逻辑的进一步复用。 -### 🧵 1. 标签 +### 🧵 1. 标签 :id=import-tag ```xml @@ -405,14 +405,14 @@ AND u.n_age IS NULL ``` -### 🧶 2. 属性介绍 +### 🧶 2. 属性介绍 :id=import-property - **namespace**,表示需要引用导入的节点所在的 `XML` 文件的命名空间,**非必填**属性。如果如果不填此属性,则视为仅从本 `XML` 文件中查找和导入 `fenixId` 的节点。 - **fenixId**,表示要引用导入的 `` 节点的 `id`,**必填**属性。 - **value**,表示需要传入到要引用的 `` 节点中的上下文参数值,**非必填**属性。如果不填此属性,则会传递和使用最顶层的上下文参数。 - **match**,表示匹配条件。**非必填**属性,如果不填此属性,或者内容为空,则视为必然生成此条件 SQL 片段;否则匹配结果为 `true` 时才生成,匹配结果为 `false`时不生成。 -### 🛍️ 3. 使用示例 +### 🛍️ 3. 使用示例 :id=import-demo ```xml @@ -472,11 +472,11 @@ AND u.n_age IS NULL ``` -## 🐄 十二、choose +## 🐄 十二、choose :id=choose `choose` 标签主要用于解决"较多的"多分支条件选择逻辑,对应的即是Java中 `if/else if/ ... /else if/else` 这种逻辑。 -### 📿 1. 标签 +### 📿 1. 标签 :id=choose-tag ```xml ``` -### 📯 2. 属性介绍 +### 📯 2. 属性介绍 :id=choose-property - **when{x}**,表示匹配条件,可以写“无数”个,对应于Java中的 `if/else if` 条件。**必填**属性,如果不填此属性,表示 `false`,将直接进入 `else` 的表达式逻辑块中。 - **then{x}**,表示需要执行的逻辑,和 `when` 向对应,可以写“无数”个,内容是字符串或者是 `MVEL` 的字符串模版,**必填**属性。如果如果不填此属性,即使满足了对应的 `when` 条件,也不会做 SQL 的拼接操作。 - **else**,表示所有 `when` 条件都不满足时才执行的逻辑,内容是字符串或者 `MVEL` 的字符串模版,**非必填**属性。如果不填此属性,则表示什么都不做(这样就无任何意义了)。 -### 🎙️ 3. 使用示例 +### 🎙️ 3. 使用示例 :id=choose-demo ```xml @@ -513,13 +513,13 @@ AND u.n_age IS NULL ``` -## 🐄 十三、set +## 🐄 十三、set :id=set `set` 标签主要用于动态生成 `update` 语句中的 SQL 片段。 > **💡 注**:从 `2.4.0` 版本之后,增加了 `saveOrUpdateByNotNullProperties` 的**增量更新非`null`字段**的方法。大多数情况下,你可以不使用 `set` 标签了。 -### 📻 1. 标签 +### 📻 1. 标签 :id=set-tag ```xml ``` -### 📱 2. 属性介绍 +### 📱 2. 属性介绍 :id=set-property - **field{x}**,表示对应数据库或实体的字段,可以写“无数”个。**必填**属性。 - **value{x}**,表示对应字段的值,可以写“无数”个,对应 `MVEL` 表达式,也可以是基础数据类型,如:数字、字符串等,与相同序号的 `field` 值相对应。**非必填**属性。不填写此值则是 `null` 值。 - **match{x}**,表示匹配条件,可以写“无数”个,与相同序号的 `field` 和 `value` 值相对应。**非必填**属性,如果不填此属性,或者内容为空,则视为必然生成此条件 SQL 片段;否则匹配结果为 `true` 时才生成,匹配结果为 `false`时不生成。 -### ☎️ 3. 使用示例 +### ☎️ 3. 使用示例 :id=set-demo ```xml