Skip to content

Commit

Permalink
Merge pull request #767 from jeffgbutler/simplification
Browse files Browse the repository at this point in the history
General Refactoring
  • Loading branch information
jeffgbutler authored Apr 8, 2024
2 parents d61d7c0 + d86f687 commit a79770d
Show file tree
Hide file tree
Showing 34 changed files with 142 additions and 132 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This log will detail notable changes to MyBatis Dynamic SQL. Full details are av

This is a minor release with several enhancements.

GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/milestone/13](https://github.com/mybatis/mybatis-dynamic-sql/milestone/13)
GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/milestone/13?closed=1](https://github.com/mybatis/mybatis-dynamic-sql/milestone/13?closed=1)

### Case Expressions and Cast Function
We've added support for CASE expressions to the library. Both simple and searched case expressions are supported.
Expand Down
82 changes: 46 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,63 +9,73 @@
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=mybatis_mybatis-dynamic-sql&metric=security_rating)](https://sonarcloud.io/dashboard?id=mybatis_mybatis-dynamic-sql)

## What Is This?
This library is a general purpose SQL generator. Think of it as a typesafe and expressive SQL DSL (domain specific language),
with support for rendering SQL formatted properly for MyBatis3 and Spring's NamedParameterJDBCTemplate.
This library is a general purpose SQL generator. Think of it as a typesafe and expressive SQL DSL (domain specific
language), with support for rendering SQL formatted properly for MyBatis3 and Spring's NamedParameterJDBCTemplate.

The library also contains extensions for Kotlin that enable an idiomatic Kotlin DSL for SQL.

The library will generate full DELETE, INSERT, SELECT, and UPDATE statements. The DSL implemented by the
library is very similar to native SQL but it includes many functions that allow for very dynamic SQL statements.
For example, a typical search can be coded with a query like this (the following code is Kotlin, but Java code is very similar):
library is very similar to native SQL, but it includes many functions that allow for very dynamic SQL statements.
For example, a typical search can be coded with a query like this (the following code is Kotlin, but Java code is very
similar):

```kotlin
fun search(id: String?, firstName: String?, lastName: String?) =
select(Customer.id, Customer.firstName, Customer.lastName) {
data class SearchParameters(val id: String?, val firstName: String?, val lastName: String?)

fun search(searchParameters: SearchParameters) =
select(id, firstName, lastName) {
from(Customer)
where { Customer.active.isTrue() }
and { Customer.id (isEqualToWhenPresent(id).map{ it?.padStart(5, '0') }) }
and { Customer.firstName (isLikeCaseInsensitiveWhenPresent(firstName)
.map{ "%" + it.trim() + "%" }) }
and { Customer.lastName (isLikeCaseInsensitiveWhenPresent(lastName)
.map{ "%" + it.trim() + "%" }) }
orderBy(Customer.lastName, Customer.firstName)
where {
active isEqualTo true
and { id isEqualToWhenPresent searchParameters.id }
and {
firstName(isLikeCaseInsensitiveWhenPresent(searchParameters.firstName)
.map { "%" + it.trim() + "%" })
}
and {
lastName(isLikeCaseInsensitiveWhenPresent(searchParameters.lastName)
.map { "%" + it.trim() + "%" })
}
}
orderBy(lastName, firstName)
limit(500)
}
```

This query does quite a lot...

1. It is a search with three search criteria - any combination of search criteria can be used
1. Only records with an active status will be returned
1. If `id` is specified, it will be padded to length 5 with '0' at the beginning of the string
1. If `firstName` is specified, it will be used in a case-insensitive search and SQL wildcards will be appended
1. If `lastName` is specified, it will be used in a case-insensitive search and SQL wildcards will be appended
1. The query results are limited to 500 rows
2. Only records with an active status will be returned
3. If `id` is specified, it will be used as a filter
4. If `firstName` is specified, it will be used in a case-insensitive search and SQL wildcards will be appended
5. If `lastName` is specified, it will be used in a case-insensitive search and SQL wildcards will be appended
6. The query results are limited to 500 rows

Using the dynamic SQL features of the library eliminates a lot of code that would be required for checking nulls, adding wild cards, etc. This query clearly expresses the intent of the search in just a few lines.
Using the dynamic SQL features of the library eliminates a lot of code that would be required for checking nulls,
adding wild cards, etc. This query clearly expresses the intent of the search in just a few lines.

See the following pages for detailed information:

| Page | Comments|
|------|---------|
|[Quick Start](src/site/markdown/docs/quickStart.md) | Shows a complete example of building code for this library |
|[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) |
|[Kotlin Support with MyBatis3](src/site/markdown/docs/kotlinMyBatis3.md) | Information about the Kotlin extensions and Kotlin DSL when using MyBatis3 as the runtime |
|[Spring Support](src/site/markdown/docs/spring.md) | Information about specialized support for Spring JDBC Templates |
|[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 |
|[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) |
| Page | Comments |
|--------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Quick Start](src/site/markdown/docs/quickStart.md) | Shows a complete example of building code for this library |
| [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) |
| [Kotlin Support with MyBatis3](src/site/markdown/docs/kotlinMyBatis3.md) | Information about the Kotlin extensions and Kotlin DSL when using MyBatis3 as the runtime |
| [Spring Support](src/site/markdown/docs/spring.md) | Information about specialized support for Spring JDBC Templates |
| [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 |
| [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) |

The library test cases provide several complete examples of using the library in various different styles:

| Language | Runtime | Comments | Code Directory |
|---|---|---|---|
| Java | MyBatis3 | Example using Java utility classes for MyBatis in the style of MyBatis Generator | [../examples/simple](src/test/java/examples/simple) |
| Java | MyBatis3 + MyBatis-Spring | Example using MyBatis-Spring integration | [../examples/column/comparison](src/test/java/examples/column/comparison) |
| Java | MyBatis3 + MyBatis-Spring (Spring Batch)| Example using Java utility classes for the MyBatis integration with Spring Batch | [../examples/springbatch](src/test/java/examples/springbatch) |
| Java | Spring JDBC | Example using Java utility classes for Spring JDBC Template | [../examples/spring](src/test/java/examples/spring) |
| Kotlin | MyBatis3 | Example using Kotlin utility classes for MyBatis in the style of MyBatis Generator | [../examples/kotlin/mybatis3/canonical](src/test/kotlin/examples/kotlin/mybatis3/canonical) |
| Kotlin | MyBatis3 + MyBatis-Spring | Example using MyBatis-Spring integration in Kotlin | [../examples/kotlin/mybatis3/column/comparison](src/test/kotlin/examples/kotlin/mybatis3/column/comparison) |
| Kotlin | Spring JDBC | Example using Kotlin utility classes for Spring JDBC Template | [../examples/kotlin/spring/canonical](src/test/kotlin/examples/kotlin/spring/canonical) |
| Language | Runtime | Comments | Code Directory |
|----------|------------------------------------------|------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
| Java | MyBatis3 | Example using Java utility classes for MyBatis in the style of MyBatis Generator | [../examples/simple](src/test/java/examples/simple) |
| Java | MyBatis3 + MyBatis-Spring | Example using MyBatis-Spring integration | [../examples/column/comparison](src/test/java/examples/column/comparison) |
| Java | MyBatis3 + MyBatis-Spring (Spring Batch) | Example using Java utility classes for the MyBatis integration with Spring Batch | [../examples/springbatch](src/test/java/examples/springbatch) |
| Java | Spring JDBC | Example using Java utility classes for Spring JDBC Template | [../examples/spring](src/test/java/examples/spring) |
| Kotlin | MyBatis3 | Example using Kotlin utility classes for MyBatis in the style of MyBatis Generator | [../examples/kotlin/mybatis3/canonical](src/test/kotlin/examples/kotlin/mybatis3/canonical) |
| Kotlin | MyBatis3 + MyBatis-Spring | Example using MyBatis-Spring integration in Kotlin | [../examples/kotlin/mybatis3/column/comparison](src/test/kotlin/examples/kotlin/mybatis3/column/comparison) |
| Kotlin | Spring JDBC | Example using Kotlin utility classes for Spring JDBC Template | [../examples/kotlin/spring/canonical](src/test/kotlin/examples/kotlin/spring/canonical) |


## Requirements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ protected AbstractListValueCondition(Collection<T> values) {
this.values = Objects.requireNonNull(values);
}

public final <R> Stream<R> mapValues(Function<T, R> mapper) {
return values.stream().map(mapper);
public final Stream<T> values() {
return values.stream();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;

import org.mybatis.dynamic.sql.SortSpecification;
Expand All @@ -34,8 +33,8 @@ private OrderByModel(Collection<? extends SortSpecification> columns) {
this.columns.addAll(columns);
}

public <R> Stream<R> mapColumns(Function<SortSpecification, R> mapper) {
return columns.stream().map(mapper);
public Stream<SortSpecification> columns() {
return columns.stream();
}

public static OrderByModel of(Collection<? extends SortSpecification> columns) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@

public class OrderByRenderer {
public FragmentAndParameters render(OrderByModel orderByModel) {
String phrase = orderByModel.mapColumns(this::calculateOrderByPhrase)
String phrase = orderByModel.columns()
.map(this::calculateOrderByPhrase)
.collect(Collectors.joining(", ", "order by ", "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return FragmentAndParameters.fromFragment(phrase);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;

import org.mybatis.dynamic.sql.SqlTable;
Expand All @@ -37,8 +36,8 @@ protected AbstractMultiRowInsertModel(AbstractBuilder<T, ?> builder) {
columnMappings = Objects.requireNonNull(builder.columnMappings);
}

public <R> Stream<R> mapColumnMappings(Function<AbstractColumnMapping, R> mapper) {
return columnMappings.stream().map(mapper);
public Stream<AbstractColumnMapping> columnMappings() {
return columnMappings.stream();
}

public List<T> records() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;

import org.jetbrains.annotations.NotNull;
Expand All @@ -43,8 +42,8 @@ private GeneralInsertModel(Builder builder) {
statementConfiguration = Objects.requireNonNull(builder.statementConfiguration);
}

public <R> Stream<R> mapColumnMappings(Function<AbstractColumnMapping, R> mapper) {
return insertMappings.stream().map(mapper);
public Stream<AbstractColumnMapping> columnMappings() {
return insertMappings.stream();
}

public SqlTable table() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;

import org.mybatis.dynamic.sql.SqlColumn;
Expand All @@ -33,8 +32,9 @@ private InsertColumnListModel(List<SqlColumn<?>> columns) {
this.columns.addAll(columns);
}

public <R> Stream<R> mapColumns(Function<SqlColumn<?>, R> mapper) {
return columns.stream().map(mapper);
@SuppressWarnings("java:S1452")
public Stream<SqlColumn<?>> columns() {
return columns.stream();
}

public static InsertColumnListModel of(List<SqlColumn<?>> columns) {
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/org/mybatis/dynamic/sql/insert/InsertModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;

import org.jetbrains.annotations.NotNull;
Expand All @@ -41,8 +40,8 @@ private InsertModel(Builder<T> builder) {
Validator.assertNotEmpty(columnMappings, "ERROR.7"); //$NON-NLS-1$
}

public <R> Stream<R> mapColumnMappings(Function<AbstractColumnMapping, R> mapper) {
return columnMappings.stream().map(mapper);
public Stream<AbstractColumnMapping> columnMappings() {
return columnMappings.stream();
}

public T row() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@
public class BatchInsertRenderer<T> {

private final BatchInsertModel<T> model;
private final RenderingStrategy renderingStrategy;
private final MultiRowValuePhraseVisitor visitor;

private BatchInsertRenderer(Builder<T> builder) {
model = Objects.requireNonNull(builder.model);
renderingStrategy = Objects.requireNonNull(builder.renderingStrategy);
visitor = new MultiRowValuePhraseVisitor(builder.renderingStrategy, "row"); //$NON-NLS-1$)
}

public BatchInsert<T> render() {
MultiRowValuePhraseVisitor visitor = new MultiRowValuePhraseVisitor(renderingStrategy, "row"); //$NON-NLS-1$)
FieldAndValueCollector collector = model.mapColumnMappings(m -> m.accept(visitor))
FieldAndValueCollector collector = model.columnMappings()
.map(m -> m.accept(visitor))
.collect(FieldAndValueCollector.collect());

String insertStatement = InsertRenderingUtilities.calculateInsertStatement(model.table(), collector);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,19 @@
public class GeneralInsertRenderer {

private final GeneralInsertModel model;
private final RenderingContext renderingContext;
private final GeneralInsertValuePhraseVisitor visitor;

private GeneralInsertRenderer(Builder builder) {
model = Objects.requireNonNull(builder.model);
renderingContext = RenderingContext.withRenderingStrategy(builder.renderingStrategy)
RenderingContext renderingContext = RenderingContext.withRenderingStrategy(builder.renderingStrategy)
.withStatementConfiguration(builder.statementConfiguration)
.build();
visitor = new GeneralInsertValuePhraseVisitor(renderingContext);
}

public GeneralInsertStatementProvider render() {
GeneralInsertValuePhraseVisitor visitor = new GeneralInsertValuePhraseVisitor(renderingContext);
FieldAndValueCollector collector = model.mapColumnMappings(m -> m.accept(visitor))
FieldAndValueCollector collector = model.columnMappings()
.map(m -> m.accept(visitor))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(FieldAndValueCollector.collect());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private Optional<FieldAndValueAndParameters> buildNullFragment(AbstractColumnMap
}

private Optional<FieldAndValueAndParameters> buildFragment(AbstractColumnMapping mapping, Object value) {
RenderedParameterInfo parameterInfo = mapping.mapColumn(renderingContext::calculateParameterInfo);
RenderedParameterInfo parameterInfo = renderingContext.calculateParameterInfo(mapping.column());

return FieldAndValueAndParameters.withFieldName(mapping.columnName())
.withValuePhrase(parameterInfo.renderedPlaceHolder())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,16 @@
public class InsertRenderer<T> {

private final InsertModel<T> model;
private final RenderingStrategy renderingStrategy;
private final ValuePhraseVisitor visitor;

private InsertRenderer(Builder<T> builder) {
model = Objects.requireNonNull(builder.model);
renderingStrategy = Objects.requireNonNull(builder.renderingStrategy);
visitor = new ValuePhraseVisitor(builder.renderingStrategy);
}

public InsertStatementProvider<T> render() {
ValuePhraseVisitor visitor = new ValuePhraseVisitor(renderingStrategy);

FieldAndValueCollector collector = model.mapColumnMappings(m -> m.accept(visitor))
FieldAndValueCollector collector = model.columnMappings()
.map(m -> m.accept(visitor))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(FieldAndValueCollector.collect());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ private Optional<String> calculateColumnsPhrase() {
}

private String calculateColumnsPhrase(InsertColumnListModel columnList) {
return columnList.mapColumns(SqlColumn::name)
return columnList.columns()
.map(SqlColumn::name)
.collect(Collectors.joining(", ", "(", ")")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}

Expand Down
Loading

0 comments on commit a79770d

Please sign in to comment.