diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ea64545a..8dc2cb462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/issues?q=miles 1. Added support for criteria groups without an initial criteria. This makes it possible to create an independent list of pre-created criteria and then add the list to a where clause. See the tests in the related pull request for usage examples. ([#462](https://github.com/mybatis/mybatis-dynamic-sql/pull/462)) +2. Added the ability to specify a table alias on DELETE and UPDATE statements. + This is especially useful when working with a sub-query with an exists or not exists condition. + ([#489](https://github.com/mybatis/mybatis-dynamic-sql/pull/489)) ## Release 1.4.0 - March 3, 2022 diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java b/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java index 757a21b67..42aa8eaee 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java @@ -128,6 +128,10 @@ static DeleteDSL deleteFrom(SqlTable table) { return DeleteDSL.deleteFrom(table); } + static DeleteDSL deleteFrom(SqlTable table, String tableAlias) { + return DeleteDSL.deleteFrom(table, tableAlias); + } + static InsertDSL.IntoGatherer insert(T row) { return InsertDSL.insert(row); } @@ -210,6 +214,10 @@ static UpdateDSL update(SqlTable table) { return UpdateDSL.update(table); } + static UpdateDSL update(SqlTable table, String tableAlias) { + return UpdateDSL.update(table, tableAlias); + } + static WhereDSL where() { return WhereDSL.where(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/delete/DeleteDSL.java b/src/main/java/org/mybatis/dynamic/sql/delete/DeleteDSL.java index 3ff81b73b..eba241267 100644 --- a/src/main/java/org/mybatis/dynamic/sql/delete/DeleteDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/delete/DeleteDSL.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,10 +29,12 @@ public class DeleteDSL extends AbstractWhereSupport.DeleteWhereB private final Function adapterFunction; private final SqlTable table; + private final String tableAlias; private final DeleteWhereBuilder whereBuilder = new DeleteWhereBuilder(); - private DeleteDSL(SqlTable table, Function adapterFunction) { + private DeleteDSL(SqlTable table, String tableAlias, Function adapterFunction) { this.table = Objects.requireNonNull(table); + this.tableAlias = tableAlias; this.adapterFunction = Objects.requireNonNull(adapterFunction); } @@ -42,7 +44,7 @@ public DeleteWhereBuilder where() { } /** - * WARNING! Calling this method could result in an delete statement that deletes + * WARNING! Calling this method could result in a delete statement that deletes * all rows in a table. * * @return the model class @@ -51,17 +53,22 @@ public DeleteWhereBuilder where() { @Override public R build() { DeleteModel deleteModel = DeleteModel.withTable(table) + .withTableAlias(tableAlias) .withWhereModel(whereBuilder.buildWhereModel()) .build(); return adapterFunction.apply(deleteModel); } - public static DeleteDSL deleteFrom(Function adapterFunction, SqlTable table) { - return new DeleteDSL<>(table, adapterFunction); + public static DeleteDSL deleteFrom(Function adapterFunction, SqlTable table, String tableAlias) { + return new DeleteDSL<>(table, tableAlias, adapterFunction); } public static DeleteDSL deleteFrom(SqlTable table) { - return deleteFrom(Function.identity(), table); + return deleteFrom(Function.identity(), table, null); + } + + public static DeleteDSL deleteFrom(SqlTable table, String tableAlias) { + return deleteFrom(Function.identity(), table, tableAlias); } public class DeleteWhereBuilder extends AbstractWhereDSL implements Buildable { diff --git a/src/main/java/org/mybatis/dynamic/sql/delete/DeleteModel.java b/src/main/java/org/mybatis/dynamic/sql/delete/DeleteModel.java index 70d2e83c6..d5113f05e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/delete/DeleteModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/delete/DeleteModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,17 +27,23 @@ public class DeleteModel { private final SqlTable table; + private final String tableAlias; private final WhereModel whereModel; private DeleteModel(Builder builder) { table = Objects.requireNonNull(builder.table); whereModel = builder.whereModel; + tableAlias = builder.tableAlias; } public SqlTable table() { return table; } + public Optional tableAlias() { + return Optional.ofNullable(tableAlias); + } + public Optional whereModel() { return Optional.ofNullable(whereModel); } @@ -56,6 +62,7 @@ public static Builder withTable(SqlTable table) { public static class Builder { private SqlTable table; + private String tableAlias; private WhereModel whereModel; public Builder withTable(SqlTable table) { @@ -63,6 +70,11 @@ public Builder withTable(SqlTable table) { return this; } + public Builder withTableAlias(String tableAlias) { + this.tableAlias = tableAlias; + return this; + } + public Builder withWhereModel(WhereModel whereModel) { this.whereModel = whereModel; return this; diff --git a/src/main/java/org/mybatis/dynamic/sql/delete/render/DeleteRenderer.java b/src/main/java/org/mybatis/dynamic/sql/delete/render/DeleteRenderer.java index 1ff79402c..d8450194b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/delete/render/DeleteRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/delete/render/DeleteRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,9 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; +import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.delete.DeleteModel; +import org.mybatis.dynamic.sql.render.ExplicitTableAliasCalculator; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.render.TableAliasCalculator; import org.mybatis.dynamic.sql.where.WhereModel; @@ -31,10 +33,14 @@ public class DeleteRenderer { private final DeleteModel deleteModel; private final RenderingStrategy renderingStrategy; + private final TableAliasCalculator tableAliasCalculator; private DeleteRenderer(Builder builder) { deleteModel = Objects.requireNonNull(builder.deleteModel); renderingStrategy = Objects.requireNonNull(builder.renderingStrategy); + tableAliasCalculator = builder.deleteModel.tableAlias() + .map(a -> ExplicitTableAliasCalculator.of(deleteModel.table(), a)) + .orElseGet(TableAliasCalculator::empty); } public DeleteStatementProvider render() { @@ -56,8 +62,12 @@ private String calculateDeleteStatement(WhereClauseProvider whereClause) { } private String calculateDeleteStatement() { - return "delete from" //$NON-NLS-1$ - + spaceBefore(deleteModel.table().tableNameAtRuntime()); + SqlTable table = deleteModel.table(); + String tableName = table.tableNameAtRuntime(); + String aliasedTableName = tableAliasCalculator.aliasForTable(table) + .map(a -> tableName + " " + a).orElse(tableName); //$NON-NLS-1$ + + return "delete from" + spaceBefore(aliasedTableName); //$NON-NLS-1$ } private DeleteStatementProvider renderWithoutWhereClause() { @@ -69,7 +79,7 @@ private Optional renderWhereClause(WhereModel whereModel) { return WhereRenderer.withWhereModel(whereModel) .withRenderingStrategy(renderingStrategy) .withSequence(new AtomicInteger(1)) - .withTableAliasCalculator(TableAliasCalculator.empty()) + .withTableAliasCalculator(tableAliasCalculator) .build() .render(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/render/TableAliasCalculator.java b/src/main/java/org/mybatis/dynamic/sql/render/TableAliasCalculator.java index 722d13889..6d95ab717 100644 --- a/src/main/java/org/mybatis/dynamic/sql/render/TableAliasCalculator.java +++ b/src/main/java/org/mybatis/dynamic/sql/render/TableAliasCalculator.java @@ -29,12 +29,12 @@ static TableAliasCalculator empty() { return new TableAliasCalculator() { @Override public Optional aliasForColumn(SqlTable table) { - return Optional.empty(); + return table.tableAlias(); } @Override public Optional aliasForTable(SqlTable table) { - return Optional.empty(); + return table.tableAlias(); } }; } diff --git a/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java b/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java index 357def526..870f6dbcf 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,10 +45,12 @@ public class UpdateDSL extends AbstractWhereSupport.UpdateWhereB private final Function adapterFunction; private final List columnMappings = new ArrayList<>(); private final SqlTable table; + private final String tableAlias; private final UpdateWhereBuilder whereBuilder = new UpdateWhereBuilder(); - private UpdateDSL(SqlTable table, Function adapterFunction) { + private UpdateDSL(SqlTable table, String tableAlias, Function adapterFunction) { this.table = Objects.requireNonNull(table); + this.tableAlias = tableAlias; this.adapterFunction = Objects.requireNonNull(adapterFunction); } @@ -71,18 +73,23 @@ public UpdateWhereBuilder where() { @Override public R build() { UpdateModel updateModel = UpdateModel.withTable(table) + .withTableAlias(tableAlias) .withColumnMappings(columnMappings) .withWhereModel(whereBuilder.buildWhereModel()) .build(); return adapterFunction.apply(updateModel); } - public static UpdateDSL update(Function adapterFunction, SqlTable table) { - return new UpdateDSL<>(table, adapterFunction); + public static UpdateDSL update(Function adapterFunction, SqlTable table, String tableAlias) { + return new UpdateDSL<>(table, tableAlias, adapterFunction); } public static UpdateDSL update(SqlTable table) { - return update(Function.identity(), table); + return update(Function.identity(), table, null); + } + + public static UpdateDSL update(SqlTable table, String tableAlias) { + return update(Function.identity(), table, tableAlias); } public class SetClauseFinisher { diff --git a/src/main/java/org/mybatis/dynamic/sql/update/UpdateModel.java b/src/main/java/org/mybatis/dynamic/sql/update/UpdateModel.java index 7e6b65d31..338aecf92 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/UpdateModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/UpdateModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ public class UpdateModel { private final SqlTable table; + private final String tableAlias; private final WhereModel whereModel; private final List columnMappings; @@ -39,12 +40,17 @@ private UpdateModel(Builder builder) { table = Objects.requireNonNull(builder.table); whereModel = builder.whereModel; columnMappings = Objects.requireNonNull(builder.columnMappings); + tableAlias = builder.tableAlias; } public SqlTable table() { return table; } + public Optional tableAlias() { + return Optional.ofNullable(tableAlias); + } + public Optional whereModel() { return Optional.ofNullable(whereModel); } @@ -67,6 +73,7 @@ public static Builder withTable(SqlTable table) { public static class Builder { private SqlTable table; + private String tableAlias; private WhereModel whereModel; private final List columnMappings = new ArrayList<>(); @@ -75,6 +82,11 @@ public Builder withTable(SqlTable table) { return this; } + public Builder withTableAlias(String tableAlias) { + this.tableAlias = tableAlias; + return this; + } + public Builder withColumnMappings(List columnMappings) { this.columnMappings.addAll(columnMappings); return this; diff --git a/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java index 21fdcca3b..0b2ef7359 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.render.RenderingStrategy; @@ -40,28 +41,34 @@ public class SetPhraseVisitor extends UpdateMappingVisitor, String> aliasedColumnNameFunction; - public SetPhraseVisitor(AtomicInteger sequence, RenderingStrategy renderingStrategy) { + public SetPhraseVisitor(AtomicInteger sequence, RenderingStrategy renderingStrategy, + TableAliasCalculator tableAliasCalculator) { this.sequence = Objects.requireNonNull(sequence); this.renderingStrategy = Objects.requireNonNull(renderingStrategy); + Objects.requireNonNull(tableAliasCalculator); + aliasedColumnNameFunction = c -> tableAliasCalculator.aliasForColumn(c.table()) + .map(alias -> alias + "." + c.name()) //$NON-NLS-1$ + .orElseGet(c::name); } @Override public Optional visit(NullMapping mapping) { - return FragmentAndParameters.withFragment(mapping.columnName() + " = null") //$NON-NLS-1$ + return FragmentAndParameters.withFragment(mapping.mapColumn(aliasedColumnNameFunction) + " = null") //$NON-NLS-1$ .buildOptional(); } @Override public Optional visit(ConstantMapping mapping) { - String fragment = mapping.columnName() + " = " + mapping.constant(); //$NON-NLS-1$ + String fragment = mapping.mapColumn(aliasedColumnNameFunction) + " = " + mapping.constant(); //$NON-NLS-1$ return FragmentAndParameters.withFragment(fragment) .buildOptional(); } @Override public Optional visit(StringConstantMapping mapping) { - String fragment = mapping.columnName() + String fragment = mapping.mapColumn(aliasedColumnNameFunction) + " = '" //$NON-NLS-1$ + mapping.constant() + "'"; //$NON-NLS-1$ @@ -80,7 +87,7 @@ public Optional visit(ValueOrNullMapping mapping) return mapping.value() .map(v -> buildFragment(mapping, v)) .orElseGet(() -> FragmentAndParameters - .withFragment(mapping.columnName() + " = null") //$NON-NLS-1$ + .withFragment(mapping.mapColumn(aliasedColumnNameFunction) + " = null") //$NON-NLS-1$ .buildOptional() ); } @@ -98,7 +105,7 @@ public Optional visit(SelectMapping mapping) { .build() .render(); - String fragment = mapping.columnName() + String fragment = mapping.mapColumn(aliasedColumnNameFunction) + " = (" //$NON-NLS-1$ + selectStatement.getSelectStatement() + ")"; //$NON-NLS-1$ @@ -110,7 +117,7 @@ public Optional visit(SelectMapping mapping) { @Override public Optional visit(ColumnToColumnMapping mapping) { - String setPhrase = mapping.columnName() + String setPhrase = mapping.mapColumn(aliasedColumnNameFunction) + " = " //$NON-NLS-1$ + mapping.rightColumn().renderWithTableAlias(TableAliasCalculator.empty()); @@ -122,7 +129,7 @@ private Optional buildFragment(AbstractColumnMapping String mapKey = RenderingStrategy.formatParameterMapKey(sequence); String jdbcPlaceholder = mapping.mapColumn(c -> calculateJdbcPlaceholder(c, mapKey)); - String setPhrase = mapping.columnName() + String setPhrase = mapping.mapColumn(aliasedColumnNameFunction) + " = " //$NON-NLS-1$ + jdbcPlaceholder; diff --git a/src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java b/src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java index 517e5debf..1b9c9274f 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.render.ExplicitTableAliasCalculator; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.render.TableAliasCalculator; import org.mybatis.dynamic.sql.update.UpdateModel; @@ -37,14 +39,18 @@ public class UpdateRenderer { private final UpdateModel updateModel; private final RenderingStrategy renderingStrategy; private final AtomicInteger sequence = new AtomicInteger(1); + private final TableAliasCalculator tableAliasCalculator; private UpdateRenderer(Builder builder) { updateModel = Objects.requireNonNull(builder.updateModel); renderingStrategy = Objects.requireNonNull(builder.renderingStrategy); + tableAliasCalculator = builder.updateModel.tableAlias() + .map(a -> ExplicitTableAliasCalculator.of(updateModel.table(), a)) + .orElseGet(TableAliasCalculator::empty); } public UpdateStatementProvider render() { - SetPhraseVisitor visitor = new SetPhraseVisitor(sequence, renderingStrategy); + SetPhraseVisitor visitor = new SetPhraseVisitor(sequence, renderingStrategy, tableAliasCalculator); List> fragmentsAndParameters = updateModel.mapColumnMappings(m -> m.accept(visitor)) @@ -72,8 +78,13 @@ private String calculateUpdateStatement(List> fr } private String calculateUpdateStatement(List> fragmentsAndParameters) { + SqlTable table = updateModel.table(); + String tableName = table.tableNameAtRuntime(); + String aliasedTableName = tableAliasCalculator.aliasForTable(table) + .map(a -> tableName + " " + a).orElse(tableName); //$NON-NLS-1$ + return "update" //$NON-NLS-1$ - + spaceBefore(updateModel.table().tableNameAtRuntime()) + + spaceBefore(aliasedTableName) + spaceBefore(calculateSetPhrase(fragmentsAndParameters)); } @@ -104,7 +115,7 @@ private Optional renderWhereClause(WhereModel whereModel) { return WhereRenderer.withWhereModel(whereModel) .withRenderingStrategy(renderingStrategy) .withSequence(sequence) - .withTableAliasCalculator(TableAliasCalculator.empty()) + .withTableAliasCalculator(tableAliasCalculator) .build() .render(); } diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/model/ModelBuilderFunctions.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/model/ModelBuilderFunctions.kt index 263f2f434..2e5216133 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/model/ModelBuilderFunctions.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/model/ModelBuilderFunctions.kt @@ -62,6 +62,9 @@ fun countFrom(table: SqlTable, completer: CountCompleter): SelectModel = fun deleteFrom(table: SqlTable, completer: DeleteCompleter): DeleteModel = KotlinDeleteBuilder(SqlBuilder.deleteFrom(table)).apply(completer).build() +fun deleteFrom(table: SqlTable, tableAlias: String, completer: DeleteCompleter): DeleteModel = + KotlinDeleteBuilder(SqlBuilder.deleteFrom(table, tableAlias)).apply(completer).build() + fun insert(row: T, completer: KotlinInsertCompleter): InsertModel = KotlinInsertBuilder(row).apply(completer).build() @@ -114,3 +117,6 @@ fun selectDistinct(columns: List, completer: SelectCompleter): Sele fun update(table: SqlTable, completer: UpdateCompleter): UpdateModel = KotlinUpdateBuilder(SqlBuilder.update(table)).apply(completer).build() + +fun update(table: SqlTable, tableAlias: String, completer: UpdateCompleter): UpdateModel = + KotlinUpdateBuilder(SqlBuilder.update(table, tableAlias)).apply(completer).build() diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/ProviderBuilderFunctions.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/ProviderBuilderFunctions.kt index 0306bd374..e58e7c707 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/ProviderBuilderFunctions.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/ProviderBuilderFunctions.kt @@ -65,6 +65,9 @@ fun countFrom(table: SqlTable, completer: CountCompleter): SelectStatementProvid fun deleteFrom(table: SqlTable, completer: DeleteCompleter): DeleteStatementProvider = deleteFrom(table, completer).render(RenderingStrategies.MYBATIS3) +fun deleteFrom(table: SqlTable, tableAlias: String, completer: DeleteCompleter): DeleteStatementProvider = + deleteFrom(table, tableAlias, completer).render(RenderingStrategies.MYBATIS3) + fun insert(row: T, completer: KotlinInsertCompleter): InsertStatementProvider = insert(row, completer).render(RenderingStrategies.MYBATIS3) @@ -115,3 +118,6 @@ fun selectDistinct(columns: List, completer: SelectCompleter): Sele fun update(table: SqlTable, completer: UpdateCompleter): UpdateStatementProvider = update(table, completer).render(RenderingStrategies.MYBATIS3) + +fun update(table: SqlTable, tableAlias: String, completer: UpdateCompleter): UpdateStatementProvider = + update(table, tableAlias, completer).render(RenderingStrategies.MYBATIS3) diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/ProviderBuilderFunctions.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/ProviderBuilderFunctions.kt index b9e900a7c..7fcb6bd39 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/ProviderBuilderFunctions.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/ProviderBuilderFunctions.kt @@ -65,6 +65,9 @@ fun countFrom(table: SqlTable, completer: CountCompleter): SelectStatementProvid fun deleteFrom(table: SqlTable, completer: DeleteCompleter): DeleteStatementProvider = deleteFrom(table, completer).render(RenderingStrategies.SPRING_NAMED_PARAMETER) +fun deleteFrom(table: SqlTable, tableAlias: String, completer: DeleteCompleter): DeleteStatementProvider = + deleteFrom(table, tableAlias, completer).render(RenderingStrategies.SPRING_NAMED_PARAMETER) + fun insert(row: T, completer: KotlinInsertCompleter): InsertStatementProvider = insert(row, completer).render(RenderingStrategies.SPRING_NAMED_PARAMETER) @@ -115,3 +118,6 @@ fun selectDistinct(columns: List, completer: SelectCompleter): Sele fun update(table: SqlTable, completer: UpdateCompleter): UpdateStatementProvider = update(table, completer).render(RenderingStrategies.SPRING_NAMED_PARAMETER) + +fun update(table: SqlTable, tableAlias: String, completer: UpdateCompleter): UpdateStatementProvider = + update(table, tableAlias, completer).render(RenderingStrategies.SPRING_NAMED_PARAMETER) diff --git a/src/test/java/examples/joins/ExistsTest.java b/src/test/java/examples/joins/ExistsTest.java index 865810166..2d8f8c572 100644 --- a/src/test/java/examples/joins/ExistsTest.java +++ b/src/test/java/examples/joins/ExistsTest.java @@ -25,9 +25,13 @@ import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider; import org.mybatis.dynamic.sql.render.RenderingStrategies; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; +import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider; +import org.mybatis.dynamic.sql.util.mybatis3.CommonDeleteMapper; import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper; +import org.mybatis.dynamic.sql.util.mybatis3.CommonUpdateMapper; import java.io.InputStream; import java.io.InputStreamReader; @@ -62,7 +66,9 @@ void setup() throws Exception { Environment environment = new Environment("test", new JdbcTransactionFactory(), ds); Configuration config = new Configuration(environment); config.addMapper(JoinMapper.class); + config.addMapper(CommonDeleteMapper.class); config.addMapper(CommonSelectMapper.class); + config.addMapper(CommonUpdateMapper.class); sqlSessionFactory = new SqlSessionFactoryBuilder().build(config); } @@ -379,4 +385,100 @@ void testWhereExistsAnd() { assertThat(row).containsEntry("DESCRIPTION", "Helmet"); } } + + @Test + void testDeleteWithHardAlias() { + try (SqlSession session = sqlSessionFactory.openSession()) { + CommonDeleteMapper mapper = session.getMapper(CommonDeleteMapper.class); + + ItemMasterDynamicSQLSupport.ItemMaster im = itemMaster.withAlias("im"); + + DeleteStatementProvider deleteStatement = deleteFrom(im) + .where(notExists(select(orderLine.allColumns()) + .from(orderLine, "ol") + .where(orderLine.itemId, isEqualTo(im.itemId))) + ) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatement = "delete from ItemMaster im where not exists " + + "(select ol.* from OrderLine ol where ol.item_id = im.item_id)"; + assertThat(deleteStatement.getDeleteStatement()).isEqualTo(expectedStatement); + + int rows = mapper.delete(deleteStatement); + assertThat(rows).isEqualTo(1); + } + } + + @Test + void testDeleteWithSoftAlias() { + try (SqlSession session = sqlSessionFactory.openSession()) { + CommonDeleteMapper mapper = session.getMapper(CommonDeleteMapper.class); + + DeleteStatementProvider deleteStatement = deleteFrom(itemMaster, "im") + .where(notExists(select(orderLine.allColumns()) + .from(orderLine, "ol") + .where(orderLine.itemId, isEqualTo(itemMaster.itemId))) + ) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatement = "delete from ItemMaster im where not exists " + + "(select ol.* from OrderLine ol where ol.item_id = im.item_id)"; + assertThat(deleteStatement.getDeleteStatement()).isEqualTo(expectedStatement); + + int rows = mapper.delete(deleteStatement); + assertThat(rows).isEqualTo(1); + } + } + + @Test + void testUpdateWithHardAlias() { + try (SqlSession session = sqlSessionFactory.openSession()) { + CommonUpdateMapper mapper = session.getMapper(CommonUpdateMapper.class); + + ItemMasterDynamicSQLSupport.ItemMaster im = itemMaster.withAlias("im"); + + UpdateStatementProvider updateStatement = update(im) + .set(im.description).equalTo("No Orders") + .where(notExists(select(orderLine.allColumns()) + .from(orderLine, "ol") + .where(orderLine.itemId, isEqualTo(im.itemId))) + ) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatement = "update ItemMaster im " + + "set im.description = #{parameters.p1,jdbcType=VARCHAR} " + + "where not exists (select ol.* from OrderLine ol where ol.item_id = im.item_id)"; + assertThat(updateStatement.getUpdateStatement()).isEqualTo(expectedStatement); + + int rows = mapper.update(updateStatement); + assertThat(rows).isEqualTo(1); + } + } + + @Test + void testUpdateWithSoftAlias() { + try (SqlSession session = sqlSessionFactory.openSession()) { + CommonUpdateMapper mapper = session.getMapper(CommonUpdateMapper.class); + + UpdateStatementProvider updateStatement = update(itemMaster, "im") + .set(itemMaster.description).equalTo("No Orders") + .where(notExists(select(orderLine.allColumns()) + .from(orderLine, "ol") + .where(orderLine.itemId, isEqualTo(itemMaster.itemId))) + ) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatement = "update ItemMaster im " + + "set im.description = #{parameters.p1,jdbcType=VARCHAR} " + + "where not exists (select ol.* from OrderLine ol where ol.item_id = im.item_id)"; + assertThat(updateStatement.getUpdateStatement()).isEqualTo(expectedStatement); + + int rows = mapper.update(updateStatement); + assertThat(rows).isEqualTo(1); + } + } } diff --git a/src/test/java/examples/joins/ItemMasterDynamicSQLSupport.java b/src/test/java/examples/joins/ItemMasterDynamicSQLSupport.java index 23672af71..f88efcdcb 100644 --- a/src/test/java/examples/joins/ItemMasterDynamicSQLSupport.java +++ b/src/test/java/examples/joins/ItemMasterDynamicSQLSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,20 +18,20 @@ import java.sql.JDBCType; import java.util.Date; +import org.mybatis.dynamic.sql.AliasableSqlTable; import org.mybatis.dynamic.sql.SqlColumn; -import org.mybatis.dynamic.sql.SqlTable; public final class ItemMasterDynamicSQLSupport { public static final ItemMaster itemMaster = new ItemMaster(); public static final SqlColumn itemId = itemMaster.itemId; - public static final SqlColumn description = itemMaster.description; + public static final SqlColumn description = itemMaster.description; - public static final class ItemMaster extends SqlTable { + public static final class ItemMaster extends AliasableSqlTable { public final SqlColumn itemId = column("item_id", JDBCType.INTEGER); - public final SqlColumn description = column("description", JDBCType.DATE); + public final SqlColumn description = column("description", JDBCType.VARCHAR); public ItemMaster() { - super("ItemMaster"); + super("ItemMaster", ItemMaster::new); } } } diff --git a/src/test/java/examples/schema_supplier/SchemaSupplierTest.java b/src/test/java/examples/schema_supplier/SchemaSupplierTest.java index ac3fcf171..aa40c2310 100644 --- a/src/test/java/examples/schema_supplier/SchemaSupplierTest.java +++ b/src/test/java/examples/schema_supplier/SchemaSupplierTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -99,7 +99,7 @@ void testSchemaSwitchingProperty() { insertFlintstones(mapper); List records = mapper.select(SelectDSLCompleter.allRows()); - assertThat(records.size()).isEqualTo(2); + assertThat(records).hasSize(2); System.setProperty(SchemaSupplier.schema_property, "schema2"); diff --git a/src/test/kotlin/examples/kotlin/mybatis3/joins/ExistsTest.kt b/src/test/kotlin/examples/kotlin/mybatis3/joins/ExistsTest.kt index e3e803000..bb7eea316 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/joins/ExistsTest.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/joins/ExistsTest.kt @@ -27,8 +27,12 @@ import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.mybatis.dynamic.sql.util.kotlin.elements.invoke +import org.mybatis.dynamic.sql.util.kotlin.mybatis3.deleteFrom import org.mybatis.dynamic.sql.util.kotlin.mybatis3.select +import org.mybatis.dynamic.sql.util.kotlin.mybatis3.update +import org.mybatis.dynamic.sql.util.mybatis3.CommonDeleteMapper import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper +import org.mybatis.dynamic.sql.util.mybatis3.CommonUpdateMapper import java.io.InputStreamReader import java.sql.DriverManager @@ -48,6 +52,8 @@ class ExistsTest { val environment = Environment("test", JdbcTransactionFactory(), ds) val config = Configuration(environment) config.addMapper(CommonSelectMapper::class.java) + config.addMapper(CommonDeleteMapper::class.java) + config.addMapper(CommonUpdateMapper::class.java) return SqlSessionFactoryBuilder().build(config).openSession() } @@ -663,6 +669,116 @@ class ExistsTest { } } + @Test + fun testDeleteWithHardAlias() { + newSession().use { session -> + val mapper = session.getMapper(CommonDeleteMapper::class.java) + val im = itemMaster.withAlias("im") + val deleteStatement = deleteFrom(im) { + where { + not { + exists { + select(orderLine.allColumns()) { + from(orderLine, "ol") + where{orderLine.itemId isEqualTo im.itemId} + } + } + } + } + } + + val expectedStatement = "delete from ItemMaster im where not exists " + + "(select ol.* from OrderLine ol where ol.item_id = im.item_id)" + + assertThat(deleteStatement.deleteStatement).isEqualTo(expectedStatement) + val rows = mapper.delete(deleteStatement) + assertThat(rows).isEqualTo(1) + } + } + + @Test + fun testDeleteWithSoftAlias() { + newSession().use { session -> + val mapper = session.getMapper(CommonDeleteMapper::class.java) + val deleteStatement = deleteFrom(itemMaster, "im") { + where { + not { + exists { + select(orderLine.allColumns()) { + from(orderLine, "ol") + where{orderLine.itemId isEqualTo itemMaster.itemId} + } + } + } + } + } + + val expectedStatement = "delete from ItemMaster im where not exists " + + "(select ol.* from OrderLine ol where ol.item_id = im.item_id)" + + assertThat(deleteStatement.deleteStatement).isEqualTo(expectedStatement) + val rows = mapper.delete(deleteStatement) + assertThat(rows).isEqualTo(1) + } + } + + @Test + fun testUpdateWithHardAlias() { + newSession().use { session -> + val mapper = session.getMapper(CommonUpdateMapper::class.java) + val im = itemMaster.withAlias("im") + val updateStatement = update(im) { + set(im.description) equalTo "No Orders" + where { + not { + exists { + select(orderLine.allColumns()) { + from(orderLine, "ol") + where{orderLine.itemId isEqualTo im.itemId} + } + } + } + } + } + + val expectedStatement = "update ItemMaster im " + + "set im.description = #{parameters.p1,jdbcType=VARCHAR} " + + "where not exists (select ol.* from OrderLine ol where ol.item_id = im.item_id)" + + assertThat(updateStatement.updateStatement).isEqualTo(expectedStatement) + val rows = mapper.update(updateStatement) + assertThat(rows).isEqualTo(1) + } + } + + @Test + fun testUpdateWithSoftAlias() { + newSession().use { session -> + val mapper = session.getMapper(CommonUpdateMapper::class.java) + val updateStatement = update(itemMaster, "im") { + set(itemMaster.description) equalTo "No Orders" + where { + not { + exists { + select(orderLine.allColumns()) { + from(orderLine, "ol") + where{orderLine.itemId isEqualTo itemMaster.itemId} + } + } + } + } + } + + val expectedStatement = "update ItemMaster im " + + "set im.description = #{parameters.p1,jdbcType=VARCHAR} " + + "where not exists (select ol.* from OrderLine ol where ol.item_id = im.item_id)" + + assertThat(updateStatement.updateStatement).isEqualTo(expectedStatement) + val rows = mapper.update(updateStatement) + assertThat(rows).isEqualTo(1) + } + } + companion object { const val JDBC_URL = "jdbc:hsqldb:mem:aname" const val JDBC_DRIVER = "org.hsqldb.jdbcDriver" diff --git a/src/test/kotlin/examples/kotlin/mybatis3/joins/ItemMasterDynamicSQLSupport.kt b/src/test/kotlin/examples/kotlin/mybatis3/joins/ItemMasterDynamicSQLSupport.kt index e23df9750..3e9764d13 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/joins/ItemMasterDynamicSQLSupport.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/joins/ItemMasterDynamicSQLSupport.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 the original author or authors. + * Copyright 2016-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,18 +15,17 @@ */ package examples.kotlin.mybatis3.joins -import org.mybatis.dynamic.sql.SqlTable +import org.mybatis.dynamic.sql.AliasableSqlTable import org.mybatis.dynamic.sql.util.kotlin.elements.column import java.sql.JDBCType -import java.util.Date object ItemMasterDynamicSQLSupport { val itemMaster = ItemMaster() val itemId = itemMaster.itemId val description = itemMaster.description - class ItemMaster : SqlTable("ItemMaster") { + class ItemMaster : AliasableSqlTable("ItemMaster", ::ItemMaster) { val itemId = column(name = "item_id", jdbcType = JDBCType.INTEGER) - val description = column(name = "description", jdbcType = JDBCType.DATE) + val description = column(name = "description", jdbcType = JDBCType.VARCHAR) } } diff --git a/src/test/kotlin/examples/kotlin/spring/canonical/SpringKotlinSubQueryTest.kt b/src/test/kotlin/examples/kotlin/spring/canonical/SpringKotlinSubQueryTest.kt index d6fc1ca3b..d06b2649e 100644 --- a/src/test/kotlin/examples/kotlin/spring/canonical/SpringKotlinSubQueryTest.kt +++ b/src/test/kotlin/examples/kotlin/spring/canonical/SpringKotlinSubQueryTest.kt @@ -15,6 +15,8 @@ */ package examples.kotlin.spring.canonical +import examples.kotlin.mybatis3.joins.ItemMasterDynamicSQLSupport.itemMaster +import examples.kotlin.mybatis3.joins.OrderLineDynamicSQLSupport.orderLine import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.person import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.firstName import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.id @@ -23,8 +25,10 @@ import org.junit.jupiter.api.Test import org.mybatis.dynamic.sql.DerivedColumn import org.mybatis.dynamic.sql.util.kotlin.elements.`as` import org.mybatis.dynamic.sql.util.kotlin.elements.invoke +import org.mybatis.dynamic.sql.util.kotlin.spring.deleteFrom import org.mybatis.dynamic.sql.util.kotlin.spring.select import org.mybatis.dynamic.sql.util.kotlin.spring.selectList +import org.mybatis.dynamic.sql.util.kotlin.spring.update import org.springframework.beans.factory.annotation.Autowired import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate import org.springframework.test.context.junit.jupiter.SpringJUnitConfig @@ -148,4 +152,48 @@ open class SpringKotlinSubQueryTest { assertThat(rows[2]).containsEntry("PERSONID", 2) assertThat(rows[2]).containsEntry("ROWNUM", 3) } + + @Test + fun testDeleteWithSoftAliasRendersProperlyWithSpring() { + val deleteStatement = deleteFrom(itemMaster, "im") { + where { + not { + exists { + select(orderLine.allColumns()) { + from(orderLine, "ol") + where { orderLine.itemId isEqualTo itemMaster.itemId } + } + } + } + } + } + + val expectedStatement = "delete from ItemMaster im where not exists " + + "(select ol.* from OrderLine ol where ol.item_id = im.item_id)" + + assertThat(deleteStatement.deleteStatement).isEqualTo(expectedStatement) + } + + @Test + fun testUpdateWithSoftAliasRendersProperlyWithSpring() { + val updateStatement = update(itemMaster, "im") { + set(itemMaster.description) equalTo "No Orders" + where { + not { + exists { + select(orderLine.allColumns()) { + from(orderLine, "ol") + where { orderLine.itemId isEqualTo itemMaster.itemId } + } + } + } + } + } + + val expectedStatement = "update ItemMaster im " + + "set im.description = :p1 " + + "where not exists (select ol.* from OrderLine ol where ol.item_id = im.item_id)" + + assertThat(updateStatement.updateStatement).isEqualTo(expectedStatement) + } }