Skip to content

Commit cb61b46

Browse files
committed
Documentation
1 parent 3b3e832 commit cb61b46

File tree

6 files changed

+174
-72
lines changed

6 files changed

+174
-72
lines changed

CHANGELOG.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This log will detail notable changes to MyBatis Dynamic SQL. Full details are available on the GitHub milestone pages.
44

5-
## Release 1.3.0 - Unreleased
5+
## Release 1.3.0 - May 4, 2021
66

77
GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.3.0+](https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.3.0+)
88

@@ -13,12 +13,13 @@ The major themes of this release include the following:
1313
1. Add support for subqueries in select statements - both in a from clause and a join clause.
1414
1. Add support for the "exists" and "not exists" operator. This will work in "where" clauses anywhere
1515
they are supported.
16-
1. Refactor and improve the built-in conditions for consistency (see below)
16+
1. Refactor and improve the built-in conditions for consistency (see below). There is one breaking change also
17+
detailed below.
1718
1. Continue to refine the Kotlin DSL. Many changes to the Kotlin DSL are internal and should be source code
1819
compatible with existing code. There is one breaking change detailed below.
1920
1. Remove deprecated code from prior releases.
2021

21-
### Built-In Condition Refactoring
22+
### Built-In Condition Refactoring and Breaking Change
2223
All built-in conditions have been refactored. The changes should have little impact for the vast majority of users.
2324
However, there are some changes in behavior and one breaking change.
2425

@@ -38,7 +39,7 @@ However, there are some changes in behavior and one breaking change.
3839
1. All the "WhenPresent" conditions have been removed as separate classes. The methods that produced these conditions
3940
in the SqlBuilder remain, and they will now produce a condition with a "NotNull" filter applied. So at the API level
4041
things will function exactly as before, but the intermediate classes will be different.
41-
1. One breaking change is that the builder for List value conditions has been removed without replacement. If you
42+
1. One **breaking change** is that the builder for List value conditions has been removed without replacement. If you
4243
were using this builder to supply a "value stream transformer", then the replacement is to build a new List value
4344
condition and then call the "map" and "filter" methods as needed. For example, prior code looked like this
4445

@@ -87,7 +88,7 @@ and confusing method overload problems for methods that did not offer any real b
8788
methods in the Java DSL, then in the Kotlin DSL you will have to change the function argument from a supplier to the
8889
actual value itself.
8990

