Skip to content

Commit ef7b1dd

Browse files
committed
Documentation for General Insert
1 parent ec3efbb commit ef7b1dd

File tree

14 files changed

+278
-74
lines changed

14 files changed

+278
-74
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
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.1.5 - Unreleased
6+
7+
GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.1.5+](https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.1.5+)
8+
9+
### Added
10+
11+
- Added a general insert statement that does not require a separate record class to hold values for the insert. ([#201](https://github.com/mybatis/mybatis-dynamic-sql/issues/201))
12+
513
## Release 1.1.4 - November 23, 2019
614

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

src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertDSL.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,35 +59,35 @@ public SetClauseFinisher(SqlColumn<T> column) {
5959
this.column = column;
6060
}
6161

62-
public GeneralInsertDSL equalToNull() {
62+
public GeneralInsertDSL toNull() {
6363
insertMappings.add(NullMapping.of(column));
6464
return GeneralInsertDSL.this;
6565
}
6666

67-
public GeneralInsertDSL equalToConstant(String constant) {
67+
public GeneralInsertDSL toConstant(String constant) {
6868
insertMappings.add(ConstantMapping.of(column, constant));
6969
return GeneralInsertDSL.this;
7070
}
7171

72-
public GeneralInsertDSL equalToStringConstant(String constant) {
72+
public GeneralInsertDSL toStringConstant(String constant) {
7373
insertMappings.add(StringConstantMapping.of(column, constant));
7474
return GeneralInsertDSL.this;
7575
}
7676

77-
public GeneralInsertDSL equalTo(T value) {
78-
return equalTo(() -> value);
77+
public GeneralInsertDSL toValue(T value) {
78+
return toValue(() -> value);
7979
}
8080

81-
public GeneralInsertDSL equalTo(Supplier<T> valueSupplier) {
81+
public GeneralInsertDSL toValue(Supplier<T> valueSupplier) {
8282
insertMappings.add(ValueMapping.of(column, valueSupplier));
8383
return GeneralInsertDSL.this;
8484
}
8585

86-
public GeneralInsertDSL equalToWhenPresent(T value) {
87-
return equalToWhenPresent(() -> value);
86+
public GeneralInsertDSL toValueWhenPresent(T value) {
87+
return toValueWhenPresent(() -> value);
8888
}
8989

90-
public GeneralInsertDSL equalToWhenPresent(Supplier<T> valueSupplier) {
90+
public GeneralInsertDSL toValueWhenPresent(Supplier<T> valueSupplier) {
9191
if (valueSupplier.get() != null) {
9292
insertMappings.add(ValueMapping.of(column, valueSupplier));
9393
}

src/site/markdown/docs/insert.md

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ The library will generate a variety of INSERT statements:
44
1. An insert for a single record
55
1. An insert for multiple records with a single statement
66
1. An insert for multiple records with a JDBC batch
7-
2. An insert with a select statement
7+
1. A general insert statement
8+
1. An insert with a select statement
89

910
## Single Record Insert
1011
A single record insert is a statement that inserts a single record into a table. This statement is configured differently than other statements in the library so that MyBatis' support for generated keys will work properly. To use the statement, you must first create an object that will map to the database row, then map object attributes to fields in the database. For example:
@@ -213,6 +214,66 @@ It is important to open a MyBatis session by setting the executor type to BATCH.
213214

214215
Notice that the same mapper method that is used to insert a single record is now executed multiple times. The `map` methods are the same with the exception that the `toPropertyWhenPresent` mapping is not supported for batch inserts.
215216

217+
## General Insert Statement
218+
A general insert is used to build arbitrary insert statements. The general insert does not require a separate record object o hold values for the statement - any value can be passed into the statement. This version of the insert is not convienient for retriving generated keys with MyBatis - for that use case we recommend the "single record insert". However the general insert is perfectly acceptible for Spring JDBC template or MyBatis inserts that do not return generated keys. For example
219+
220+
```java
221+
GeneralInsertStatementProvider insertStatement = insertInto(animalData)
222+
.set(id).toValue(101)
223+
.set(animalName).toStringConstant("Fred")
224+
.set(brainWeight).toConstant("2.2")
225+
.set(bodyWeight).toValue(4.5)
226+
.build()
227+
.render(RenderingStrategies.MYBATIS3);
228+
```
229+
230+
Notice the `set` method. It is used to set the value for a database column. There are several different possibilities:
231+
232+
1. `set(column).toNull()` will insert a null into a column
233+
2. `set(column).toConstant(constant_value)` will insert a constant into a column. The constant_value will be written into the generated insert statement exactly as entered
234+
3. `set(column).toStringConstant(constant_value)` will insert a constant into a column. The constant_value will be written into the generated insert statement surrounded by single quote marks (as an SQL String)
235+
4. `set(column).toValue(value)` will insert a value into a column. The value of the property will be bound to the SQL statement as a prepared statement parameter
236+
5. `set(column).toValueWhenPresent(property, Supplier<?> valueSupplier)` will insert a value into a column if the value is non-null. The value of the property will be bound to the SQL statement as a prepared statement parameter.
237+
238+
### Annotated Mapper for General Insert Statements
239+
The GeneralInsertStatementProvider object can be used as a parameter to a MyBatis mapper method directly. If you
240+
are using an annotated mapper, the insert method should look like this:
241+
242+
```java
243+
import org.apache.ibatis.annotations.InsertProvider;
244+
import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider;
245+
import org.mybatis.dynamic.sql.util.SqlProviderAdapter;
246+
247+
...
248+
@InsertProvider(type=SqlProviderAdapter.class, method="generalInsert")
249+
int generalInsert(GeneralInsertStatementProvider insertStatement);
250+
...
251+
252+
```
253+
254+
### XML Mapper for General Insert Statements
255+
We do not recommend using an XML mapper for insert statements, but if you want to do so the GeneralInsertStatementProvider object can be used as a parameter to a MyBatis mapper method directly.
256+
257+
If you are using an XML mapper, the insert method should look like this in the Java interface:
258+
259+
```java
260+
import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider;
261+
262+
...
263+
int generalInsert(GeneralInsertStatementProvider insertStatement);
264+
...
265+
266+
```
267+
268+
The XML element should look like this:
269+
270+
```xml
271+
<insert id="generalInsert">
272+
${insertStatement}
273+
</insert>
274+
```
275+
276+
216277
## Insert with Select
217278
An insert select is an SQL insert statement the inserts the results of a select. For example:
218279

src/site/markdown/docs/kotlinMyBatis3.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,17 @@ val rows = mapper.delete { allRows() }
152152

153153
Insert method support enables the removal of some of the boilerplate code from insert methods in a mapper interfaces.
154154

155-
To use this support, we envision creating several methods - two standard mapper methods, and other extension methods. The standard mapper methods are standard MyBatis Dynamic SQL methods that will execute a delete:
155+
To use this support, we envision creating several methods - both standard mapper methods, and other extension methods. The standard mapper methods are standard MyBatis Dynamic SQL methods that will execute an insert:
156156

157157
```kotlin
158158
@Mapper
159159
interface PersonMapper {
160160
@InsertProvider(type = SqlProviderAdapter::class, method = "insert")
161161
fun insert(insertStatement: InsertStatementProvider<PersonRecord>): Int
162162

163+
@InsertProvider(type = SqlProviderAdapter::class, method = "generalInsert")
164+
fun generalInsert(insertStatement: GeneralInsertStatementProvider): Int
165+
163166
@InsertProvider(type = SqlProviderAdapter::class, method = "insertMultiple")
164167
fun insertMultiple(insertStatement: MultiRowInsertStatementProvider<PersonRecord>): Int
165168
}
@@ -179,6 +182,9 @@ fun PersonMapper.insert(record: PersonRecord) =
179182
map(addressId).toProperty("addressId")
180183
}
181184

185+
fun PersonMapper.insert(completer: GeneralInsertCompleter) =
186+
insertInto(this::generalInsert, Person, completer)
187+
182188
fun PersonMapper.insertMultiple(vararg records: PersonRecord) =
183189
insertMultiple(records.toList())
184190

@@ -205,7 +211,7 @@ fun PersonMapper.insertSelective(record: PersonRecord) =
205211
}
206212
```
207213

208-
Note these methods use Kotlin utility methods named `insert` and `insertMultiple`. Both methods accept a function with a receiver that will allow column mappings. The methods will build and execute insert statements.= with the supplied column mappings.
214+
Note these methods use Kotlin utility methods named `insert`, `insertInto`, and `insertMultiple`. Those methods accept a function with a receiver that will allow column mappings. The methods will build and execute insert statements with the supplied column mappings.
209215

210216
Clients use these methods as follows:
211217

@@ -214,6 +220,17 @@ Clients use these methods as follows:
214220
val record = PersonRecord(100, "Joe", LastName("Jones"), Date(), true, "Developer", 1)
215221
val rows = mapper.insert(record)
216222

223+
// general insert...
224+
val rows = mapper.insert {
225+
set(id).toValue(100)
226+
set(firstName).toValue("Joe")
227+
set(lastName).toValue(LastName("Jones"))
228+
set(employed).toValue(true)
229+
set(occupation).toValue("Developer")
230+
set(addressId).toValue(1)
231+
set(birthDate).toValue(Date())
232+
}
233+
217234
// multiple insert...
218235
val record1 = PersonRecord(100, "Joe", LastName("Jones"), Date(), true, "Developer", 1)
219236
val record2 = PersonRecord(101, "Sarah", LastName("Smith"), Date(), true, "Architect", 2)

src/site/markdown/docs/kotlinSpring.md

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ There is also an extention method that can be used to count all rows in a table:
101101
}
102102
```
103103

104-
## Insert Method Support
104+
## Insert Record Method Support
105105

106-
Insert method support enables the creation of arbitrary insert statements.
106+
Insert method support enables the creation of arbitrary insert statements given a class that matches a database row. If you do not with to create such a class, then see the general insert support following this section.
107107

108108
The DSL for insert methods looks like this:
109109

@@ -128,7 +128,7 @@ This code creates an `InsertStatementProvider` that can be executed with an exte
128128
val rows = template.insert(insertStatement) // rows is an Int
129129
```
130130

131-
This is the two step execution process. This can be combined into a single step with code like the following:
131+
This is the two step execution process. These steps can be combined into a single step with code like the following:
132132

133133
```kotlin
134134
val record = PersonRecord(100, "Joe", "Jones", Date(), "Yes", "Developer", 1)
@@ -146,6 +146,49 @@ This is the two step execution process. This can be combined into a single step
146146

147147
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.
148148

149+
## General Insert Method Support
150+
151+
General insert method support enables the creation of arbitrary insert statements and does not require the creation of a class matching the database row.
152+
153+
The DSL for general insert methods looks like this:
154+
155+
```kotlin
156+
val insertStatement = insertInto(Person) { // insertStatement is a GeneralInsertStatementProvider
157+
set(id).toValue(100)
158+
set(firstName).toValue("Joe")
159+
set(lastName).toValue("Jones")
160+
set(birthDate).toValue(Date())
161+
set(employed).toValue("Yes")
162+
set(occupation).toValue("Developer")
163+
set(addressId).toValue(1)
164+
}
165+
```
166+
167+
This code creates a `GeneralInsertStatementProvider` that can be executed with an extension method for `NamedParameterJdbcTemplate` like this:
168+
169+
```kotlin
170+
val template: NamedParameterJdbcTemplate = getTemplate() // not shown
171+
val rows = template.insert(insertStatement) // rows is an Int
172+
```
173+
174+
This is the two step execution process. These steps can be combined into a single step with code like the following:
175+
176+
```kotlin
177+
val myOccupation = "Developer"
178+
179+
val rows = template.insertInto(Person) {
180+
set(id).toValue(100)
181+
set(firstName).toValue("Joe")
182+
set(lastName).toValue("Jones")
183+
set(birthDate).toValue(Date())
184+
set(employed).toValue("Yes")
185+
set(occupation).toValueWhenPresent(myOccupation)
186+
set(addressId).toValue(1)
187+
}
188+
```
189+
190+
Note the use of the `toValueWhenPresent` 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.
191+
149192
## Select Method Support
150193

151194
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.

src/site/markdown/docs/mybatis3.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,28 @@ int rows = mapper.delete(DeleteDSLCompleter.allRows());
7373

7474
The goal of insert method support is to remove some of the boilerplate code from insert methods in a mapper interfaces.
7575

76-
To use this support, we envision creating several methods on a MyBatis mapper interface. The first two methods are the standard MyBatis Dynamic SQL method that will execute an insert:
76+
To use this support, we envision creating several methods on a MyBatis mapper interface. The first methods are the standard MyBatis methods that will execute an insert:
7777

7878
```java
7979
@InsertProvider(type=SqlProviderAdapter.class, method="insert")
8080
int insert(InsertStatementProvider<PersonRecord> insertStatement);
8181

82+
@InsertProvider(type=SqlProviderAdapter.class, method="generalInsert")
83+
int generalInsert(GeneralInsertStatementProvider insertStatement);
84+
8285
@InsertProvider(type=SqlProviderAdapter.class, method="insertMultiple")
8386
int insertMultiple(MultiRowInsertStatementProvider<PersonRecord> insertStatement);
8487
```
8588

86-
These two methods are standard methods for MyBatis Dynamic SQL. They execute a single row insert and a multiple row insert.
89+
These methods are standard methods for MyBatis Dynamic SQL. They execute a single row insert, a general insert, and a multiple row insert.
8790

8891
These methods can be used to implement simplified insert methods:
8992

9093
```java
94+
default int insert(UnaryOperator<GeneralInsertDSL> completer) {
95+
return MyBatis3Utils.insert(this::generalInsert, person, completer);
96+
}
97+
9198
default int insert(PersonRecord record) {
9299
return MyBatis3Utils.insert(this::insert, record, person, c ->
93100
c.map(id).toProperty("id")
@@ -117,7 +124,7 @@ default int insertMultiple(Collection<PersonRecord> records) {
117124
}
118125
```
119126

120-
In the mapper, only the column mappings need to be specified and no other boilerplate code is needed.
127+
The first insert method is a general insert and can be used to create arbitrary inserts with different combinations of columns specified. The other methods have the insert statements mapped to a POJO "record" class that holds values for the insert statement.
121128

122129
## Select Method Support
123130

src/site/markdown/docs/spring.md

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ MyBatis3 is a higher level abstraction over JDBC than Spring JDBC templates. Whi
2323
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:
2424

2525
```java
26-
NamedParameterJdbcTemplate template = getTemplate();
26+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
2727

2828
SelectStatementProvider selectStatement = select(id, firstName, lastName, fullName)
2929
.from(generatedAlways)
@@ -47,11 +47,46 @@ The Spring Named Parameter JDBC template expects an SQL statement with parameter
4747
});
4848
```
4949

50-
## Executing Insert Statements
51-
Insert 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:
50+
## Executing General Insert Statements
51+
General insert statements do not require a POJO object matching a table row. Following is a complete example:
5252

5353
```java
54-
NamedParameterJdbcTemplate template = getTemplate();
54+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
55+
56+
GeneralInsertStatementProvider insertStatement = insertInto(generatedAlways)
57+
.set(id).toValue(100)
58+
.set(firstName).toValue("Bob")
59+
.set(lastName).toValue("Jones")
60+
.build()
61+
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
62+
63+
int rows = template.update(insertStatement.getInsertStatement(), insertStatement.getParameters());
64+
```
65+
66+
If you want to retrieve generated keys for a general insert statement the steps are similar except that you must wrap the parameters in a `MapSqlParameterSource` object and use a `GeneratedKeyHolder`. Following is a complete example of this usage:
67+
68+
```java
69+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
70+
71+
GeneralInsertStatementProvider insertStatement = insertInto(generatedAlways)
72+
.set(id).toValue(100)
73+
.set(firstName).toValue("Bob")
74+
.set(lastName).toValue("Jones")
75+
.build()
76+
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
77+
78+
MapSqlParameterSource parameterSource = new MapSqlParameterSource(insertStatement.getParameters());
79+
KeyHolder keyHolder = new GeneratedKeyHolder();
80+
81+
int rows = template.update(insertStatement.getInsertStatement(), parameterSource, keyHolder);
82+
String generatedKey = (String) keyHolder.getKeys().get("FULL_NAME");
83+
```
84+
85+
## Executing Insert Record Statements
86+
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:
87+
88+
```java
89+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
5590

5691
GeneratedAlwaysRecord record = new GeneratedAlwaysRecord();
5792
record.setId(100);
@@ -77,7 +112,7 @@ Insert statements are a bit different - MyBatis Dynamic SQL generates a properly
77112
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):
78113

79114
```java
80-
NamedParameterJdbcTemplate template = getTemplate();
115+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
81116

82117
List<GeneratedAlwaysRecord> records = new ArrayList<>();
83118
GeneratedAlwaysRecord record = new GeneratedAlwaysRecord();
@@ -109,11 +144,11 @@ Batch insert support in Spring is a bit different than batch support in MyBatis3
109144
Updates and deletes use the `MapSqlParameterSource` as with select statements, but use the `update` method in the template. For example:
110145

111146
```java
112-
NamedParameterJdbcTemplate template = getTemplate();
147+
NamedParameterJdbcTemplate template = getTemplate(); // not shown
113148

114149
UpdateStatementProvider updateStatement = update(generatedAlways)
115150
.set(firstName).equalToStringConstant("Rob")
116-
.where(id, isIn(1, 5, 22))
151+
.where(id, isIn(1, 5, 22))
117152
.build()
118153
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
119154

0 commit comments

Comments
 (0)