Skip to content

Commit dca9657

Browse files
authored
Merge pull request #111 from jeffgbutler/master
Add a new documentation page for complex queries
2 parents 7fd1b21 + eb4e63b commit dca9657

File tree

5 files changed

+244
-1
lines changed

5 files changed

+244
-1
lines changed
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Complex Queries
2+
Enhancements in version 1.1.2 make it easier to code complex queries.
3+
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:
5+
6+
1. If an id is entered, use the id and ignore the other search parameters
7+
1. If an id is not entered, then do a fuzzy search based on the other parameters
8+
9+
This can be implemented with code like the following...
10+
11+
```java
12+
public SelectStatementProvider search(Integer targetId, String fName, String lName) {
13+
var builder = select(id, firstName, lastName) // (1)
14+
.from(person)
15+
.where(); // (2)
16+
17+
if (targetId != null) { // (3)
18+
builder
19+
.and(id, isEqualTo(targetId));
20+
} else {
21+
builder
22+
.and(firstName, isLike(fName).when(Objects::nonNull).then(s -> "%" + s + "%")) // (4)
23+
.and(lastName, isLikeWhenPresent(lName).then(this::addWildcards)); // (5)
24+
}
25+
26+
builder
27+
.orderBy(lastName, firstName)
28+
.fetchFirst(50).rowsOnly(); // (6)
29+
30+
return builder.build().render(RenderingStrategy.MYBATIS3); // (7)
31+
}
32+
33+
public String addWildcards(String s) {
34+
return "%" + s + "%";
35+
}
36+
```
37+
38+
Notes:
39+
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 place 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
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
47+

