Skip to content

Commit 1c79ee0

Browse files
committed
Updates to Spring Documentation
1 parent bce715b commit 1c79ee0

File tree

1 file changed

+276
-10
lines changed

1 file changed

+276
-10
lines changed

src/site/markdown/docs/spring.md

+276-10
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,44 @@ 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
15+
The generated SQL statement providers are compatible with Spring's `NamedParameterJdbcTemplate` in all cases. The only challenge comes with presenting statement parameters to Spring in the correct manner. To make this easier, the library provides a utility class `org.mybatis.dynamic.sql.util.spring.NamedParameterJdbcTemplateExtensions` that executes statements properly in all cases and hides the complexity of rendering statements and formatting parameters. All the examples below will show usage both with and without the utility class.
1616

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:
17+
## Type Converters for Spring
1818

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
19+
Spring JDBC templates do not have the equivalent of a type handler in MyBatis3. This is generally not a problem in processing results because you can build type conversions into your row handler. If you were manually creating the parameter map that is used as input to a Spring template you could perform a type conversion there too. But when you use MyBatis Dynamic SQL, the parameters are generated by the library, so you do not have the opportunity to perform type conversions directly.
20+
21+
To address this issue, the library provides a parameter type converter that can be used to perform a type conversion before parameters are placed in a paramter map.
22+
23+
For example, suppose we want to use a `Boolean` in Java to represent the value of a flag, but in the database the corresponding field is a `CHAR` field that expects values "true" or "false". This can be accomplished by using a `ParameterTypeConverter`. First create the converter as follows:
24+
25+
```java
26+
public class TrueFalseParameterConverter implements ParameterTypeConverter<Boolean, String> {
27+
@Override
28+
public String convert(Boolean source) {
29+
return source == null ? null : source ? "true" : "false";
30+
}
31+
}
32+
```
33+
34+
The type converter is compatible with Spring's existing Converter interface. Associate the type converter with a SqlColumn as follows:
35+
36+
```java
37+
...
38+
public final SqlColumn<Boolean> employed = column("employed", JDBCType.VARCHAR)
39+
.withParameterTypeConverter(new TrueFalseParameterConverter());
40+
...
41+
```
42+
43+
MyBatis Dynamic SQL will now call the converter function before corresponding parameters are placed into the generated parameter map. The converter will be called in the following cases:
44+
45+
1. With a general insert statement when using the `set(...).toValue(...)` or `set(...).toValueWhenPresent(...)` mappings
46+
1. With an update statement when using the `set(...).equalTo(...)` or `set(...).equalToWhenPresent(...)` mappings
47+
1. With where clauses in any statement type that contain conditions referencing the field
2048

2149
## Executing Select Statements
22-
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:
50+
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.
51+
52+
The following code shows a complete example without the utility class:
2353

2454
```java
2555
NamedParameterJdbcTemplate template = getTemplate(); // not shown
@@ -46,7 +76,62 @@ The Spring Named Parameter JDBC template expects an SQL statement with parameter
4676
});
4777
```
4878

