Skip to content

Commit 29d77bb

Browse files
committed
Documentation for Spring/Kotlin
1 parent 59c8333 commit 29d77bb

File tree

7 files changed

+336
-6
lines changed

7 files changed

+336
-6
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ See the following pages for further information:
2626
|------|---------|
2727
|[Quick Start](src/site/markdown/docs/quickStart.md) | Shows a complete example of building code for this library |
2828
|[MyBatis3 Support](src/site/markdown/docs/mybatis3.md) | Information about specialized support for [MyBatis3](https://github.com/mybatis/mybatis-3). The examples on this page are similar to the code generated by [MyBatis Generator](https://github.com/mybatis/generator) |
29+
|[Kotlin Support with MyBatis3](src/site/markdown/docs/kotlinMyBatis3.md) | Information about the Kotlin extensions and Kotlin DSL when using MyBatis3 as the runtime |
2930
|[Spring Support](src/site/markdown/docs/spring.md) | Information about specialized support for Spring JDBC Templates |
31+
|[Kotlin Support with Spring](src/site/markdown/docs/kotlinSpring.md) | Information about the Kotlin extensions and Kotlin DSL when using Spring JDBC Template as the runtime |
3032
|[Spring Batch Support](src/site/markdown/docs/springBatch.md) | Information about specialized support for Spring Batch using the [MyBatis Spring Integration](https://github.com/mybatis/spring) |
31-
|[Kotlin Support](src/site/markdown/docs/kotlin.md) | Information about the Kotlin extensions and Kotlin DSL |
3233

3334
## Requirements
3435

src/site/markdown/docs/kotlinMyBatis3.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Kotlin Support for MyBatis3
2-
MyBatis Dynamic SQL includes Kotlin extension methods that enable an SQL DSL for Kotlin. This is the recommended method of using the library in Kotlin.
2+
MyBatis Dynamic SQL includes Kotlin extension methods that enable an SQL DSL for Kotlin. This is the recommended method of using the library in Kotlin with MyBatis3.
33

44
The standard usage patterns for MyBatis Dynamic SQL and MyBatis3 in Java must be modified somewhat for Kotlin. Kotlin interfaces can contain both abstract and non-abstract methods (somewhat similar to Java's default methods in an interface). But using these methods in Kotlin based mapper interfaces will cause a failure with MyBatis because of the underlying Kotlin implementation.
55

@@ -94,10 +94,10 @@ This is a standard method for MyBatis Dynamic SQL that executes a query and retu
9494

9595
```kotlin
9696
fun PersonMapper.count(completer: CountCompleter) =
97-
count(this::count, Person, completer)
97+
countFrom(this::count, Person, completer)
9898
```
9999

100-
This method shows the use of `CountCompleter` which is a Kotlin typealias for a function with a receiver that will allow a user to supply a where clause. This also shows use of the Kotlin `count` method which is supplied by the library. That method will build and execute the select count statement with the supplied where clause. Clients can use the method as follows:
100+
This method shows the use of `CountCompleter` which is a Kotlin typealias for a function with a receiver that will allow a user to supply a where clause. This also shows use of the Kotlin `countFrom` method which is supplied by the library. That method will build and execute the select count statement with the supplied where clause. Clients can use the method as follows:
101101

102102
```kotlin
103103
val rows = mapper.count {
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
# Kotlin Support for Spring
2+
MyBatis Dynamic SQL includes Kotlin extension methods that enable an SQL DSL for Kotlin. This is the recommended method of using the library in Kotlin with Spring JDBC template.
3+
4+
This page will show our recommended pattern for using the MyBatis Dynamic SQL with Kotlin and Spring JDBC Template. The code shown on this page is from the `src/test/kotlin/examples/kotlin/spring/canonical` directory in this repository. That directory contains a complete example of using this library with Kotlin and Spring.
5+
6+
All Kotlin support for Spring is available in two packages:
7+
8+
* `org.mybatis.dynamic.sql.util.kotlin` - contains extension methods and utilities to enable an idiomatic Kotlin DSL for MyBatis Dynamic SQL. These objects can be used for clients using any execution target (i.e. MyBatis3 or Spring JDBC Templates)
9+
* `org.mybatis.dynamic.sql.util.kotlin.spring` - contains utlities specifically to simplify integration with Spring JDBC Template
10+
11+
The Kotlin support for Spring is implemented as extension methods to `NamedParameterJdbcTemplate`. There are extension methods to support count, delete, insert, select, and update operations based on SQL generated by this library. For each operation, there are two different methods of executing SQL. With the first method you build the appropriate SQL provider object as a separate step before executing the SQL. The second method combines these two operations into a single step. We will illustrate both approaches below.
12+
13+
## Kotlin Dynamic SQL Support Objects
14+
Because Kotlin does not support static class members, we recommend a simpler pattern for creating the class containing the support objects. For example:
15+
16+
```kotlin
17+
object PersonDynamicSqlSupport {
18+
object Person : SqlTable("Person") {
19+
val id = column<Int>("id", JDBCType.INTEGER)
20+
val firstName = column<String>("first_name", JDBCType.VARCHAR)
21+
val lastName = column<String>("last_name", JDBCType.VARCHAR)
22+
val birthDate = column<Date>("birth_date", JDBCType.DATE)
23+
val employed = column<String>("employed", JDBCType.VARCHAR)
24+
val occupation = column<String>("occupation", JDBCType.VARCHAR)
25+
val addressId = column<Int>("address_id", JDBCType.INTEGER)
26+
}
27+
}
28+
```
29+
30+
This object is a singleton containing the `SqlTable` and `SqlColumn` objects that map to the database table.
31+
32+
**Important Note:** Spring JDBC template does not support type handlers, so column definitions in the support class should match the data types of the corresponding column.
33+
34+
## Count Method Support
35+
36+
A count query is a specialized select - it returns a single column - typically a long - and supports joins and a where clause.
37+
38+
The DSL for count methods looks like this:
39+
40+
```kotlin
41+
val countStatement = countFrom(Person) { // countStatement is a SelectStatementProvider
42+
where(id, isLessThan(4))
43+
}
44+
```
45+
46+
This code creates a `SelectStatementProvider` that can be executed with an extension method for `NamedParameterJdbcTemplate` like this:
47+
48+
```kotlin
49+
val template: NamedParameterJdbcTemplate = getTemplate() // not shown
50+
val rows = template.count(countStatement) // rows is a Long
51+
```
52+
53+
This is the two step execution process. This can be combined into a single step with code like the following:
54+
55+
```kotlin
56+
val rows = template.countFrom(Person) {
57+
where(id, isLessThan(4))
58+
}
59+
```
60+
61+
There is also an extention method that can be used to count all rows in a table:
62+
63+
```kotlin
64+
val rows = template.countFrom(Person) {
65+
allRows()
66+
}
67+
```
68+
69+
## Delete Method Support
70+
71+
Delete method support enables the creation of methods that execute a delete statement allowing a user to specify a where clause at runtime, but abstracting away all other details.
72+
73+
The DSL for delete methods looks like this:
74+
75+
```kotlin
76+
val deleteStatement = deleteFrom(Person) { // deleteStatement is a DeleteStatementProvider
77+
where(id, isLessThan(4))
78+
}
79+
```
80+
81+
This code creates a `DeleteStatementProvider` that can be executed with an extension method for `NamedParameterJdbcTemplate` like this:
82+
83+
```kotlin
84+
val template: NamedParameterJdbcTemplate = getTemplate() // not shown
85+
val rows = template.delete(deleteStatement) // rows is an Int
86+
```
87+
88+
This is the two step execution process. This can be combined into a single step with code like the following:
89+
90+
```kotlin
91+
val rows = template.deleteFrom(Person) {
92+
where(id, isLessThan(4))
93+
}
94+
```
95+
96+
There is also an extention method that can be used to count all rows in a table:
97+
98+
```kotlin
99+
val rows = template.deleteFrom(Person) {
100+
allRows()
101+
}
102+
```
103+
104+
## Insert Method Support
105+
106+
Insert method support enables the creation of arbitrary insert statements.
107+
108+
The DSL for insert methods looks like this:
109+
110+
```kotlin
111+
val record = PersonRecord(100, "Joe", "Jones", Date(), "Yes", "Developer", 1)
112+
113+
val insertStatement = insert(record).into(Person) { // insertStatement is an InsertStatementProvider
114+
map(id).toProperty("id")
115+
map(firstName).toProperty("firstName")
116+
map(lastName).toProperty("lastName")
117+
map(birthDate).toProperty("birthDate")
118+
map(employed).toProperty("employed")
119+
map(occupation).toProperty("occupation")
120+
map(addressId).toProperty("addressId")
121+
}
122+
```
123+
124+
This code creates an `InsertStatementProvider` that can be executed with an extension method for `NamedParameterJdbcTemplate` like this:
125+
126+
```kotlin
127+
val template: NamedParameterJdbcTemplate = getTemplate() // not shown
128+
val rows = template.insert(insertStatement) // rows is an Int
129+
```
130+
131+
This is the two step execution process. This can be combined into a single step with code like the following:
132+
133+
```kotlin
134+
val record = PersonRecord(100, "Joe", "Jones", Date(), "Yes", "Developer", 1)
135+
136+
val rows = template.insert(record, Person) {
137+
map(id).toProperty("id")
138+
map(firstName).toProperty("firstName")
139+
map(lastName).toProperty("lastName")
140+
map(birthDate).toProperty("birthDate")
141+
map(employed).toProperty("employed")
142+
map(occupation).toPropertyWhenPresent("occupation", record::occupation)
143+
map(addressId).toProperty("addressId")
144+
}
145+
```
146+
147+
Note the use of the `toPropertyWhenPresent` mapping - this will only set the insert value if the value of the property is non null. Also note that you can use the mapping methods to map insert fields to nulls and constants if desired.
148+
149+
## Select Method Support
150+
151+
Select method support enables the creation of methods that execute a query allowing a user to specify a where clause and/or an order by clause and/or pagination clauses at runtime, but abstracting away all other details.
152+
153+
The DSL for select methods looks like this:
154+
155+
```kotlin
156+
val selectStatement = select(id, firstName, lastName, birthDate, employed, occupation, // selectStatement is a SelectStatementProvider
157+
addressId).from(Person) {
158+
where(id, isLessThan(5))
159+
and(id, isLessThan(4)) {
160+
and(id, isLessThan(3)) {
161+
and(id, isLessThan(2))
162+
}
163+
}
164+
orderBy(id)
165+
limit(3)
166+
}
167+
```
168+
169+
This code creates a `SelectStatementProvider` that can be executed with an extension method for `NamedParameterJdbcTemplate` like this:
170+
171+
```kotlin
172+
val template: NamedParameterJdbcTemplate = getTemplate() // not shown
173+
val rows = template.selectList(selectStatement) { rs, _ -> // rows is a List of PersonRecord in this case
174+
val record = PersonRecord()
175+
record.id = rs.getInt(1)
176+
record.firstName = rs.getString(2)
177+
record.lastName = rs.getString(3)
178+
record.birthDate = rs.getTimestamp(4)
179+
record.employed = rs.getString(5)
180+
record.occupation = rs.getString(6)
181+
record.addressId = rs.getInt(7)
182+
record
183+
}
184+
```
185+
Note that you must provide a row mapper to tell Spring JDBC how to create result objects.
186+
187+
This is the two step execution process. This can be combined into a single step with code like the following:
188+
189+
```kotlin
190+
val rows = template.select(id, firstName, lastName, birthDate, employed, occupation, addressId)
191+
.from(Person) {
192+
where(id, isLessThan(4)) {
193+
and(occupation, isNotNull())
194+
}
195+
and(occupation, isNotNull())
196+
orderBy(id)
197+
limit(3)
198+
}.withRowMapper { rs, _ ->
199+
val record = PersonRecord()
200+
record.id = rs.getInt(1)
201+
record.firstName = rs.getString(2)
202+
record.lastName = rs.getString(3)
203+
record.birthDate = rs.getTimestamp(4)
204+
record.employed = rs.getString(5)
205+
record.occupation = rs.getString(6)
206+
record.addressId = rs.getInt(7)
207+
record
208+
}
209+
```
210+
211+
There are similar methods for selecing a single row, or executing a select distinct query. For example, you could implement a "select by primary key" method using code like this:
212+
213+
```kotlin
214+
val record = template.selectOne(id, firstName, lastName, birthDate, employed, occupation, addressId)
215+
.from(Person) {
216+
where(id, isEqualTo(key))
217+
}.withRowMapper { rs, _ ->
218+
val record = PersonRecord()
219+
record.id = rs.getInt(1)
220+
record.firstName = rs.getString(2)
221+
record.lastName = rs.getString(3)
222+
record.birthDate = rs.getTimestamp(4)
223+
record.employed = rs.getString(5)
224+
record.occupation = rs.getString(6)
225+
record.addressId = rs.getInt(7)
226+
record
227+
}
228+
```
229+
230+
In this case, the data type for `record` would be `PersonRecord?` - a nullable value.
231+
232+
There is also an extention method that can be used to select all rows in a table:
233+
234+
```kotlin
235+
val rows = template.select(id, firstName, lastName, birthDate, employed, occupation, addressId)
236+
.from(Person) {
237+
allRows()
238+
orderBy(id)
239+
}.withRowMapper { rs, _ ->
240+
val record = PersonRecord()
241+
record.id = rs.getInt(1)
242+
record.firstName = rs.getString(2)
243+
record.lastName = rs.getString(3)
244+
record.birthDate = rs.getTimestamp(4)
245+
record.employed = rs.getString(5)
246+
record.occupation = rs.getString(6)
247+
record.addressId = rs.getInt(7)
248+
record
249+
}
250+
```
251+
252+
Note that we have supplied an `order by` clause as well.
253+
254+
## Update Method Support
255+
256+
Update method support enables the creation of methods that execute an update allowing a user to specify SET clauses and/or a WHERE clause, but abstracting away all other details.
257+
258+
The DSL for delete methods looks like this:
259+
260+
```kotlin
261+
val updateStatement = update(Person) { // updateStatement is an UpdateStatementProvider
262+
set(firstName).equalTo("Sam")
263+
where(firstName, isEqualTo("Fred"))
264+
}
265+
```
266+
267+
This code creates an `UpdateStatementProvider` that can be executed with an extension method for `NamedParameterJdbcTemplate` like this:
268+
269+
```kotlin
270+
val template: NamedParameterJdbcTemplate = getTemplate() // not shown
271+
val rows = template.update(updateStatement) // rows is an Int
272+
```
273+
274+
This is the two step execution process. This can be combined into a single step with code like the following:
275+
276+
```kotlin
277+
val rows = template.update(Person) {
278+
set(firstName).equalTo("Sam")
279+
where(firstName, isEqualTo("Fred"))
280+
}
281+
```
282+
283+
There a many different set mappings the allow setting values to null, constantats, etc. There is also a maping that will only set the column value if the passed value is non null.
284+
285+
If you wish to update all rows in a table, simply omit the where clause:
286+
287+
```kotlin
288+
val rows = template.update(Person) {
289+
set(firstName).equalTo("Sam")
290+
}
291+
```

src/site/markdown/docs/mybatis3.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This is a standard method for MyBatis Dynamic SQL that executes a query and retu
2020

2121
```java
2222
default long count(CountDSLCompleter completer) {
23-
return MyBatis3Utils.count(this::count, person, completer);
23+
return MyBatis3Utils.countFrom(this::count, person, completer);
2424
}
2525
```
2626

src/site/markdown/docs/spring.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ The SQL statement objects are created in exactly the same way as for MyBatis - o
1212
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
1313
```
1414

15+
## Limitations
16+
17+
MyBatis3 is a higher level abstraction over JDBC than Spring JDBC templates. While most functions in the library will work with Spring, there are some functions that do not work:
18+
19+
1. Spring JDBC templates do not have anything equivalent to a type handler in MyBatis3. Therefore it is best to use data types that can be automatically understood by Spring
20+
1. The multiple row insert statement *will not* render properly for Spring. However, batch inserts *will* render properly for Spring
21+
1522
## Executing Select Statements
1623
The Spring Named Parameter JDBC template expects an SQL statement with parameter markers in the Spring format, and a set of matched parameters. MyBatis Dynamic SQL will generate both. The parameters returned from the generated SQL statement can be wrapped in a Spring `MapSqlParameterSource`. Spring also expects you to provide a row mapper for creating the returned objects. The following code shows a complete example:
1724

src/site/site.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
<item href="docs/mybatis3.html" name="MyBatis3 Support" />
5151
<item href="docs/kotlinMyBatis3.html" name="Kotlin Support for MyBatis3" />
5252
<item href="docs/spring.html" name="Spring Support" />
53+
<item href="docs/kotlinSpring.html" name="Kotlin Support for Spring" />
5354
<item href="docs/springBatch.html" name="Spring Batch Support" />
5455
<item href="docs/howItWorks.html" name="How it Works" />
5556
<item href="docs/extending.html" name="Extending the Library" />

0 commit comments

Comments
 (0)