Skip to content

Commit ec526aa

Browse files
authored
Merge pull request #918 from jeffgbutler/kotlin-example
Add a Kotlin example of a custom condition
2 parents eeedb40 + 10fe79d commit ec526aa

File tree

10 files changed

+150
-9
lines changed

10 files changed

+150
-9
lines changed

src/main/java/org/mybatis/dynamic/sql/where/condition/CaseInsensitiveVisitableCondition.java src/main/java/org/mybatis/dynamic/sql/where/condition/CaseInsensitiveRenderableCondition.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import org.mybatis.dynamic.sql.render.RenderingContext;
2121
import org.mybatis.dynamic.sql.util.FragmentAndParameters;
2222

23-
public interface CaseInsensitiveVisitableCondition extends RenderableCondition<String> {
23+
public interface CaseInsensitiveRenderableCondition extends RenderableCondition<String> {
2424

2525
@Override
2626
default FragmentAndParameters renderLeftColumn(RenderingContext renderingContext,

src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitive.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import org.mybatis.dynamic.sql.util.Validator;
2828

2929
public class IsInCaseInsensitive extends AbstractListValueCondition<String>
30-
implements CaseInsensitiveVisitableCondition {
30+
implements CaseInsensitiveRenderableCondition {
3131
private static final IsInCaseInsensitive EMPTY = new IsInCaseInsensitive(Collections.emptyList());
3232

3333
public static IsInCaseInsensitive empty() {

src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import org.mybatis.dynamic.sql.util.Utilities;
2828

2929
public class IsInCaseInsensitiveWhenPresent extends AbstractListValueCondition<String>
30-
implements CaseInsensitiveVisitableCondition {
30+
implements CaseInsensitiveRenderableCondition {
3131
private static final IsInCaseInsensitiveWhenPresent EMPTY =
3232
new IsInCaseInsensitiveWhenPresent(Collections.emptyList());
3333

src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitive.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import org.mybatis.dynamic.sql.util.StringUtilities;
2424

2525
public class IsLikeCaseInsensitive extends AbstractSingleValueCondition<String>
26-
implements CaseInsensitiveVisitableCondition {
26+
implements CaseInsensitiveRenderableCondition {
2727
private static final IsLikeCaseInsensitive EMPTY = new IsLikeCaseInsensitive("") { //$NON-NLS-1$
2828
@Override
2929
public String value() {

src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitive.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import org.mybatis.dynamic.sql.util.Validator;
2828

2929
public class IsNotInCaseInsensitive extends AbstractListValueCondition<String>
30-
implements CaseInsensitiveVisitableCondition {
30+
implements CaseInsensitiveRenderableCondition {
3131
private static final IsNotInCaseInsensitive EMPTY = new IsNotInCaseInsensitive(Collections.emptyList());
3232

3333
public static IsNotInCaseInsensitive empty() {

src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import org.mybatis.dynamic.sql.util.Utilities;
2828

2929
public class IsNotInCaseInsensitiveWhenPresent extends AbstractListValueCondition<String>
30-
implements CaseInsensitiveVisitableCondition {
30+
implements CaseInsensitiveRenderableCondition {
3131
private static final IsNotInCaseInsensitiveWhenPresent EMPTY =
3232
new IsNotInCaseInsensitiveWhenPresent(Collections.emptyList());
3333

src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitive.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import org.mybatis.dynamic.sql.util.StringUtilities;
2424

2525
public class IsNotLikeCaseInsensitive extends AbstractSingleValueCondition<String>
26-
implements CaseInsensitiveVisitableCondition {
26+
implements CaseInsensitiveRenderableCondition {
2727
private static final IsNotLikeCaseInsensitive EMPTY = new IsNotLikeCaseInsensitive("") { //$NON-NLS-1$
2828
@Override
2929
public String value() {

src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ open class GroupingCriteriaCollector : SubCriteriaCollector() {
235235
.build()
236236
}
237237

238-
// infix functions...we may be able to rewrite these as extension functions once Kotlin solves the multiple
239-
// receivers problem (https://youtrack.jetbrains.com/issue/KT-42435)
238+
// infix functions...we may be able to rewrite these as extension functions once Kotlin implements the context
239+
// parameters proposal (https://github.com/Kotlin/KEEP/issues/367)
240240

241241
// conditions for all data types
242242
fun BindableColumn<*>.isNull() = invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNull())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package examples.kotlin.mybatis3.mariadb
2+
3+
import java.util.function.Predicate
4+
import java.util.function.Function
5+
import org.mybatis.dynamic.sql.AbstractSingleValueCondition
6+
import org.mybatis.dynamic.sql.BindableColumn
7+
import org.mybatis.dynamic.sql.render.RenderingContext
8+
import org.mybatis.dynamic.sql.util.FragmentAndParameters
9+
10+
sealed class KIsLikeEscape<T : Any>(
11+
value: T,
12+
private val escapeCharacter: Char? = null
13+
) : AbstractSingleValueCondition<T>(value) {
14+
15+
override fun operator(): String = "like"
16+
17+
override fun renderCondition(
18+
renderingContext: RenderingContext,
19+
leftColumn: BindableColumn<T>
20+
): FragmentAndParameters = with(super.renderCondition(renderingContext, leftColumn)) {
21+
escapeCharacter?.let { mapFragment { "$it ESCAPE '$escapeCharacter'" } } ?: this
22+
}
23+
24+
override fun filter(predicate: Predicate<in T>): KIsLikeEscape<T> =
25+
filterSupport(predicate, EmptyIsLikeEscape::empty, this)
26+
27+
fun <R : Any> map(mapper : Function<in T, out R>): KIsLikeEscape<R> =
28+
mapSupport(mapper, { r -> ConcreteIsLikeEscape(r, escapeCharacter) }, EmptyIsLikeEscape::empty)
29+
30+
companion object {
31+
fun <T: Any> isLike(value: T, escapeCharacter: Char? = null) : KIsLikeEscape<T> =
32+
ConcreteIsLikeEscape(value, escapeCharacter)
33+
}
34+
}
35+
36+
private class ConcreteIsLikeEscape<T: Any>(
37+
value: T,
38+
escapeCharacter: Char? = null
39+
) : KIsLikeEscape<T>(value, escapeCharacter)
40+
41+
private class EmptyIsLikeEscape : KIsLikeEscape<Any>(-1) {
42+
override fun isEmpty(): Boolean = true
43+
44+
override fun value(): Any {
45+
throw NoSuchElementException("No value present")
46+
}
47+
48+
companion object {
49+
private val EMPTY: KIsLikeEscape<Any> = EmptyIsLikeEscape()
50+
51+
@Suppress("UNCHECKED_CAST")
52+
internal fun <T : Any> empty(): KIsLikeEscape<T> = EMPTY as KIsLikeEscape<T>
53+
}
54+
}

src/test/kotlin/examples/kotlin/mybatis3/mariadb/KMariaDBTest.kt

+87
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import org.mybatis.dynamic.sql.util.mybatis3.CommonUpdateMapper
3838
import org.testcontainers.containers.MariaDBContainer
3939
import org.testcontainers.junit.jupiter.Container
4040
import org.testcontainers.junit.jupiter.Testcontainers
41+
import java.util.Locale
4142

4243
@Testcontainers
4344
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@@ -142,6 +143,92 @@ class KMariaDBTest {
142143
}
143144
}
144145

146+
147+
/**
148+
* Shortcut function for KIsLikeEscape
149+
*
150+
* Note that the following example uses of this function are a bit awkward and don't look as natural as the
151+
* built-in conditions. We should be able to improve this once Kotlin implements the context parameters
152+
* proposal (https://github.com/Kotlin/KEEP/issues/367)
153+
*/
154+
fun <T : Any> isLike(value: T, escapeCharacter: Char? = null) = KIsLikeEscape.isLike(value, escapeCharacter)
155+
156+
@Test
157+
fun testIsLikeEscape() {
158+
sqlSessionFactory.openSession().use { session ->
159+
val mapper = session.getMapper(CommonSelectMapper::class.java)
160+
val selectStatement = select(id, description) {
161+
from(items)
162+
where {
163+
description(isLike("Item 1%", '#'))
164+
}
165+
}
166+
167+
assertThat(selectStatement.selectStatement).isEqualTo("select id, description from items where description like #{parameters.p1,jdbcType=VARCHAR} ESCAPE '#'")
168+
assertThat(selectStatement.parameters).containsEntry("p1", "Item 1%")
169+
170+
val rows = mapper.selectManyMappedRows(selectStatement)
171+
assertThat(rows).hasSize(11)
172+
}
173+
}
174+
175+
@Test
176+
fun testIsLikeEscapeNoEscapeCharacter() {
177+
val selectStatement = select(id, description) {
178+
from(items)
179+
where {
180+
description(isLike("%fred%"))
181+
}
182+
}
183+
184+
assertThat(selectStatement.selectStatement).isEqualTo("select id, description from items where description like #{parameters.p1,jdbcType=VARCHAR}")
185+
assertThat(selectStatement.parameters).containsEntry("p1", "%fred%")
186+
}
187+
188+
@Test
189+
fun testIsLikeEscapeMap() {
190+
val selectStatement = select(id, description) {
191+
from(items)
192+
where {
193+
description(isLike("%fred%", '#').map { s -> s.uppercase(Locale.getDefault()) })
194+
}
195+
}
196+
197+
assertThat(selectStatement.selectStatement).isEqualTo("select id, description from items where description like #{parameters.p1,jdbcType=VARCHAR} ESCAPE '#'")
198+
assertThat(selectStatement.parameters).containsEntry("p1", "%FRED%")
199+
}
200+
201+
@Test
202+
fun testIsLikeEscapeFilter() {
203+
val selectStatement = select(id, description) {
204+
from(items)
205+
where {
206+
description(isLike("%fred%", '#').filter { _ -> false })
207+
}
208+
configureStatement { isNonRenderingWhereClauseAllowed = true }
209+
}
210+
211+
assertThat(selectStatement.selectStatement).isEqualTo("select id, description from items")
212+
assertThat(selectStatement.parameters).isEmpty()
213+
}
214+
215+
@Test
216+
fun testIsLikeEscapeFilterMapFilter() {
217+
val selectStatement = select(id, description) {
218+
from(items)
219+
where {
220+
description(isLike("%fred%", '#')
221+
.filter { _ -> true }
222+
.map { s -> s.uppercase(Locale.getDefault()) }
223+
.filter{_ -> false })
224+
}
225+
configureStatement { isNonRenderingWhereClauseAllowed = true }
226+
}
227+
228+
assertThat(selectStatement.selectStatement).isEqualTo("select id, description from items")
229+
assertThat(selectStatement.parameters).isEmpty()
230+
}
231+
145232
companion object {
146233
@Container
147234
private val mariadb = MariaDBContainer(TestContainersConfiguration.MARIADB_LATEST)

0 commit comments

Comments
 (0)