49-
## Executing General Insert Statements
79+
The following code shows a complete example with the utility class:
80+
81+
```java
82+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
83+
NamedParameterJdbcTemplateExtensions extensions = new NamedParameterJdbcTemplateExtensions(template);
84+
85+
Buildable<SelectModel> selectStatement = select(id, firstName, lastName, fullName)
86+
.from(generatedAlways)
87+
.where(id, isGreaterThan(3))
88+
.orderBy(id.descending());
89+
90+
List<GeneratedAlwaysRecord> records = extensions.selectList(selectStatement,
91+
new RowMapper<GeneratedAlwaysRecord>(){
92+
@Override
93+
public GeneratedAlwaysRecord mapRow(ResultSet rs, int rowNum) throws SQLException {
94+
GeneratedAlwaysRecord record = new GeneratedAlwaysRecord();
95+
record.setId(rs.getInt(1));
96+
record.setFirstName(rs.getString(2));
97+
record.setLastName(rs.getString(3));
98+
record.setFullName(rs.getString(4));
99+
return record;
100+
}
101+
});
102+
```
103+
104+
The utility class also includes a `selectOne` method that returns an `Optional`. An example is shown below:
105+
106+
```java
107+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
108+
NamedParameterJdbcTemplateExtensions extensions = new NamedParameterJdbcTemplateExtensions(template);
109+
110+
Buildable<SelectModel> selectStatement = select(id, firstName, lastName, fullName)
111+
.from(generatedAlways)
112+
.where(id, isEqualTo(3));
113+
114+
Optional<GeneratedAlwaysRecord> record = extensions.selectOne(selectStatement,
115+
new RowMapper<GeneratedAlwaysRecord>(){
116+
@Override
117+
public GeneratedAlwaysRecord mapRow(ResultSet rs, int rowNum) throws SQLException {
118+
GeneratedAlwaysRecord record = new GeneratedAlwaysRecord();
119+
record.setId(rs.getInt(1));
120+
record.setFirstName(rs.getString(2));
121+
record.setLastName(rs.getString(3));
122+
record.setFullName(rs.getString(4));
123+
return record;
124+
}
125+
});
126+
```
127+
128+
## Executing Insert Statements
129+
130+
The library generates several types of insert statements. See the [Insert Statements](insert.html) page for details.
131+
132+
Spring supports retrieval of generated keys for many types of inserts. This library has support for generated key retrieval where it is supported by Spring.
133+
134+
### Executing General Insert Statements
50135
General insert statements do not require a POJO object matching a table row. Following is a complete example:
51136

52137
```java
@@ -81,7 +166,26 @@ If you want to retrieve generated keys for a general insert statement the steps
81166
String generatedKey = (String) keyHolder.getKeys().get("FULL_NAME");
82167
```
83168

84-
## Executing Insert Record Statements
169+
This can be simplified by using the utility class as follows:
170+
171+
```java
172+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
173+
NamedParameterJdbcTemplateExtensions extensions = new NamedParameterJdbcTemplateExtensions(template);
174+
175+
Buildable<GeneralInsertModel> insertStatement = insertInto(generatedAlways)
176+
.set(id).toValue(100)
177+
.set(firstName).toValue("Bob")
178+
.set(lastName).toValue("Jones");
179+
180+
// no generated key retrieval
181+
int rows = extensions.generalInsert(insertStatement);
182+
183+
// retrieve generated keys
184+
KeyHolder keyHolder = new GeneratedKeyHolder();
185+
int rows = extensions.generalInsert(insertStatement, keyHolder);
186+
```
187+
188+
### Executing Single Record Insert Statements
85189
Insert record statements are a bit different - MyBatis Dynamic SQL generates a properly formatted SQL string for Spring, but instead of a map of parameters, the parameter mappings are created for the inserted record itself. So the parameters for the Spring template are created by a `BeanPropertySqlParameterSource`. Generated keys in Spring are supported with a `GeneratedKeyHolder`. The following is a complete example:
86190

87191
```java
@@ -107,8 +211,101 @@ Insert record statements are a bit different - MyBatis Dynamic SQL generates a p
107211
String generatedKey = (String) keyHolder.getKeys().get("FULL_NAME");
108212
```
109213

