From 08d91bdf84282b8016cc312dd6dca1f5847e75c9 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 20 Mar 2019 16:42:05 +0200 Subject: [PATCH] #75 - Add anonymous indexed bind markers. Anonymous indexed bind markers come with a static placeholder symbol for all parameter occurrences and they are bound by index. Original pull request: #84. --- .../r2dbc/dialect/AnonymousBindMarkers.java | 59 ++++++++++++++++++ .../r2dbc/dialect/BindMarkersFactory.java | 16 +++++ .../data/r2dbc/dialect/IndexedBindMarker.java | 60 +++++++++++++++++++ .../r2dbc/dialect/IndexedBindMarkers.java | 44 -------------- .../AnonymousBindMarkersUnitTests.java | 60 +++++++++++++++++++ 5 files changed, 195 insertions(+), 44 deletions(-) create mode 100644 src/main/java/org/springframework/data/r2dbc/dialect/AnonymousBindMarkers.java create mode 100644 src/main/java/org/springframework/data/r2dbc/dialect/IndexedBindMarker.java create mode 100644 src/test/java/org/springframework/data/r2dbc/dialect/AnonymousBindMarkersUnitTests.java diff --git a/src/main/java/org/springframework/data/r2dbc/dialect/AnonymousBindMarkers.java b/src/main/java/org/springframework/data/r2dbc/dialect/AnonymousBindMarkers.java new file mode 100644 index 0000000..d2ef0b1 --- /dev/null +++ b/src/main/java/org/springframework/data/r2dbc/dialect/AnonymousBindMarkers.java @@ -0,0 +1,59 @@ +/* + * Copyright 2019 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.r2dbc.dialect; + +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +/** + * Anonymous, index-based bind marker using a static placeholder. Instances are bound by the ordinal position ordered by + * the appearance of the placeholder. This implementation creates indexed bind markers using an anonymous placeholder + * that correlates with an index. + * + * @author Mark Paluch + */ +class AnonymousBindMarkers implements BindMarkers { + + private static final AtomicIntegerFieldUpdater COUNTER_INCREMENTER = AtomicIntegerFieldUpdater + .newUpdater(AnonymousBindMarkers.class, "counter"); + + // access via COUNTER_INCREMENTER + @SuppressWarnings("unused") private volatile int counter; + + private final String placeholder; + + /** + * Creates a new {@link AnonymousBindMarkers} instance given {@code placeholder}. + * + * @param placeholder parameter bind marker. + */ + AnonymousBindMarkers(String placeholder) { + this.counter = 0; + this.placeholder = placeholder; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.r2dbc.dialect.BindMarkers#next() + */ + @Override + public BindMarker next() { + + int index = COUNTER_INCREMENTER.getAndIncrement(this); + + return new IndexedBindMarker(placeholder, index); + } + +} diff --git a/src/main/java/org/springframework/data/r2dbc/dialect/BindMarkersFactory.java b/src/main/java/org/springframework/data/r2dbc/dialect/BindMarkersFactory.java index 751d8ae..10e3900 100644 --- a/src/main/java/org/springframework/data/r2dbc/dialect/BindMarkersFactory.java +++ b/src/main/java/org/springframework/data/r2dbc/dialect/BindMarkersFactory.java @@ -43,6 +43,22 @@ public interface BindMarkersFactory { return () -> new IndexedBindMarkers(prefix, beginWith); } + /** + * Creates anonymous, index-based bind marker using a static placeholder. Instances are bound by the ordinal position + * ordered by the appearance of the placeholder. This implementation creates indexed bind markers using an anonymous + * placeholder that correlates with an index. + * + * @param placeholder parameter placeholder. + * @return a {@link BindMarkersFactory} using {@code placeholder}. + * @see io.r2dbc.spi.Statement#bindNull(int, Class) + * @see io.r2dbc.spi.Statement#bind(int, Object) + */ + static BindMarkersFactory anonymous(String placeholder) { + + Assert.hasText(placeholder, "Placeholder must not be empty!"); + return () -> new AnonymousBindMarkers(placeholder); + } + /** * Create named {@link BindMarkers} using identifiers to bind parameters. Named bind markers can support * {@link BindMarkers#next(String) name hints}. If no {@link BindMarkers#next(String) hint} is given, named bind diff --git a/src/main/java/org/springframework/data/r2dbc/dialect/IndexedBindMarker.java b/src/main/java/org/springframework/data/r2dbc/dialect/IndexedBindMarker.java new file mode 100644 index 0000000..109d929 --- /dev/null +++ b/src/main/java/org/springframework/data/r2dbc/dialect/IndexedBindMarker.java @@ -0,0 +1,60 @@ +/* + * Copyright 2019 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.r2dbc.dialect; + +import io.r2dbc.spi.Statement; + +/** + * A single indexed bind marker. + */ +class IndexedBindMarker implements BindMarker { + + private final String placeholder; + + private int index; + + IndexedBindMarker(String placeholder, int index) { + this.placeholder = placeholder; + this.index = index; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.r2dbc.dialect.BindMarker#getPlaceholder() + */ + @Override + public String getPlaceholder() { + return placeholder; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.r2dbc.dialect.BindMarker#bindValue(io.r2dbc.spi.Statement, java.lang.Object) + */ + @Override + public void bind(Statement statement, Object value) { + statement.bind(this.index, value); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.r2dbc.dialect.BindMarker#bindNull(io.r2dbc.spi.Statement, java.lang.Class) + */ + @Override + public void bindNull(Statement statement, Class valueType) { + statement.bindNull(this.index, valueType); + } +} diff --git a/src/main/java/org/springframework/data/r2dbc/dialect/IndexedBindMarkers.java b/src/main/java/org/springframework/data/r2dbc/dialect/IndexedBindMarkers.java index 54b5e75..50a80c0 100644 --- a/src/main/java/org/springframework/data/r2dbc/dialect/IndexedBindMarkers.java +++ b/src/main/java/org/springframework/data/r2dbc/dialect/IndexedBindMarkers.java @@ -1,7 +1,5 @@ package org.springframework.data.r2dbc.dialect; -import io.r2dbc.spi.Statement; - import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; /** @@ -45,46 +43,4 @@ class IndexedBindMarkers implements BindMarkers { return new IndexedBindMarker(prefix + "" + (index + offset), index); } - - /** - * A single indexed bind marker. - */ - static class IndexedBindMarker implements BindMarker { - - private final String placeholder; - - private int index; - - IndexedBindMarker(String placeholder, int index) { - this.placeholder = placeholder; - this.index = index; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.r2dbc.dialect.BindMarker#getPlaceholder() - */ - @Override - public String getPlaceholder() { - return placeholder; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.r2dbc.dialect.BindMarker#bindValue(io.r2dbc.spi.Statement, java.lang.Object) - */ - @Override - public void bind(Statement statement, Object value) { - statement.bind(this.index, value); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.r2dbc.dialect.BindMarker#bindNull(io.r2dbc.spi.Statement, java.lang.Class) - */ - @Override - public void bindNull(Statement statement, Class valueType) { - statement.bindNull(this.index, valueType); - } - } } diff --git a/src/test/java/org/springframework/data/r2dbc/dialect/AnonymousBindMarkersUnitTests.java b/src/test/java/org/springframework/data/r2dbc/dialect/AnonymousBindMarkersUnitTests.java new file mode 100644 index 0000000..112f5b5 --- /dev/null +++ b/src/test/java/org/springframework/data/r2dbc/dialect/AnonymousBindMarkersUnitTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2019 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.r2dbc.dialect; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import io.r2dbc.spi.Statement; + +import org.junit.Test; + +/** + * Unit tests for {@link AnonymousBindMarkers}. + * + * @author Mark Paluch + */ +public class AnonymousBindMarkersUnitTests { + + @Test // gh-75 + public void shouldCreateNewBindMarkers() { + + BindMarkersFactory factory = BindMarkersFactory.anonymous("?"); + + BindMarkers bindMarkers1 = factory.create(); + BindMarkers bindMarkers2 = factory.create(); + + assertThat(bindMarkers1.next().getPlaceholder()).isEqualTo("?"); + assertThat(bindMarkers2.next().getPlaceholder()).isEqualTo("?"); + } + + @Test // gh-75 + public void shouldBindByIndex() { + + Statement statement = mock(Statement.class); + + BindMarkers bindMarkers = BindMarkersFactory.anonymous("?").create(); + + BindMarker first = bindMarkers.next(); + BindMarker second = bindMarkers.next(); + + second.bind(statement, "foo"); + first.bindNull(statement, Object.class); + + verify(statement).bindNull(0, Object.class); + verify(statement).bind(1, "foo"); + } +}