Skip to content

Commit 759d0cd

Browse files
authored
Merge pull request #318 from jeffgbutler/test-polishing
General Updates to Documentation and Examples
2 parents 54fd8f9 + 86f9557 commit 759d0cd

19 files changed

+544
-564
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ See the following pages for detailed information:
5555
|[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 |
5656
|[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) |
5757

58+
The library test cases provide several complete examples of using the library in various different styles:
59+
60+
| Language | Runtime | Comments | Code Directory |
61+
|---|---|---|---|
62+
| Java | MyBatis3 | Example using Java utility classes for MyBatis in the style of MyBatis Generator | [src/test/java/examples/simple](src/test/java/examples/simple) |
63+
| Java | MyBatis3 | Example using Java utility classes for the MyBatis integration with Spring Batch | [src/test/java/examples/springbatch](src/test/java/examples/springbatch) |
64+
| Java | Spring JDBC | Example using Java utility classes for Spring JDBC Template | [src/test/java/examples/spring](src/test/java/examples/spring) |
65+
| Kotlin | MyBatis3 | Example using Kotlin utility classes for MyBatis in the style of MyBatis Generator | [src/test/kotlin/examples/kotlin/mybatis3/canonical](src/test/kotlin/examples/kotlin/mybatis3/canonical) |
66+
| Kotlin | Spring JDBC | Example using Kotlin utility classes for Spring JDBC Template | [src/test/kotlin/examples/kotlin/spring/canonical](src/test/kotlin/examples/kotlin/spring/canonical) |
67+
68+
5869
## Requirements
5970

6071
The library has no dependencies. Java 8 or higher is required.

src/site/markdown/docs/mybatis3.md

Lines changed: 56 additions & 85 deletions
Large diffs are not rendered by default.

src/site/markdown/docs/quickStart.md

Lines changed: 117 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,38 @@ Working with MyBatis Dynamic SQL requires the following steps:
99
For the purposes of this discussion, we will show using the library to perform CRUD operations on this table:
1010

1111
```sql
12-
create table SimpleTable (
13-
id int not null,
14-
first_name varchar(30) not null,
15-
last_name varchar(30) not null,
16-
birth_date date not null,
17-
employed varchar(3) not null,
18-
occupation varchar(30) null,
19-
primary key(id)
12+
create table Person (
13+
id int not null,
14+
first_name varchar(30) not null,
15+
last_name varchar(30) not null,
16+
birth_date date not null,
17+
employed varchar(3) not null,
18+
occupation varchar(30) null,
19+
address_id int not null,
20+
primary key(id)
2021
);
2122
```
2223

24+
We will also create a simple Java class to represent a row in the table:
25+
26+
```java
27+
package examples.simple;
28+
29+
import java.util.Date;
30+
31+
public class PersonRecord {
32+
private Integer id;
33+
private String firstName;
34+
private LastName lastName;
35+
private Date birthDate;
36+
private Boolean employed;
37+
private String occupation;
38+
private Integer addressId;
39+
40+
// getters and setters omitted
41+
}
42+
```
43+
2344
## Defining Tables and Columns
2445

2546
The class `org.mybatis.dynamic.sql.SqlTable` is used to define a table. A table definition includes
@@ -36,8 +57,8 @@ A column definition includes:
3657
4. (optional) The name of a type handler to use in MyBatis if the default type handler is not desired
3758

3859
We suggest the following usage pattern to give maximum flexibility. This pattern will allow you to use your
39-
table and columns in a "qualified" or "un-qualified" manner that looks like natural SQL. For example, in the
40-
following a column could be referred to as `firstName` or `simpleTable.firstName`.
60+
table and column names in a "qualified" or "un-qualified" manner that looks like natural SQL. For example, in the
61+
following a column could be referred to as `firstName` or `person.firstName`.
4162

4263
```java
4364
package examples.simple;
@@ -48,109 +69,130 @@ import java.util.Date;
4869
import org.mybatis.dynamic.sql.SqlColumn;
4970
import org.mybatis.dynamic.sql.SqlTable;
5071

51-
public final class SimpleTableDynamicSqlSupport {
52-
public static final SimpleTable simpleTable = new SimpleTable();
53-
public static final SqlColumn<Integer> id = simpleTable.id;
54-
public static final SqlColumn<String> firstName = simpleTable.firstName;
55-
public static final SqlColumn<String> lastName = simpleTable.lastName;
56-
public static final SqlColumn<Date> birthDate = simpleTable.birthDate;
57-
public static final SqlColumn<Boolean> employed = simpleTable.employed;
58-
public static final SqlColumn<String> occupation = simpleTable.occupation;
59-
60-
public static final class SimpleTable extends SqlTable {
72+
public final class PersonDynamicSqlSupport {
73+
public static final Person person = new Person();
74+
public static final SqlColumn<Integer> id = person.id;
75+
public static final SqlColumn<String> firstName = person.firstName;
76+
public static final SqlColumn<LastName> lastName = person.lastName;
77+
public static final SqlColumn<Date> birthDate = person.birthDate;
78+
public static final SqlColumn<Boolean> employed = person.employed;
79+
public static final SqlColumn<String> occupation = person.occupation;
80+
public static final SqlColumn<Integer> addressId = person.addressId;
81+
82+
public static final class Person extends SqlTable {
6183
public final SqlColumn<Integer> id = column("id", JDBCType.INTEGER);
6284
public final SqlColumn<String> firstName = column("first_name", JDBCType.VARCHAR);
63-
public final SqlColumn<String> lastName = column("last_name", JDBCType.VARCHAR);
85+
public final SqlColumn<LastName> lastName = column("last_name", JDBCType.VARCHAR, "examples.simple.LastNameTypeHandler");
6486
public final SqlColumn<Date> birthDate = column("birth_date", JDBCType.DATE);
6587
public final SqlColumn<Boolean> employed = column("employed", JDBCType.VARCHAR, "examples.simple.YesNoTypeHandler");
6688
public final SqlColumn<String> occupation = column("occupation", JDBCType.VARCHAR);
89+
public final SqlColumn<Integer> addressId = column("address_id", JDBCType.INTEGER);
6790

68-
public SimpleTable() {
69-
super("SimpleTable");
91+
public Person() {
92+
super("Person");
7093
}
7194
}
7295
}
7396
```
7497

7598
## Creating MyBatis3 Mappers
76-
The library will create classes that will be used as input to a MyBatis mapper. These classes include the generated SQL, as well as a parameter set that will match the generated SQL. Both are required by MyBatis. It is intended that these objects be the one and only parameter to a MyBatis mapper method.
99+
The library will create classes that will be used as input to a MyBatis mapper. These classes include the generated
100+
SQL, as well as a parameter set that will match the generated SQL. Both are required by MyBatis. It is intended that
101+
these objects be the one and only parameter to a MyBatis mapper method.
77102

78-
The library can be used with both XML and annotated mappers, but we recommend using MyBatis' annotated mapper support in all cases. The only case where XML is required is when you code a JOIN statement - in that case you will need to define your result map in XML due to limitations of the MyBatis annotations in supporting joins.
103+
The library can be used with both XML and annotated mappers, but we recommend using MyBatis' annotated mapper support in
104+
all cases. The only case where XML is required is when you code a JOIN statement - in that case you will need to define
105+
your result map in XML due to limitations of the MyBatis annotations in supporting joins.
79106

80107
For example, a mapper might look like this:
81108

82109
```java
83110
package examples.simple;
84111

85112
import java.util.List;
113+
import java.util.Optional;
86114

87-
import org.apache.ibatis.annotations.DeleteProvider;
88-
import org.apache.ibatis.annotations.InsertProvider;
89115
import org.apache.ibatis.annotations.Mapper;
90116
import org.apache.ibatis.annotations.Result;
91117
import org.apache.ibatis.annotations.ResultMap;
92118
import org.apache.ibatis.annotations.Results;
93119
import org.apache.ibatis.annotations.SelectProvider;
94-
import org.apache.ibatis.annotations.UpdateProvider;
95120
import org.apache.ibatis.type.JdbcType;
96-
import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider;
97-
import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider;
98121
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
99-
import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider;
100122
import org.mybatis.dynamic.sql.util.SqlProviderAdapter;
123+
import org.mybatis.dynamic.sql.util.mybatis3.CommonCountMapper;
124+
import org.mybatis.dynamic.sql.util.mybatis3.CommonDeleteMapper;
125+
import org.mybatis.dynamic.sql.util.mybatis3.CommonInsertMapper;
126+
import org.mybatis.dynamic.sql.util.mybatis3.CommonUpdateMapper;
101127

102128
@Mapper
103-
public interface SimpleTableAnnotatedMapper {
104-
105-
@InsertProvider(type=SqlProviderAdapter.class, method="insert")
106-
int insert(InsertStatementProvider<SimpleTableRecord> insertStatement);
107-
108-
@UpdateProvider(type=SqlProviderAdapter.class, method="update")
109-
int update(UpdateStatementProvider updateStatement);
110-
111-
@SelectProvider(type=SqlProviderAdapter.class, method="select")
112-
@Results(id="SimpleTableResult", value= {
113-
@Result(column="A_ID", property="id", jdbcType=JdbcType.INTEGER, id=true),
114-
@Result(column="first_name", property="firstName", jdbcType=JdbcType.VARCHAR),
115-
@Result(column="last_name", property="lastName", jdbcType=JdbcType.VARCHAR),
116-
@Result(column="birth_date", property="birthDate", jdbcType=JdbcType.DATE),
117-
@Result(column="employed", property="employed", jdbcType=JdbcType.VARCHAR, typeHandler=YesNoTypeHandler.class),
118-
@Result(column="occupation", property="occupation", jdbcType=JdbcType.VARCHAR)
129+
public interface PersonMapper extends CommonCountMapper, CommonDeleteMapper, CommonInsertMapper<PersonRecord>, CommonUpdateMapper {
130+
131+
@SelectProvider(type = SqlProviderAdapter.class, method = "select")
132+
@Results(id = "PersonResult", value = {
133+
@Result(column = "A_ID", property = "id", jdbcType = JdbcType.INTEGER, id = true),
134+
@Result(column = "first_name", property = "firstName", jdbcType = JdbcType.VARCHAR),
135+
@Result(column = "last_name", property = "lastName", jdbcType = JdbcType.VARCHAR, typeHandler = LastNameTypeHandler.class),
136+
@Result(column = "birth_date", property = "birthDate", jdbcType = JdbcType.DATE),
137+
@Result(column = "employed", property = "employed", jdbcType = JdbcType.VARCHAR, typeHandler = YesNoTypeHandler.class),
138+
@Result(column = "occupation", property = "occupation", jdbcType = JdbcType.VARCHAR),
139+
@Result(column = "address_id", property = "addressId", jdbcType = JdbcType.INTEGER)
119140
})
120-
List<SimpleTableRecord> selectMany(SelectStatementProvider selectStatement);
121-
122-
@SelectProvider(type=SqlProviderAdapter.class, method="select")
123-
@ResultMap("SimpleTableResult")
124-
SimpleTableRecord selectOne(SelectStatementProvider selectStatement);
141+
List<PersonRecord> selectMany(SelectStatementProvider selectStatement);
125142

126-
@DeleteProvider(type=SqlProviderAdapter.class, method="delete")
127-
int delete(DeleteStatementProvider deleteStatement);
128-
129-
@SelectProvider(type=SqlProviderAdapter.class, method="select")
130-
long count(SelectStatementProvider selectStatement);
143+
@SelectProvider(type = SqlProviderAdapter.class, method = "select")
144+
@ResultMap("PersonResult")
145+
Optional<PersonRecord> selectOne(SelectStatementProvider selectStatement);
131146
}
132147
```
133148

149+
This mapper implements full CRUD functionality for the table. The base interfaces `CommonCountMapper`,
150+
`CommonDeleteMapper`, etc. provide insert, update, delete, and count capabilities. Only the select methods must be
151+
written because of the custom result map.
152+
153+
Note that the `CommonInsertMapper` interface will not properly return the generated key if one is produced by the insert.
154+
If you need generated key support, see the documentation page for INSERT statements for details on how to implement
155+
such support.
156+
134157
## Executing SQL with MyBatis3
135-
In a DAO or service class, you can use the generated statement as input to your mapper methods. Here's
136-
an example from `examples.simple.SimpleTableAnnotatedMapperTest`:
158+
In a service class, you can use the generated statement as input to your mapper methods. Here are some
159+
examples from `examples.simple.PersonMapperTest`:
137160

138161
```java
139-
@Test
140-
public void testSelectByExample() {
141-
try (SqlSession session = sqlSessionFactory.openSession()) {
142-
SimpleTableAnnotatedMapper mapper = session.getMapper(SimpleTableAnnotatedMapper.class);
143-
144-
SelectStatementProvider selectStatement = select(id.as("A_ID"), firstName, lastName, birthDate, employed, occupation)
145-
.from(simpleTable)
146-
.where(id, isEqualTo(1))
147-
.or(occupation, isNull())
148-
.build()
149-
.render(RenderingStrategies.MYBATIS3);
150-
151-
List<SimpleTableRecord> rows = mapper.selectMany(selectStatement);
152-
153-
assertThat(rows.size()).isEqualTo(3);
154-
}
162+
@Test
163+
void testGeneralSelect() {
164+
try (SqlSession session = sqlSessionFactory.openSession()) {
165+
PersonMapper mapper = session.getMapper(PersonMapper.class);
166+
167+
SelectStatementProvider selectStatement = select(id.as("A_ID"), firstName, lastName, birthDate, employed,
168+
occupation, addressId)
169+
.from(person)
170+
.where(id, isEqualTo(1))
171+
.or(occupation, isNull())
172+
.build()
173+
.render(RenderingStrategies.MYBATIS3);
174+
175+
List<PersonRecord> rows = mapper.selectMany(selectStatement);
176+
assertThat(rows).hasSize(3);
155177
}
178+
}
179+
180+
@Test
181+
void testGeneralDelete() {
182+
try (SqlSession session = sqlSessionFactory.openSession()) {
183+
PersonMapper mapper = session.getMapper(PersonMapper.class);
184+
185+
DeleteStatementProvider deleteStatement = deleteFrom(person)
186+
.where(occupation, isNull())
187+
.build()
188+
.render(RenderingStrategies.MYBATIS3);
189+
190+
int rows = mapper.delete(deleteStatement);
191+
assertThat(rows).isEqualTo(2);
192+
}
193+
}
156194
```
195+
196+
If you use MyBatis Generator, the generator will create several additional utility methods in a mapper like this that
197+
will improve its usefulness. You can see a full example of the type of code created by MyBatis generator by looking
198+
at the full example at https://github.com/mybatis/mybatis-dynamic-sql/tree/master/src/test/java/examples/simple

src/site/markdown/docs/spring.md

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,14 @@ The following code shows a complete example without the utility class:
6363

6464
SqlParameterSource namedParameters = new MapSqlParameterSource(selectStatement.getParameters());
6565
List<GeneratedAlwaysRecord> records = template.query(selectStatement.getSelectStatement(), namedParameters,
66-
new RowMapper<GeneratedAlwaysRecord>(){
67-
@Override
68-
public GeneratedAlwaysRecord mapRow(ResultSet rs, int rowNum) throws SQLException {
69-
GeneratedAlwaysRecord record = new GeneratedAlwaysRecord();
70-
record.setId(rs.getInt(1));
71-
record.setFirstName(rs.getString(2));
72-
record.setLastName(rs.getString(3));
73-
record.setFullName(rs.getString(4));
74-
return record;
75-
}
76-
});
66+
(rs, rowNum) -> {
67+
GeneratedAlwaysRecord record = new GeneratedAlwaysRecord();
68+
record.setId(rs.getInt(1));
69+
record.setFirstName(rs.getString(2));
70+
record.setLastName(rs.getString(3));
71+
record.setFullName(rs.getString(4));
72+
return record;
73+
});
7774
```
7875

7976
The following code shows a complete example with the utility class:
@@ -88,17 +85,14 @@ The following code shows a complete example with the utility class:
8885
.orderBy(id.descending());
8986

9087
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-
});
88+
(rs, rowNum) -> {
89+
GeneratedAlwaysRecord record = new GeneratedAlwaysRecord();
90+
record.setId(rs.getInt(1));
91+
record.setFirstName(rs.getString(2));
92+
record.setLastName(rs.getString(3));
93+
record.setFullName(rs.getString(4));
94+
return record;
95+
});
10296
```
10397

10498
The utility class also includes a `selectOne` method that returns an `Optional`. An example is shown below:
@@ -112,17 +106,14 @@ The utility class also includes a `selectOne` method that returns an `Optional`.
112106
.where(id, isEqualTo(3));
113107

114108
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-
});
109+
(rs, rowNum) -> {
110+
GeneratedAlwaysRecord record = new GeneratedAlwaysRecord();
111+
record.setId(rs.getInt(1));
112+
record.setFirstName(rs.getString(2));
113+
record.setLastName(rs.getString(3));
114+
record.setFullName(rs.getString(4));
115+
return record;
116+
});
126117
```
127118

128119
## Executing Insert Statements

0 commit comments

Comments
 (0)