diff --git a/CHANGELOG.md b/CHANGELOG.md index ad302dbc5..ba43b4a02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ For examples of global and statement configuration, see the "Configuration of th page for details. ([#515](https://github.com/mybatis/mybatis-dynamic-sql/pull/515)) 5. Added several checks for invalid SQL ([#516](https://github.com/mybatis/mybatis-dynamic-sql/pull/516)) 6. Added documentation for the various exceptions thrown by the library ([#517](https://github.com/mybatis/mybatis-dynamic-sql/pull/517)) +7. Update the "insertSelect" method in the Kotlin DSL to make it consistent with the other insert methods ([#524](https://github.com/mybatis/mybatis-dynamic-sql/pull/524)) ## Release 1.4.0 - March 3, 2022 diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinSubQueryBuilders.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinSubQueryBuilders.kt index 024283575..a2355750b 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinSubQueryBuilders.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinSubQueryBuilders.kt @@ -18,12 +18,14 @@ package org.mybatis.dynamic.sql.util.kotlin import org.mybatis.dynamic.sql.BasicColumn import org.mybatis.dynamic.sql.SqlBuilder import org.mybatis.dynamic.sql.SqlColumn +import org.mybatis.dynamic.sql.SqlTable +import org.mybatis.dynamic.sql.insert.InsertSelectModel import org.mybatis.dynamic.sql.select.SelectModel import org.mybatis.dynamic.sql.util.Buildable import org.mybatis.dynamic.sql.util.Messages @MyBatisDslMarker -sealed class KotlinBaseSubQueryBuilder : Buildable { +sealed class KotlinBaseSubQueryBuilder { private var selectBuilder: KotlinSelectBuilder? = null fun select(vararg selectList: BasicColumn, completer: SelectCompleter): Unit = @@ -40,28 +42,54 @@ sealed class KotlinBaseSubQueryBuilder : Buildable { selectBuilder = KotlinSelectBuilder(SqlBuilder.selectDistinct(selectList)).apply(completer) } - override fun build(): SelectModel = + internal fun buildSelectModel(): SelectModel = selectBuilder?.build()?: throw KInvalidSQLException(Messages.getString("ERROR.28")) //$NON-NLS-1$ } -class KotlinSubQueryBuilder : KotlinBaseSubQueryBuilder() +class KotlinSubQueryBuilder : KotlinBaseSubQueryBuilder(), Buildable { + override fun build(): SelectModel = buildSelectModel() +} -class KotlinQualifiedSubQueryBuilder : KotlinBaseSubQueryBuilder() { +class KotlinQualifiedSubQueryBuilder : KotlinBaseSubQueryBuilder(), Buildable { var correlationName: String? = null operator fun String.unaryPlus() { correlationName = this } + + override fun build(): SelectModel = buildSelectModel() } typealias InsertSelectCompleter = KotlinInsertSelectSubQueryBuilder.() -> Unit -class KotlinInsertSelectSubQueryBuilder : KotlinBaseSubQueryBuilder() { - internal var columnList: List>? = null +class KotlinInsertSelectSubQueryBuilder : KotlinBaseSubQueryBuilder(), Buildable { + private var columnList: List>? = null + private var table: SqlTable? = null + + fun into(table: SqlTable) { + this.table = table + } fun columns(vararg columnList: SqlColumn<*>): Unit = columns(columnList.asList()) fun columns(columnList: List>) { this.columnList = columnList } + + override fun build(): InsertSelectModel { + if (table == null) { + throw KInvalidSQLException(Messages.getString("ERROR.29")) //$NON-NLS-1$ + } + + return if (columnList == null) { + SqlBuilder.insertInto(table) + .withSelectStatement { buildSelectModel() } + .build() + } else { + SqlBuilder.insertInto(table) + .withColumnList(columnList) + .withSelectStatement { buildSelectModel() } + .build() + } + } } 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 12932f179..0a0ef660d 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 @@ -77,20 +77,17 @@ fun insertInto(table: SqlTable, completer: GeneralInsertCompleter): GeneralInser fun insertMultiple(rows: Collection, completer: KotlinMultiRowInsertCompleter): MultiRowInsertModel = KotlinMultiRowInsertBuilder(rows).apply(completer).build() +@Deprecated("Please use the new form - move the table into the lambda with into(table)") fun insertSelect(table: SqlTable, completer: InsertSelectCompleter): InsertSelectModel = - with(KotlinInsertSelectSubQueryBuilder().apply(completer)) { - if (columnList == null) { - SqlBuilder.insertInto(table) - .withSelectStatement(this) - .build() - } else { - SqlBuilder.insertInto(table) - .withColumnList(columnList) - .withSelectStatement(this) - .build() - } + with(KotlinInsertSelectSubQueryBuilder()) { + into(table) + apply(completer) + build() } +fun insertSelect(completer: InsertSelectCompleter): InsertSelectModel = + KotlinInsertSelectSubQueryBuilder().apply(completer).build() + @Deprecated("Please switch to the insertBatch statement in the model package") fun BatchInsertDSL.IntoGatherer.into( table: SqlTable, diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/MapperSupportFunctions.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/MapperSupportFunctions.kt index b57867580..deb50298a 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/MapperSupportFunctions.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/MapperSupportFunctions.kt @@ -128,7 +128,10 @@ fun insertSelect( table: SqlTable, completer: InsertSelectCompleter ): Int = - insertSelect(table, completer).run(mapper) + insertSelect { + into(table) + run(completer) + }.run(mapper) fun selectDistinct( mapper: (SelectStatementProvider) -> List, 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 9d5fe760d..cd588866f 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 @@ -83,9 +83,13 @@ fun insertMultiple( ): MultiRowInsertStatementProvider = insertMultiple(rows, completer).render(RenderingStrategies.MYBATIS3) +@Deprecated("Please use the new form - move the table into the lambda with into(table)") fun insertSelect(table: SqlTable, completer: InsertSelectCompleter): InsertSelectStatementProvider = insertSelect(table, completer).render(RenderingStrategies.MYBATIS3) +fun insertSelect(completer: InsertSelectCompleter): InsertSelectStatementProvider = + insertSelect(completer).render(RenderingStrategies.MYBATIS3) + @Deprecated("Please switch to the insertBatch statement in the mybatis3 package") fun BatchInsertDSL.IntoGatherer.into(table: SqlTable, completer: BatchInsertDSL.() -> Unit): BatchInsert = into(table, completer).render(RenderingStrategies.MYBATIS3) diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/NamedParameterJdbcTemplateExtensions.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/NamedParameterJdbcTemplateExtensions.kt index 7c7b352cb..c2b8dcad8 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/NamedParameterJdbcTemplateExtensions.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/NamedParameterJdbcTemplateExtensions.kt @@ -148,9 +148,13 @@ fun NamedParameterJdbcTemplate.insertMultiple( ): Int = update(insertStatement.insertStatement, BeanPropertySqlParameterSource(insertStatement), keyHolder) +@Deprecated("Please use the new form - move the table into the lambda with into(table)") fun NamedParameterJdbcTemplate.insertSelect(table: SqlTable, completer: InsertSelectCompleter): Int = insertSelect(org.mybatis.dynamic.sql.util.kotlin.spring.insertSelect(table, completer)) +fun NamedParameterJdbcTemplate.insertSelect(completer: InsertSelectCompleter): Int = + insertSelect(org.mybatis.dynamic.sql.util.kotlin.spring.insertSelect(completer)) + fun NamedParameterJdbcTemplate.insertSelect(insertStatement: InsertSelectStatementProvider): Int = update(insertStatement.insertStatement, MapSqlParameterSource(insertStatement.parameters)) 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 cf60008cb..058645a99 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 @@ -83,9 +83,13 @@ fun insertMultiple( ): MultiRowInsertStatementProvider = insertMultiple(rows, completer).render(RenderingStrategies.SPRING_NAMED_PARAMETER) +@Deprecated("Please use the new form - move the table into the lambda with into(table)") fun insertSelect(table: SqlTable, completer: InsertSelectCompleter): InsertSelectStatementProvider = insertSelect(table, completer).render(RenderingStrategies.SPRING_NAMED_PARAMETER) +fun insertSelect(completer: InsertSelectCompleter): InsertSelectStatementProvider = + insertSelect(completer).render(RenderingStrategies.SPRING_NAMED_PARAMETER) + @Deprecated("Please switch to the insertBatch statement in the spring package") fun BatchInsertDSL.IntoGatherer.into(table: SqlTable, completer: BatchInsertDSL.() -> Unit): BatchInsert = into(table, completer).render(RenderingStrategies.SPRING_NAMED_PARAMETER) diff --git a/src/main/resources/org/mybatis/dynamic/sql/util/messages.properties b/src/main/resources/org/mybatis/dynamic/sql/util/messages.properties index 0501b0c92..808deef01 100644 --- a/src/main/resources/org/mybatis/dynamic/sql/util/messages.properties +++ b/src/main/resources/org/mybatis/dynamic/sql/util/messages.properties @@ -44,3 +44,4 @@ ERROR.25=Insert Statements Must Contain an "into" phrase ERROR.26=Multiple Row Insert Statements Must Contain an "into" phrase ERROR.27=You must specify a "from" clause before any other clauses in a select statement ERROR.28=You must specify a select statement in a sub query +ERROR.29=Insert Select Statements Must Contain an "into" phrase diff --git a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt index 7aaba90b9..077f7bb9a 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt @@ -38,6 +38,7 @@ import org.mybatis.dynamic.sql.util.kotlin.elements.add import org.mybatis.dynamic.sql.util.kotlin.elements.constant import org.mybatis.dynamic.sql.util.kotlin.elements.isIn import org.mybatis.dynamic.sql.util.kotlin.mybatis3.insertInto +import org.mybatis.dynamic.sql.util.kotlin.mybatis3.insertSelect import org.mybatis.dynamic.sql.util.kotlin.mybatis3.select import java.io.InputStreamReader import java.sql.DriverManager @@ -258,6 +259,32 @@ class PersonMapperTest { } } + @Test + fun testDeprecatedInsertSelect() { + newSession().use { session -> + val mapper = session.getMapper(PersonMapper::class.java) + + val insertStatement = insertSelect(person) { + columns(id, firstName, lastName, employed, occupation, addressId, birthDate) + select(add(id, constant("100")), firstName, lastName, employed, occupation, addressId, birthDate) { + from(person) + orderBy(id) + } + } + + val expected = "insert into Person " + + "(id, first_name, last_name, employed, occupation, address_id, birth_date) " + + "select (id + 100), first_name, last_name, employed, occupation, address_id, birth_date " + + "from Person order by id" + + assertThat(insertStatement.insertStatement).isEqualTo(expected) + + val rows = mapper.insertSelect(insertStatement) + + assertThat(rows).isEqualTo(6) + } + } + @Test fun testInsertBatch() { newSession(ExecutorType.BATCH).use { session -> diff --git a/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTemplateDirectTest.kt b/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTemplateDirectTest.kt index f37cd2046..77f9c26ca 100644 --- a/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTemplateDirectTest.kt +++ b/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTemplateDirectTest.kt @@ -303,6 +303,39 @@ open class CanonicalSpringKotlinTemplateDirectTest { @Test fun testInsertSelect() { + val rows = template.insertSelect { + into(person) + columns(id, firstName, lastName, birthDate, employed, occupation, addressId) + select( + add(id, constant("100")), firstName, lastName, birthDate, employed, occupation, addressId + ) { + from(person) + orderBy(id) + } + } + + assertThat(rows).isEqualTo(6) + + val records = template.select(id, firstName, lastName, birthDate, employed, occupation, addressId) { + from(person) + where { id isGreaterThanOrEqualTo 100 } + orderBy(id) + }.withRowMapper(personRowMapper) + + assertThat(records).hasSize(6) + with(records[1]) { + assertThat(id).isEqualTo(102) + assertThat(firstName).isEqualTo("Wilma") + assertThat(lastName).isEqualTo(LastName("Flintstone")) + assertThat(birthDate).isNotNull + assertThat(employed).isTrue + assertThat(occupation).isEqualTo("Accountant") + assertThat(addressId).isEqualTo(1) + } + } + + @Test + fun testDeprecatedInsertSelect() { val rows = template.insertSelect(person) { columns(id, firstName, lastName, birthDate, employed, occupation, addressId) select( diff --git a/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTest.kt b/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTest.kt index a134b74f8..525fd41f7 100644 --- a/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTest.kt +++ b/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTest.kt @@ -512,7 +512,8 @@ open class CanonicalSpringKotlinTest { @Test fun testInsertSelect() { - val insertStatement = insertSelect(person) { + val insertStatement = insertSelect { + into(person) columns(id, firstName, lastName, birthDate, employed, occupation, addressId) select(add(id, constant("100")), firstName, lastName, birthDate, employed, occupation, addressId) { from(person) @@ -547,6 +548,56 @@ open class CanonicalSpringKotlinTest { } } + @Test + fun testInsertSelectNoTable() { + assertThatExceptionOfType(KInvalidSQLException::class.java).isThrownBy { + insertSelect { + columns(id, firstName, lastName, birthDate, employed, occupation, addressId) + select(add(id, constant("100")), firstName, lastName, birthDate, employed, occupation, addressId) { + from(person) + orderBy(id) + } + } + }.withMessage(Messages.getString("ERROR.29")) + } + + @Test + fun testDeprecatedInsertSelect() { + val insertStatement = insertSelect(person) { + columns(id, firstName, lastName, birthDate, employed, occupation, addressId) + select(add(id, constant("100")), firstName, lastName, birthDate, employed, occupation, addressId) { + from(person) + orderBy(id) + } + } + + assertThat(insertStatement.insertStatement).isEqualTo( + "insert into Person (id, first_name, last_name, birth_date, employed, occupation, address_id) " + + "select (id + 100), first_name, last_name, birth_date, employed, occupation, address_id " + + "from Person " + + "order by id" + ) + val rows = template.insertSelect(insertStatement) + assertThat(rows).isEqualTo(6) + + val records = template.select(id, firstName, lastName, birthDate, employed, occupation, addressId) { + from(person) + where { id isGreaterThanOrEqualTo 100 } + orderBy(id) + }.withRowMapper(personRowMapper) + + assertThat(records).hasSize(6) + with(records[1]) { + assertThat(id).isEqualTo(102) + assertThat(firstName).isEqualTo("Wilma") + assertThat(lastName).isEqualTo(LastName("Flintstone")) + assertThat(birthDate).isNotNull + assertThat(employed).isTrue + assertThat(occupation).isEqualTo("Accountant") + assertThat(addressId).isEqualTo(1) + } + } + @Test fun testInsertSelectNoColumns() { val insertStatement = insertSelect(person) {