90-
A breaking change is that Kotlin support for `select` and `count` statements has been refactored. This will not impact code
91+
A **breaking change** is that Kotlin support for `select` and `count` statements has been refactored. This will not impact code
9192
created by MyBatis generator. It will have an impact on Spring/Kotlin users as well as MyBatis users that coded joins or
9293
other queries directly in Kotlin. The difference is that the `from` clause has been moved inside the lambda for select
9394
and count statements.
@@ -107,7 +108,7 @@ The new code looks like this:
107108
}
108109
```
109110

110-
This change makes the Kotlin DSL a bit more consistent and also makes it easier to implement subquery support in the
111+
This change makes the Kotlin DSL more consistent and also makes it easier to implement subquery support in the
111112
Kotlin DSL.
112113

113114
### Added
Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
# Complex Queries
2-
Enhancements in version 1.1.2 make it easier to code complex queries. The Select DSL is implemented as a set of related objects. As the select statement is built, intermediate objects of various types are returned from the various methods that implement the DSL. The select statement can be completed by calling the `build()` method many of the intermediate objects. Prior to version 1.1.2, it was necessary to call `build()` on the **last** intermediate object. This restriction has been removed, and it is now possible to call `build()` on **any** intermediate object. This, along with several other enhancements, has simplified the coding of complex queries.
2+
Enhancements in version 1.1.2 make it easier to code complex queries. The Select DSL is implemented as a set of related
3+
objects. As the select statement is built, intermediate objects of various types are returned from the various methods
4+
that implement the DSL. The select statement can be completed by calling the `build()` method many of the intermediate
5+
objects. Prior to version 1.1.2, it was necessary to call `build()` on the **last** intermediate object. This
6+
restriction has been removed, and it is now possible to call `build()` on **any** intermediate object. This, along with
7+
several other enhancements, has simplified the coding of complex queries.
38

4-
For example, suppose you want to code a complex search on a Person table. The search parameters are id, first name, and last name. The rules are:
9+
For example, suppose you want to code a complex search on a Person table. The search parameters are id, first name,
10+
and last name. The rules are:
511

612
1. If an id is entered, use the id and ignore the other search parameters
713
1. If an id is not entered, then do a fuzzy search based on the other parameters
@@ -19,15 +25,15 @@ public SelectStatementProvider search(Integer targetId, String fName, String lNa
1925
.and(id, isEqualTo(targetId));
2026
} else {
2127
builder
22-
.and(firstName, isLike(fName).when(Objects::nonNull).then(s -> "%" + s + "%")) // (4)
23-
.and(lastName, isLikeWhenPresent(lName).then(this::addWildcards)); // (5)
28+
.and(firstName, isLike(fName).filter(Objects::nonNull).map(s -> "%" + s + "%")) // (4) (5)
29+
.and(lastName, isLikeWhenPresent(lName).map(this::addWildcards)); // (6)
2430
}
2531

2632
builder
2733
.orderBy(lastName, firstName)
28-
.fetchFirst(50).rowsOnly(); // (6)
34+
.fetchFirst(50).rowsOnly(); // (7)
2935

30-
return builder.build().render(RenderingStrategies.MYBATIS3); // (7)
36+
return builder.build().render(RenderingStrategies.MYBATIS3); // (8)
3137
}
3238

3339
public String addWildcards(String s) {
@@ -37,11 +43,21 @@ public String addWildcards(String s) {
3743

3844
Notes:
3945

40-
1. Note the use of the `var` keyword here. If you are using an older version of Java, the actual type is `QueryExpressionDSL<SelectModel>.QueryExpressionWhereBuilder`
41-
1. Here we are calling `where()` with no parameters. This sets up the builder to accept conditions further along in the code. If no conditions are added, then the where clause will not be rendered
42-
1. This `if` statement implements the rules of the search. If an ID is entered , use it. Otherwise, do a fuzzy search based on first name and last name.
43-
1. The `then` statement on this line allows you to change the parameter value before it is placed in the parameter Map. In this case we are adding SQL wildcards to the start and end of the search String - but only if the search String is not null. If the search String is null, the lambda will not be called and the condition will not render
44-
1. This shows using a method reference instead of a lambda on the `then`. Method references allow you to more clearly express intent. Note also the use of the `isLikeWhenPresent` condition which is a built in condition that checks for nulls
45-
1. It is a good idea to limit the number of rows returned from a search. The library now supports `fetch first` syntax for limiting rows
46-
1. Note that we are calling the `build` method from the intermediate object retrieved in step 1. It is no longer necessary to call `build` on the last object returned from a select builder
46+
1. Note the use of the `var` keyword here. If you are using an older version of Java, the actual type is
47+
`QueryExpressionDSL<SelectModel>.QueryExpressionWhereBuilder`
48+
1. Here we are calling `where()` with no parameters. This sets up the builder to accept conditions further along in the
49+
code. If no conditions are added, then the where clause will not be rendered
50+
1. This `if` statement implements the rules of the search. If an ID is entered , use it. Otherwise, do a fuzzy search
51+
based on first name and last name.
52+
1. The `filter` method on this line will mark the condition as unrenderable if the filter is not satisfied.
53+
1. The `map` statement on this line allows you to change the parameter value before it is placed in the parameter Map.
54+
In this case we are adding SQL wildcards to the start and end of the search String - but only if the search String
55+
is not null. If the search String is null, the lambda will not be called and the condition will not render
56+
1. This line shows the use of a method reference instead of a lambda on the `map`. Method references allow you to more
57+
clearly express intent. Note also the use of the `isLikeWhenPresent` function which is a built-in function that
58+
applies a non-null filter
59+
1. It is a good idea to limit the number of rows returned from a search. The library now supports `fetch first` syntax
60+
for limiting rows
61+
1. Note that we are calling the `build` method from the intermediate object retrieved in step 1. It is no longer
62+
necessary to call `build` on the last object returned from a select builder
4763

src/site/markdown/docs/conditions.md

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ Simple conditions are the most common - they render the basic SQL operators.
3333
| Null | where(foo, isNull()) | `where foo is null` |
3434

3535

36-
## Sub-Selects
36+
## Subqueries
3737

38-
Many conditions can be rendered with sub selects.
38+
Many conditions can be rendered with subqueries.
3939

4040
| Condition | Example | Result |
4141
|-----------|---------|--------|
@@ -62,6 +62,58 @@ Column comparison conditions can be used to write where clauses comparing the va
6262
| Less Than or Equals | where(foo, isLessThanOrEqualTo(bar)) | `where foo <= bar` |
6363
| Not Equals | where(foo, isNotEqualTo(bar)) | `where foo <> bar` |
6464

65+
## Value Transformation
66+
67+
All conditions (except `isNull` and `isNotNull`) support a `map` function that allows you to transform the value(s)
68+
associated with the condition before the statement is rendered. The map function functions similarly to the JDK
69+
standard `map` functions on Streams and Optionals - it allows you to transform a value and change the data type.
70+
71+
For example, suppose you want to code a wild card search with the SQL `like` operator. To make this work, you will
72+
need to append SQL wildcards to the search value. This can be accomplished directly i the condition with a `map`
73+
method as follows:
74+
75+
```java
76+
List<Animal> search(String searchName) {
77+
SelectStatementProvider selectStatement=select(id,animalName,bodyWeight,brainWeight)
78+
.from(animalData)
79+
.where(animalName,isLike(searchName).map(s->"%"+s+"%"))
80+
.orderBy(id)
81+
.build()
82+
.render(RenderingStrategies.MYBATIS3);
83+
84+
...
85+
}
86+
```
87+
88+
You can see the `map` method accepts a lambda that adds SQL wildcards to the `searchName` field. This is more succint
89+
if you use a method reference:
90+
91+
```java
92+
List<Animal> search(String searchName){
93+
SelectStatementProvider selectStatement=select(id,animalName,bodyWeight,brainWeight)
94+
.from(animalData)
95+
.where(animalName,isLike(searchName).map(this::appendWildCards))
96+
.orderBy(id)
97+
.build()
98+
.render(RenderingStrategies.MYBATIS3);
99+
}
100+
101+
String appendWildCards(String in) {
102+
return "%" + in + "%";
103+
}
104+
```
105+
106+
The `map` on each condition accepts a lambda expression that can be used to transform the value(s) associated with the
107+
condition. The lambda is the standard JDK type `Function&lt;T,R&gt;` where `T` is the type of the condition and `R`
108+
is the output type. For most conditions this should be fairly simple to understand. The unusual cases are detailed
109+
below:
110+
111+
1. The `Between` and `NotBetween` conditions have `map` methods that accept one or two map functions. If you pass
112+
one function, it will be applied to both values in the condition. If you supply two functions, then they will be
113+
applied to the first and second values respectively.
114+
1. The `In` and `NotIn` conditions accept a single mapping function and it will be applied to all values in the
115+
collection of values in the condition.
116+
65117
## Optional Conditions
66118

67119
All conditions support optionality - meaning they can be configured to render into the final SQL if a configured test
@@ -78,8 +130,8 @@ For example, you could code a search like this:
78130
SelectStatementProvider selectStatement = select(id, animalName, bodyWeight, brainWeight)
79131
.from(animalData)
80132
.where(animalName, isEqualTo(animalName_).filter(Objects::nonNull))
81-
.and(bodyWeight, isEqualToWhen(bodyWeight_).filter(Objects::nonNull))
82-
.and(brainWeight, isEqualToWhen(brainWeight_).filter(Objects::nonNull))
133+
.and(bodyWeight, isEqualTo(bodyWeight_).filter(Objects::nonNull))
134+
.and(brainWeight, isEqualTo(brainWeight_).filter(Objects::nonNull))
83135
.build()
84136
.render(RenderingStrategies.MYBATIS3);
85137
...
@@ -96,20 +148,22 @@ table lists the optional conditions and shows how to use them:
96148

97149
| Condition | Example | Rendering Rules |
98150
|-----------|---------|-----------------|
99-
| Between| where(foo, isBetween(x).and(y).when(BiPredicate)) | The library will pass x and y to the BiPredicate's test method. The condition will render if BiPredicate.test(x, y) returns true |
100-
| Equals | where(foo, isEqualTo(x).when(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
101-
| Greater Than | where(id, isGreaterThan(x).when(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
102-
| Greater Than or Equals | where(id, isGreaterThanOrEqualTo(x).when(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
103-
| Less Than | where(id, isLessThan(x).when(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
104-
| Less Than or Equals | where(id, isLessThanOrEqualTo(x).when(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
105-
| Like | where(id, isLike(x).when(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
106-
| Like Case Insensitive | where(id, isLikeCaseInsensitive(x).when(Predicate&lt;String&gt;)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
107-
| Not Between | where(id, isNotBetween(x).and(y).when(BiPredicate)) | The library will pass x and y to the BiPredicate's test method. The condition will render if BiPredicate.test(x, y) returns true |
108-
| Not Equals | where(id, isNotEqualTo(x).when(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
109-
| Not Like | where(id, isNotLike(x).when(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
110-
| Not Like Case Insensitive | where(id, isNotLikeCaseInsensitive(x).when(Predicate&lt;String&gt;)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
111-
| Not Null | where(id, isNotNull().when(BooleanSupplier) | The condition will render if BooleanSupplier.getAsBoolean() returns true |
112-
| Null | where(id, isNull().when(BooleanSupplier) | The condition will render if BooleanSupplier.getAsBoolean() returns true |
151+
| Between| where(foo, isBetween(x).and(y).filter(BiPredicate)) | The library will pass x and y to the BiPredicate's test method. The condition will render if BiPredicate.test(x, y) returns true |
152+
| Between| where(foo, isBetween(x).and(y).filter(Predicate)) | The library will invoke the Predicate's test method twice - once with x, once with y. The condition will render if both function calls return true |
153+
| Equals | where(foo, isEqualTo(x).filter(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
154+
| Greater Than | where(id, isGreaterThan(x).filter(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
155+
| Greater Than or Equals | where(id, isGreaterThanOrEqualTo(x).filter(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
156+
| Less Than | where(id, isLessThan(x).filter(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
157+
| Less Than or Equals | where(id, isLessThanOrEqualTo(x).filter(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
158+
| Like | where(id, isLike(x).filter(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
159+
| Like Case Insensitive | where(id, isLikeCaseInsensitive(x).filter(Predicate&lt;String&gt;)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
160+
| Not Between | where(id, isNotBetween(x).and(y).filter(BiPredicate)) | The library will pass x and y to the BiPredicate's test method. The condition will render if BiPredicate.test(x, y) returns true |
161+
| Not Between| where(foo, isNotBetween(x).and(y).filter(Predicate)) | The library will invoke the Predicate's test method twice - once with x, once with y. The condition will render if both function calls return true |
162+
| Not Equals | where(id, isNotEqualTo(x).filter(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
163+
| Not Like | where(id, isNotLike(x).filter(Predicate)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
164+
| Not Like Case Insensitive | where(id, isNotLikeCaseInsensitive(x).filter(Predicate&lt;String&gt;)) | The library will pass x to the Predicate's test method. The condition will render if Predicate.test(x) returns true |
165+
| Not Null | where(id, isNotNull().filter(BooleanSupplier) | The condition will render if BooleanSupplier.getAsBoolean() returns true |
166+
| Null | where(id, isNull().filter(BooleanSupplier) | The condition will render if BooleanSupplier.getAsBoolean() returns true |
113167

114168
### "When Present" Condition Builders
115169
The library supplies several methods that supply conditions to be used in the common case of checking for null

0 commit comments

Comments
 (0)