110-
## Executing Batch Inserts
111-
Batch insert support in Spring is a bit different than batch support in MyBatis3 and Spring does not support returning generated keys from a batch insert. The following is a complete example of a batch insert (note the use of `SqlParameterSourceUtils` to create an array of parameter sources from an array of input records):
214+
This can be simplified by using the utility class as follows:
215+
216+
```java
217+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
218+
NamedParameterJdbcTemplateExtensions extensions = new NamedParameterJdbcTemplateExtensions(template);
219+
220+
GeneratedAlwaysRecord record = new GeneratedAlwaysRecord();
221+
record.setId(100);
222+
record.setFirstName("Bob");
223+
record.setLastName("Jones");
224+
225+
Buildable<InsertModel<GeneratedAlwaysRecord>> insertStatement = insert(record)
226+
.into(generatedAlways)
227+
.map(id).toProperty("id")
228+
.map(firstName).toProperty("firstName")
229+
.map(lastName).toProperty("lastName");
230+
231+
// no generated key retrieval
232+
int rows = extensions.insert(insertStatement);
233+
234+
// retrieve generated keys
235+
KeyHolder keyHolder = new GeneratedKeyHolder();
236+
int rows = extensions.insert(insertStatement, keyHolder);
237+
```
238+
239+
### Multi-Row Inserts
240+
A multi-row insert is a single insert statament with multiple VALUES clauses. This can be a convienint way in insert a small number of records into a table with a single statement. Note however that a multi-row insert is not suitable for large bulk inserts as it is possible to exceed the limit of prepared statement parameters with a large number of records. For that use case, use a batch insert (see below).
241+
242+
With multi-row insert statements MyBatis Dynamic SQL generates a properly formatted SQL string for Spring. Instead of a map of parameters, the multiple records are stored in the generated provider object and the parameter mappings are created for the generated provider itself. The parameters for the Spring template are created by a `BeanPropertySqlParameterSource`. Generated keys in Spring are supported with a `GeneratedKeyHolder`. The following is a complete example:
243+
244+
```java
245+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
246+
247+
List<GeneratedAlwaysRecord> records = new ArrayList<>();
248+
GeneratedAlwaysRecord record = new GeneratedAlwaysRecord();
249+
record.setId(100);
250+
record.setFirstName("Bob");
251+
record.setLastName("Jones");
252+
records.add(record);
253+
254+
record = new GeneratedAlwaysRecord();
255+
record.setId(101);
256+
record.setFirstName("Jim");
257+
record.setLastName("Smith");
258+
records.add(record);
259+
260+
MultiRowInsertStatementProvider<GeneratedAlwaysRecord> insertStatement = insertMultiple(records).into(generatedAlways)
261+
.map(id).toProperty("id")
262+
.map(firstName).toProperty("firstName")
263+
.map(lastName).toProperty("lastName")
264+
.build()
265+
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
266+
267+
SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(insertStatement);
268+
KeyHolder keyHolder = new GeneratedKeyHolder();
269+
270+
int rows = template.update(insertStatement.getInsertStatement(), parameterSource, keyHolder);
271+
String firstGeneratedKey = (String) keyHolder.getKeyList().get(0).get("FULL_NAME");
272+
String secondGeneratedKey = (String) keyHolder.getKeyList().get(1).get("FULL_NAME");
273+
```
274+
275+
This can be simplified by using the utility class as follows:
276+
277+
```java
278+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
279+
NamedParameterJdbcTemplateExtensions extensions = new NamedParameterJdbcTemplateExtensions(template);
280+
281+
List<GeneratedAlwaysRecord> records = new ArrayList<>();
282+
GeneratedAlwaysRecord record = new GeneratedAlwaysRecord();
283+
record.setId(100);
284+
record.setFirstName("Bob");
285+
record.setLastName("Jones");
286+
records.add(record);
287+
288+
record = new GeneratedAlwaysRecord();
289+
record.setId(101);
290+
record.setFirstName("Jim");
291+
record.setLastName("Smith");
292+
records.add(record);
293+
294+
Buildable<MultiRowInsertModel<GeneratedAlwaysRecord>> insertStatement = insertMultiple(records).into(generatedAlways)
295+
.map(id).toProperty("id")
296+
.map(firstName).toProperty("firstName")
297+
.map(lastName).toProperty("lastName");
298+
299+
// no generated key retrieval
300+
int rows = extensions.insertMultiple(insertStatement);
301+
302+
// retrieve generated keys
303+
KeyHolder keyHolder = new GeneratedKeyHolder();
304+
int rows = extensions.insertMultiple(insertStatement, keyHolder);
305+
```
306+
307+
### Executing Batch Inserts
308+
A JDBC batch insert is an efficient way to perform a bulk insert. It does not have the limitations of a multi-row insert and may perform better too. Spring does not support returning generated keys from a batch insert. The following is a complete example of a batch insert (note the use of `SqlParameterSourceUtils` to create an array of parameter sources from an array of input records):
112309