src/site/site.xml

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
<item href="docs/whereClauses.html" name="WHERE Clause Support" >
4242
<item href="docs/conditions.html" name="WHERE Conditions"/>
4343
</item>
44-
<item href="docs/select.html" name="SELECT Statements" />
44+
<item href="docs/select.html" name="SELECT Statements" >
45+
<item href="docs/complexQueries.html" name="Complex Queries"/>
46+
</item>
4547
<item href="docs/delete.html" name="DELETE Statements" />
4648
<item href="docs/insert.html" name="INSERT Statements" />
4749
<item href="docs/update.html" name="UPDATE Statements" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* Copyright 2016-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package examples.complexquery;
17+
18+
import static examples.complexquery.PersonDynamicSqlSupport.firstName;
19+
import static examples.complexquery.PersonDynamicSqlSupport.id;
20+
import static examples.complexquery.PersonDynamicSqlSupport.lastName;
21+
import static examples.complexquery.PersonDynamicSqlSupport.person;
22+
import static org.assertj.core.api.Assertions.assertThat;
23+
import static org.mybatis.dynamic.sql.SqlBuilder.*;
24+
25+
import java.util.Objects;
26+
27+
import org.junit.jupiter.api.Test;
28+
import org.mybatis.dynamic.sql.render.RenderingStrategy;
29+
import org.mybatis.dynamic.sql.select.QueryExpressionDSL;
30+
import org.mybatis.dynamic.sql.select.SelectModel;
31+
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
32+
33+
public class ComplexQueryTest {
34+
35+
@Test
36+
public void testId() {
37+
SelectStatementProvider selectStatement = search(2, null, null);
38+
39+
String expected = "select person_id, first_name, last_name"
40+
+ " from Person"
41+
+ " where person_id = #{parameters.p1}"
42+
+ " order by last_name, first_name"
43+
+ " fetch first #{parameters._fetchFirstRows} rows only";
44+
45+
assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
46+
assertThat(selectStatement.getParameters().get("p1")).isEqualTo(2);
47+
assertThat(selectStatement.getParameters().get("_fetchFirstRows")).isEqualTo(50L);
48+
}
49+
50+
@Test
51+
public void testFirstNameOnly() {
52+
SelectStatementProvider selectStatement = search(null, "fred", null);
53+
54+
String expected = "select person_id, first_name, last_name"
55+
+ " from Person"
56+
+ " where first_name like #{parameters.p1}"
57+
+ " order by last_name, first_name"
58+
+ " fetch first #{parameters._fetchFirstRows} rows only";
59+
60+
assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
61+
assertThat(selectStatement.getParameters().get("p1")).isEqualTo("%fred%");
62+
assertThat(selectStatement.getParameters().get("_fetchFirstRows")).isEqualTo(50L);
63+
}
64+
65+
@Test
66+
public void testLastNameOnly() {
67+
SelectStatementProvider selectStatement = search(null, null, "flintstone");
68+
69+
String expected = "select person_id, first_name, last_name"
70+
+ " from Person"
71+
+ " where last_name like #{parameters.p1}"
72+
+ " order by last_name, first_name"
73+
+ " fetch first #{parameters._fetchFirstRows} rows only";
74+
75+
assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
76+
assertThat(selectStatement.getParameters().get("p1")).isEqualTo("%flintstone%");
77+
assertThat(selectStatement.getParameters().get("_fetchFirstRows")).isEqualTo(50L);
78+
}
79+
80+
@Test
81+
public void testBothNames() {
82+
SelectStatementProvider selectStatement = search(null, "fred", "flintstone");
83+
84+
String expected = "select person_id, first_name, last_name"
85+
+ " from Person"
86+
+ " where first_name like #{parameters.p1}"
87+
+ " and last_name like #{parameters.p2}"
88+
+ " order by last_name, first_name"
89+
+ " fetch first #{parameters._fetchFirstRows} rows only";
90+
91+
assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
92+
assertThat(selectStatement.getParameters().get("p1")).isEqualTo("%fred%");
93+
assertThat(selectStatement.getParameters().get("p2")).isEqualTo("%flintstone%");
94+
assertThat(selectStatement.getParameters().get("_fetchFirstRows")).isEqualTo(50L);
95+
}
96+
97+
@Test
98+
public void testAllNull() {
99+
SelectStatementProvider selectStatement = search(null, null, null);
100+
101+
String expected = "select person_id, first_name, last_name"
102+
+ " from Person"
103+
+ " order by last_name, first_name"
104+
+ " fetch first #{parameters._fetchFirstRows} rows only";
105+
106+
assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
107+
assertThat(selectStatement.getParameters().get("_fetchFirstRows")).isEqualTo(50L);
108+
}
109+
110+
public SelectStatementProvider search(Integer targetId, String fName, String lName) {
111+
QueryExpressionDSL<SelectModel>.QueryExpressionWhereBuilder builder = select(id, firstName, lastName)
112+
.from(person)
113+
.where();
114+
115+
if (targetId != null) {
116+
builder
117+
.and(id, isEqualTo(targetId));
118+
} else {
119+
builder
120+
.and(firstName, isLike(fName).when(Objects::nonNull).then(s -> "%" + s + "%"))
121+
.and(lastName, isLikeWhenPresent(lName).then(this::addWildcards));
122+
}
123+
124+
builder
125+
.orderBy(lastName, firstName)
126+
.fetchFirst(50).rowsOnly();
127+
128+
return builder.build().render(RenderingStrategy.MYBATIS3);
129+
}
130+
131+
public String addWildcards(String s) {
132+
return "%" + s + "%";
133+
}
134+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Copyright 2016-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package examples.complexquery;
17+
18+
import org.mybatis.dynamic.sql.SqlColumn;
19+
import org.mybatis.dynamic.sql.SqlTable;
20+
21+
public final class PersonDynamicSqlSupport {
22+
public static final Person person = new Person();
23+
public static final SqlColumn<Integer> id = person.id;
24+
public static final SqlColumn<String> firstName = person.firstName;
25+
public static final SqlColumn<String> lastName = person.lastName;
26+
public static final SqlColumn<Integer> age = person.age;
27+
28+
public static final class Person extends SqlTable {
29+
public final SqlColumn<Integer> id = column("person_id");
30+
public final SqlColumn<String> firstName = column("first_name");
31+
public final SqlColumn<String> lastName = column("last_name");
32+
public final SqlColumn<Integer> age = column("age");
33+
34+
public Person() {
35+
super("Person");
36+
}
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright 2016-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package examples.complexquery;
17+
18+
public class SearchUtils {
19+
public static String addWildcards(String s) {
20+
return "%" + s + "%";
21+
}
22+
}

0 commit comments

Comments
 (0)