113310
```java
114311
NamedParameterJdbcTemplate template = getTemplate(); // not shown
@@ -139,8 +336,64 @@ Batch insert support in Spring is a bit different than batch support in MyBatis3
139336
int[] updateCounts = template.batchUpdate(batchInsert.getInsertStatementSQL(), batch);
140337
```
141338

142-
## Executing Delete and Update Statements
143-
Updates and deletes use the `MapSqlParameterSource` as with select statements, but use the `update` method in the template. For example:
339+
This can be simplified by using the utility class as follows:
340+
341+
```java
342+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
343+
NamedParameterJdbcTemplateExtensions extensions = new NamedParameterJdbcTemplateExtensions(template);
344+
345+
List<GeneratedAlwaysRecord> records = new ArrayList<>();
346+
GeneratedAlwaysRecord record = new GeneratedAlwaysRecord();
347+
record.setId(100);
348+
record.setFirstName("Bob");
349+
record.setLastName("Jones");
350+
records.add(record);
351+
352+
record = new GeneratedAlwaysRecord();
353+
record.setId(101);
354+
record.setFirstName("Jim");
355+
record.setLastName("Smith");
356+
records.add(record);
357+
358+
Buildable<BatchInsertModel<GeneratedAlwaysRecord>> insertStatement = insertBatch(records)
359+
.into(generatedAlways)
360+
.map(id).toProperty("id")
361+
.map(firstName).toProperty("firstName")
362+
.map(lastName).toProperty("lastName");
363+
364+
int[] updateCounts = extensions.insertBatch(insertStatement);
365+
```
366+
367+
## Executing Delete Statements
368+
Delete statements use the `MapSqlParameterSource` as with select statements, but use the `update` method in the template. For example:
369+
370+
```java
371+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
372+
373+
DeleteStatementProvider deleteStatement = deleteFrom(generatedAlways)
374+
.where(id, isLessThan(3))
375+
.build()
376+
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
377+
378+
SqlParameterSource parameterSource = new MapSqlParameterSource(deleteStatement.getParameters());
379+
380+
int rows = template.update(deleteStatement.getDeleteStatement(), parameterSource);
381+
```
382+
383+
This can be simplified by using the utility class as follows:
384+
385+
```java
386+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
387+
NamedParameterJdbcTemplateExtensions extensions = new NamedParameterJdbcTemplateExtensions(template);
388+
389+
Buildable<DeleteModel> deleteStatement = deleteFrom(generatedAlways)
390+
.where(id, isLessThan(3));
391+
392+
int rows = extensions.delete(deleteStatement);
393+
```
394+
395+
## Executing Update Statements
396+
Update statements use the `MapSqlParameterSource` as with select statements, but use the `update` method in the template. For example:
144397

145398
```java
146399
NamedParameterJdbcTemplate template = getTemplate(); // not shown
@@ -155,3 +408,16 @@ Updates and deletes use the `MapSqlParameterSource` as with select statements, b
155408

156409
int rows = template.update(updateStatement.getUpdateStatement(), parameterSource);
157410
```
411+
412+
This can be simplified by using the utility class as follows:
413+
414+
```java
415+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
416+
NamedParameterJdbcTemplateExtensions extensions = new NamedParameterJdbcTemplateExtensions(template);
417+
418+
Buildable<UpdateModel> updateStatement = update(generatedAlways)
419+
.set(firstName).equalToStringConstant("Rob")
420+
.where(id, isIn(1, 5, 22));
421+
422+
int rows = extensions.update(updateStatement);
423+
```

0 commit comments

Comments
 (0)