This is an automated email from the ASF dual-hosted git repository.

RocMarshal pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/flink-connector-jdbc.git


The following commit(s) were added to refs/heads/main by this push:
     new af994651 [FLINK-38733] Add new SplitterEnumerator on JdbcSource (#180)
af994651 is described below

commit af99465109ebafdf77b4c65b9f4526989ad93cda
Author: Joao Boto <[email protected]>
AuthorDate: Thu Jun 18 07:10:45 2026 +0200

    [FLINK-38733] Add new SplitterEnumerator on JdbcSource (#180)
---
 .../jdbc/core/datastream/source/JdbcSource.java    |  62 ++++---
 .../core/datastream/source/JdbcSourceBuilder.java  |  92 +++++++---
 .../config/ContinuousUnBoundingSettings.java       |   1 +
 .../source/enumerator/JdbcSourceEnumerator.java    | 168 +++++++++---------
 .../enumerator/JdbcSqlSplitEnumeratorBase.java     |   8 +
 .../enumerator/SqlTemplateSplitEnumerator.java     |   9 +-
 .../splitter/JdbcSqlSplitterEnumerator.java        | 141 ++++++++++++++++
 .../splitter/PreparedSplitterEnumerator.java       | 135 +++++++++++++++
 .../PreparedSplitterNumericParameters.java}        |  70 ++------
 .../splitter/SlideTimingSplitterEnumerator.java    | 187 +++++++++++++++++++++
 .../enumerator/splitter/SplitterEnumerator.java    |  73 ++++++++
 .../enumerator/splitter/SqlSplitterEnumerator.java | 106 ++++++++++++
 .../flink/connector/jdbc/lineage/LineageUtils.java |   8 +-
 .../CompositeJdbcParameterValuesProvider.java      |   1 +
 .../split/JdbcGenericParameterValuesProvider.java  |   5 +
 .../JdbcNumericBetweenParametersProvider.java      |   6 +
 .../jdbc/split/JdbcParameterValuesProvider.java    |   4 +
 .../split/JdbcSlideTimingParameterProvider.java    |  11 +-
 .../datastream/source/JdbcSourceBuilderTest.java   |  48 ++++--
 .../source/JdbcSourceStreamRelatedITCase.java      |   1 +
 .../enumerator/JdbcSourceEnumeratorTest.java       |  43 ++++-
 .../PreparedSplitterNumericParametersTest.java     | 134 +++++++++++++++
 .../SlideTimingSplitterEnumeratorTest.java         | 142 ++++++++++++++++
 23 files changed, 1233 insertions(+), 222 deletions(-)

diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSource.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSource.java
index c861d676..427c9669 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSource.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSource.java
@@ -35,7 +35,8 @@ import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.JdbcSou
 import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.JdbcSourceEnumerator;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.JdbcSourceEnumeratorState;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.JdbcSqlSplitEnumeratorBase;
-import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.SqlTemplateSplitEnumerator;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.JdbcSqlSplitterEnumerator;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.SplitterEnumerator;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.reader.JdbcSourceReader;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.reader.JdbcSourceSplitReader;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.reader.extractor.ResultExtractor;
@@ -66,17 +67,16 @@ public class JdbcSource<OUT>
                 Source<OUT, JdbcSourceSplit, JdbcSourceEnumeratorState>,
                 ResultTypeQueryable<OUT> {
 
-    private final Boundedness boundedness;
     private final TypeInformation<OUT> typeInformation;
-    private final @Nullable ContinuousUnBoundingSettings 
continuousUnBoundingSettings;
 
     private final Configuration configuration;
-    private final JdbcSqlSplitEnumeratorBase.Provider<JdbcSourceSplit> 
sqlSplitEnumeratorProvider;
+    private final SplitterEnumerator splitterEnumerator;
 
     protected JdbcConnectionProvider connectionProvider;
     private final ResultExtractor<OUT> resultExtractor;
     private final DeliveryGuarantee deliveryGuarantee;
 
+    @Deprecated
     JdbcSource(
             Configuration configuration,
             JdbcConnectionProvider connectionProvider,
@@ -85,23 +85,35 @@ public class JdbcSource<OUT>
             TypeInformation<OUT> typeInformation,
             @Nullable DeliveryGuarantee deliveryGuarantee,
             @Nullable ContinuousUnBoundingSettings 
continuousUnBoundingSettings) {
+        this(
+                configuration,
+                connectionProvider,
+                new JdbcSqlSplitterEnumerator(
+                        sqlSplitEnumeratorProvider, 
continuousUnBoundingSettings),
+                resultExtractor,
+                typeInformation,
+                deliveryGuarantee);
+    }
+
+    JdbcSource(
+            Configuration configuration,
+            JdbcConnectionProvider connectionProvider,
+            SplitterEnumerator splitterEnumerator,
+            ResultExtractor<OUT> resultExtractor,
+            TypeInformation<OUT> typeInformation,
+            @Nullable DeliveryGuarantee deliveryGuarantee) {
         this.configuration = Preconditions.checkNotNull(configuration);
         this.connectionProvider = 
Preconditions.checkNotNull(connectionProvider);
-        this.sqlSplitEnumeratorProvider = 
Preconditions.checkNotNull(sqlSplitEnumeratorProvider);
+        this.splitterEnumerator = 
Preconditions.checkNotNull(splitterEnumerator);
         this.resultExtractor = Preconditions.checkNotNull(resultExtractor);
         this.deliveryGuarantee =
                 Objects.isNull(deliveryGuarantee) ? DeliveryGuarantee.NONE : 
deliveryGuarantee;
         this.typeInformation = Preconditions.checkNotNull(typeInformation);
-        this.continuousUnBoundingSettings = continuousUnBoundingSettings;
-        this.boundedness =
-                Objects.isNull(continuousUnBoundingSettings)
-                        ? Boundedness.BOUNDED
-                        : Boundedness.CONTINUOUS_UNBOUNDED;
     }
 
     @Override
     public Boundedness getBoundedness() {
-        return boundedness;
+        return splitterEnumerator.getBoundedness();
     }
 
     @Override
@@ -124,10 +136,7 @@ public class JdbcSource<OUT>
     public SplitEnumerator<JdbcSourceSplit, JdbcSourceEnumeratorState> 
createEnumerator(
             SplitEnumeratorContext<JdbcSourceSplit> enumContext) throws 
Exception {
         return new JdbcSourceEnumerator(
-                enumContext,
-                sqlSplitEnumeratorProvider.create(),
-                continuousUnBoundingSettings,
-                new ArrayList<>());
+                enumContext, splitterEnumerator, connectionProvider, new 
ArrayList<>());
     }
 
     @Override
@@ -139,8 +148,8 @@ public class JdbcSource<OUT>
                 checkpoint.getOptionalUserDefinedSplitEnumeratorState();
         return new JdbcSourceEnumerator(
                 enumContext,
-                
sqlSplitEnumeratorProvider.restore(optionalUserDefinedSplitEnumeratorState),
-                continuousUnBoundingSettings,
+                
splitterEnumerator.restoreState(optionalUserDefinedSplitEnumeratorState),
+                connectionProvider,
                 checkpoint.getRemainingSplits());
     }
 
@@ -167,8 +176,8 @@ public class JdbcSource<OUT>
     // ---- Visible for testing methods. ---
 
     @VisibleForTesting
-    public JdbcSqlSplitEnumeratorBase.Provider<JdbcSourceSplit> 
getSqlSplitEnumeratorProvider() {
-        return sqlSplitEnumeratorProvider;
+    public SplitterEnumerator getSplitterEnumerator() {
+        return splitterEnumerator;
     }
 
     @VisibleForTesting
@@ -201,27 +210,24 @@ public class JdbcSource<OUT>
             return false;
         }
         JdbcSource<?> that = (JdbcSource<?>) o;
-        return boundedness == that.boundedness
-                && Objects.equals(typeInformation, that.typeInformation)
+        return Objects.equals(typeInformation, that.typeInformation)
                 && Objects.equals(configuration, that.configuration)
-                && Objects.equals(sqlSplitEnumeratorProvider, 
that.sqlSplitEnumeratorProvider)
+                && Objects.equals(splitterEnumerator, that.splitterEnumerator)
                 && Objects.equals(connectionProvider, that.connectionProvider)
                 && Objects.equals(resultExtractor, that.resultExtractor)
-                && deliveryGuarantee == that.deliveryGuarantee
-                && Objects.equals(continuousUnBoundingSettings, 
that.continuousUnBoundingSettings);
+                && deliveryGuarantee == that.deliveryGuarantee;
     }
 
     @Override
     public LineageVertex getLineageVertex() {
         DefaultTypeDatasetFacet defaultTypeDatasetFacet =
                 new DefaultTypeDatasetFacet(getTypeInformation());
-        SqlTemplateSplitEnumerator enumerator =
-                (SqlTemplateSplitEnumerator) 
sqlSplitEnumeratorProvider.create();
-        Optional<String> nameOpt = 
LineageUtils.tableNameOf(enumerator.getSqlTemplate(), true);
+        Optional<String> nameOpt =
+                LineageUtils.tableNameOf(splitterEnumerator.lineageQueries(), 
true);
         String namespace = LineageUtils.namespaceOf(connectionProvider);
         LineageDataset dataset =
                 LineageUtils.datasetOf(
                         nameOpt.orElse(""), namespace, 
Arrays.asList(defaultTypeDatasetFacet));
-        return LineageUtils.sourceLineageVertexOf(boundedness, 
Collections.singleton(dataset));
+        return LineageUtils.sourceLineageVertexOf(getBoundedness(), 
Collections.singleton(dataset));
     }
 }
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceBuilder.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceBuilder.java
index 87b39f40..fbd171fe 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceBuilder.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceBuilder.java
@@ -24,7 +24,10 @@ import org.apache.flink.configuration.Configuration;
 import org.apache.flink.connector.base.DeliveryGuarantee;
 import org.apache.flink.connector.jdbc.JdbcConnectionOptions;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.config.ContinuousUnBoundingSettings;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.JdbcSqlSplitEnumeratorBase;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.SqlTemplateSplitEnumerator;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.JdbcSqlSplitterEnumerator;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.SplitterEnumerator;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.reader.extractor.ResultExtractor;
 import 
org.apache.flink.connector.jdbc.datasource.connections.JdbcConnectionProvider;
 import 
org.apache.flink.connector.jdbc.datasource.connections.SimpleJdbcConnectionProvider;
@@ -51,7 +54,7 @@ import java.util.Objects;
  *
  * <pre><code>
  * JdbcSource&lt;Row> source = JdbcSource.&lt;Row>builder()
- *           .setSql(validSql)
+ *           .setSplitter(PreparedSplitterEnumerator.of(validSql))
  *           .setResultExtractor(new RowResultExtractor())
  *           .setDBUrl(dbUrl)
  *           .setDriverName(driverName)
@@ -67,6 +70,7 @@ import java.util.Objects;
  *
  * <pre><code>
  *
+ * String query = "select * from books WHERE author = ?"
  * Serializable[][] queryParameters = new String[2][1];
  * queryParameters[0] = new String[]{"Kumar"};
  * queryParameters[1] = new String[]{"Tan Ah Teck"};
@@ -78,13 +82,12 @@ import java.util.Objects;
  *          .setUsername(username)
  *                     .setDriverName("org.apache.derby.jdbc.EmbeddedDriver")
  *                     .setDBUrl("jdbc:derby:memory:ebookshop")
- *                     .setSql("select * from books WHERE author = ?")
- *                     .setJdbcParameterValuesProvider(new 
JdbcGenericParameterValuesProvider(queryParameters))
+ *          .setSplitter(PreparedSplitterEnumerator.of(query, queryParameters))
  *          .build();
  * </code></pre>
  *
  * @see Row
- * @see JdbcParameterValuesProvider
+ * @see SplitterEnumerator
  * @see PreparedStatement
  * @see DriverManager
  * @see JdbcSource
@@ -118,7 +121,7 @@ public class JdbcSourceBuilder<OUT> {
     private JdbcParameterValuesProvider jdbcParameterValuesProvider;
     private @Nullable Serializable optionalSqlSplitEnumeratorState;
     private ResultExtractor<OUT> resultExtractor;
-
+    private SplitterEnumerator splitterEnumerator;
     private JdbcConnectionProvider connectionProvider;
 
     JdbcSourceBuilder() {
@@ -132,6 +135,13 @@ public class JdbcSourceBuilder<OUT> {
         this.autoCommit = true;
     }
 
+    public JdbcSourceBuilder<OUT> setSplitter(SplitterEnumerator 
splitterEnumerator) {
+        this.splitterEnumerator = splitterEnumerator;
+        return this;
+    }
+
+    /** @deprecated Use {@link #setSplitter(SplitterEnumerator)} instead. */
+    @Deprecated
     public JdbcSourceBuilder<OUT> setSql(@Nonnull String sql) {
         Preconditions.checkArgument(
                 !StringUtils.isNullOrWhitespaceOnly(sql),
@@ -196,7 +206,10 @@ public class JdbcSourceBuilder<OUT> {
     /**
      * If the value was set as an instance of {@link 
JdbcSlideTimingParameterProvider}, it's
      * required to specify the {@link #continuousUnBoundingSettings}.
+     *
+     * @deprecated Use {@link #setSplitter(SplitterEnumerator)} instead.
      */
+    @Deprecated
     public JdbcSourceBuilder<OUT> setJdbcParameterValuesProvider(
             @Nonnull JdbcParameterValuesProvider parameterValuesProvider) {
         this.jdbcParameterValuesProvider = 
Preconditions.checkNotNull(parameterValuesProvider);
@@ -289,36 +302,63 @@ public class JdbcSourceBuilder<OUT> {
                 JdbcSourceOptions.READER_FETCH_BATCH_SIZE, 
splitReaderFetchBatchSize);
         this.configuration.set(JdbcSourceOptions.AUTO_COMMIT, autoCommit);
 
-        Preconditions.checkState(
-                !StringUtils.isNullOrWhitespaceOnly(sql), "'sql' mustn't be 
null or empty.");
         Preconditions.checkNotNull(resultExtractor, "'resultExtractor' mustn't 
be null.");
         Preconditions.checkNotNull(typeInformation, "'typeInformation' mustn't 
be null.");
 
-        if (Objects.nonNull(continuousUnBoundingSettings)) {
-            Preconditions.checkArgument(
-                    Objects.nonNull(jdbcParameterValuesProvider)
-                            && jdbcParameterValuesProvider
-                                    instanceof 
JdbcSlideTimingParameterProvider,
-                    INVALID_SLIDE_TIMING_CONTINUOUS_HINT);
-        }
-
-        if (Objects.nonNull(jdbcParameterValuesProvider)
-                && jdbcParameterValuesProvider instanceof 
JdbcSlideTimingParameterProvider) {
-            Preconditions.checkArgument(
-                    Objects.nonNull(continuousUnBoundingSettings),
-                    INVALID_CONTINUOUS_SLIDE_TIMING_HINT);
+        Preconditions.checkArgument(
+                !(this.splitterEnumerator == null && sql == null),
+                "No splitter or query defined, one must be defined");
+        Preconditions.checkArgument(
+                !(this.splitterEnumerator != null && sql != null),
+                "Splitter and query defined, use only one of them");
+
+        if (this.splitterEnumerator == null) {
+            Preconditions.checkState(
+                    !StringUtils.isNullOrWhitespaceOnly(sql), "'sql' mustn't 
be null or empty.");
+
+            if (Objects.nonNull(continuousUnBoundingSettings)) {
+                Preconditions.checkArgument(
+                        Objects.nonNull(jdbcParameterValuesProvider)
+                                && jdbcParameterValuesProvider
+                                        instanceof 
JdbcSlideTimingParameterProvider,
+                        INVALID_SLIDE_TIMING_CONTINUOUS_HINT);
+            }
+
+            if (Objects.nonNull(jdbcParameterValuesProvider)
+                    && jdbcParameterValuesProvider instanceof 
JdbcSlideTimingParameterProvider) {
+                Preconditions.checkArgument(
+                        Objects.nonNull(continuousUnBoundingSettings),
+                        INVALID_CONTINUOUS_SLIDE_TIMING_HINT);
+            }
+
+            this.splitterEnumerator =
+                    getSplitter(
+                            sql,
+                            jdbcParameterValuesProvider,
+                            optionalSqlSplitEnumeratorState,
+                            continuousUnBoundingSettings);
         }
 
         return new JdbcSource<>(
                 configuration,
                 connectionProvider,
-                new 
SqlTemplateSplitEnumerator.TemplateSqlSplitEnumeratorProvider()
-                        
.setOptionalSqlSplitEnumeratorState(optionalSqlSplitEnumeratorState)
-                        .setSqlTemplate(sql)
-                        
.setParameterValuesProvider(jdbcParameterValuesProvider),
+                splitterEnumerator,
                 resultExtractor,
                 typeInformation,
-                deliveryGuarantee,
-                continuousUnBoundingSettings);
+                deliveryGuarantee);
+    }
+
+    private SplitterEnumerator getSplitter(
+            String sqlTemplate,
+            JdbcParameterValuesProvider parameterProvider,
+            Serializable userDefinedState,
+            @Nullable ContinuousUnBoundingSettings continuousSettings) {
+        JdbcSqlSplitEnumeratorBase.Provider<?> provider =
+                new 
SqlTemplateSplitEnumerator.TemplateSqlSplitEnumeratorProvider()
+                        .setOptionalSqlSplitEnumeratorState(userDefinedState)
+                        .setSqlTemplate(sqlTemplate)
+                        .setParameterValuesProvider(parameterProvider);
+
+        return new JdbcSqlSplitterEnumerator(provider, continuousSettings);
     }
 }
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/config/ContinuousUnBoundingSettings.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/config/ContinuousUnBoundingSettings.java
index da4a8d92..e0a4c1a7 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/config/ContinuousUnBoundingSettings.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/config/ContinuousUnBoundingSettings.java
@@ -31,6 +31,7 @@ import static 
org.apache.flink.util.Preconditions.checkNotNull;
  * continuous discovery and streaming mode.
  */
 @PublicEvolving
+@Deprecated
 public class ContinuousUnBoundingSettings implements Serializable {
 
     private static final long serialVersionUID = 1L;
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSourceEnumerator.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSourceEnumerator.java
index c545a08f..d371fd9f 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSourceEnumerator.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSourceEnumerator.java
@@ -18,12 +18,12 @@
 
 package org.apache.flink.connector.jdbc.core.datastream.source.enumerator;
 
-import org.apache.flink.api.connector.source.Boundedness;
 import org.apache.flink.api.connector.source.SourceEvent;
 import org.apache.flink.api.connector.source.SplitEnumerator;
 import org.apache.flink.api.connector.source.SplitEnumeratorContext;
-import 
org.apache.flink.connector.jdbc.core.datastream.source.config.ContinuousUnBoundingSettings;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.SplitterEnumerator;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.split.JdbcSourceSplit;
+import 
org.apache.flink.connector.jdbc.datasource.connections.JdbcConnectionProvider;
 import org.apache.flink.util.Preconditions;
 
 import org.slf4j.Logger;
@@ -35,11 +35,9 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.Objects;
 import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /** JDBC source enumerator. */
 public class JdbcSourceEnumerator
@@ -47,50 +45,32 @@ public class JdbcSourceEnumerator
     private static final Logger LOG = 
LoggerFactory.getLogger(JdbcSourceEnumerator.class);
 
     private final SplitEnumeratorContext<JdbcSourceSplit> context;
-    private final Boundedness boundedness;
-    private final LinkedHashMap<Integer, String> readersAwaitingSplit;
     private final List<JdbcSourceSplit> unassigned;
-    private final JdbcSqlSplitEnumeratorBase<JdbcSourceSplit> 
sqlSplitEnumerator;
-    private final @Nullable ContinuousUnBoundingSettings 
continuousUnBoundingSettings;
+    private final SplitterEnumerator splitterEnumerator;
+    private final JdbcConnectionProvider connectionProvider;
+    private final List<Integer> readersWaitingForSplits = new ArrayList<>();
+    private final AtomicInteger asyncCallsPending = new AtomicInteger(0);
 
     public JdbcSourceEnumerator(
             SplitEnumeratorContext<JdbcSourceSplit> context,
-            JdbcSqlSplitEnumeratorBase<JdbcSourceSplit> sqlSplitEnumerator,
-            ContinuousUnBoundingSettings continuousUnBoundingSettings,
+            SplitterEnumerator splitterEnumerator,
+            JdbcConnectionProvider connectionProvider,
             List<JdbcSourceSplit> unassigned) {
         this.context = Preconditions.checkNotNull(context);
-        this.sqlSplitEnumerator = 
Preconditions.checkNotNull(sqlSplitEnumerator);
-        this.continuousUnBoundingSettings = continuousUnBoundingSettings;
-        this.boundedness =
-                Objects.isNull(continuousUnBoundingSettings)
-                        ? Boundedness.BOUNDED
-                        : Boundedness.CONTINUOUS_UNBOUNDED;
+        this.splitterEnumerator = 
Preconditions.checkNotNull(splitterEnumerator);
         this.unassigned = Preconditions.checkNotNull(unassigned);
-        this.readersAwaitingSplit = new LinkedHashMap<>();
+        this.connectionProvider = connectionProvider;
     }
 
     @Override
     public void start() {
-        sqlSplitEnumerator.open();
-        if (boundedness == Boundedness.CONTINUOUS_UNBOUNDED
-                && Objects.nonNull(continuousUnBoundingSettings)) {
-            context.callAsync(
-                    () -> sqlSplitEnumerator.enumerateSplits(() -> 1024 - 
unassigned.size() > 0),
-                    this::processNewSplits,
-                    
continuousUnBoundingSettings.getInitialDiscoveryDelay().toMillis(),
-                    
continuousUnBoundingSettings.getDiscoveryInterval().toMillis());
-        } else {
-            try {
-                unassigned.addAll(sqlSplitEnumerator.enumerateSplits(() -> 
true));
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
-        }
+        splitterEnumerator.start(connectionProvider);
+        preDiscoverSplits();
     }
 
     @Override
     public void close() throws IOException {
-        sqlSplitEnumerator.close();
+        splitterEnumerator.close();
     }
 
     @Override
@@ -100,11 +80,20 @@ public class JdbcSourceEnumerator
 
     @Override
     public void handleSplitRequest(int subtask, @Nullable String hostname) {
-        if (boundedness == Boundedness.BOUNDED) {
-            assignSplitsForBounded(subtask, hostname);
+        if (!context.registeredReaders().containsKey(subtask)) {
+            LOG.warn("Ignoring split request from unregistered reader {}", 
subtask);
+            return;
+        }
+        final Optional<JdbcSourceSplit> nextSplit = getNextSplit();
+        if (nextSplit.isPresent()) {
+            context.assignSplit(nextSplit.get(), subtask);
+            LOG.info("Assigned split to subtask {} : {}", subtask, 
nextSplit.get());
+            preDiscoverSplits();
         } else {
-            readersAwaitingSplit.put(subtask, hostname);
-            assignSplitsForUnbounded();
+            if (!readersWaitingForSplits.contains(subtask)) {
+                readersWaitingForSplits.add(subtask);
+            }
+            preDiscoverSplits();
         }
     }
 
@@ -115,18 +104,13 @@ public class JdbcSourceEnumerator
 
     @Override
     public void addSplitsBack(List<JdbcSourceSplit> splits, int subtaskId) {
-        LOG.debug("File Source Enumerator adds splits back: {}", splits);
+        LOG.debug("Source Enumerator adds splits back: {}", splits);
         unassigned.addAll(splits);
-        if (boundedness == Boundedness.BOUNDED) {
-            context.registeredReaders()
-                    .keySet()
-                    .forEach(subTask -> assignSplitsForBounded(subTask, null));
-        } else if (boundedness == Boundedness.CONTINUOUS_UNBOUNDED) {
-            if (context.registeredReaders().containsKey(subtaskId)) {
-                readersAwaitingSplit.put(subtaskId, null);
-            }
-            assignSplitsForUnbounded();
+        if (context.registeredReaders().containsKey(subtaskId)
+                && !readersWaitingForSplits.contains(subtaskId)) {
+            readersWaitingForSplits.add(subtaskId);
         }
+        preDiscoverSplits();
     }
 
     @Override
@@ -136,7 +120,7 @@ public class JdbcSourceEnumerator
                 Collections.emptyList(),
                 Collections.emptyList(),
                 new ArrayList<>(unassigned),
-                sqlSplitEnumerator.optionalSqlSplitEnumeratorState);
+                splitterEnumerator.serializableState());
     }
 
     private Optional<JdbcSourceSplit> getNextSplit() {
@@ -149,58 +133,64 @@ public class JdbcSourceEnumerator
         return Optional.of(next);
     }
 
-    private void assignSplitsForBounded(int subtask, @Nullable String 
hostname) {
-        if (!context.registeredReaders().containsKey(subtask)) {
-            return;
-        }
-        if (LOG.isInfoEnabled()) {
-            final String hostInfo =
-                    hostname == null ? "(no host locality info)" : "(on host 
'" + hostname + "')";
-            LOG.info("Subtask {} {} is requesting a Jdbc source split", 
subtask, hostInfo);
-        }
-        final Optional<JdbcSourceSplit> nextSplit = getNextSplit();
-        if (nextSplit.isPresent()) {
-            final JdbcSourceSplit split = nextSplit.get();
-            context.assignSplit(split, subtask);
-            LOG.info("Assigned split to subtask {} : {}", subtask, split);
-        } else {
-            context.signalNoMoreSplits(subtask);
-            LOG.info("No more splits available for subtask {}", subtask);
+    private void preDiscoverSplits() {
+        int targetParallelism = context.currentParallelism();
+        while (asyncCallsPending.get() < targetParallelism
+                && !splitterEnumerator.isAllSplitsFinished()) {
+            asyncCallsPending.incrementAndGet();
+            context.callAsync(() -> splitterEnumerator.enumerateSplits(), 
this::onSplitsDiscovered);
         }
+
+        signalNoMoreSplitsIfDone();
     }
 
-    private void processNewSplits(List<JdbcSourceSplit> splits, Throwable 
error) {
+    private void onSplitsDiscovered(List<JdbcSourceSplit> splits, Throwable 
error) {
+        asyncCallsPending.decrementAndGet();
         if (error != null) {
-            LOG.error("Failed to enumerate sql splits.", error);
+            LOG.error("Failed to discover splits.", error);
+            preDiscoverSplits();
             return;
         }
-        this.unassigned.addAll(splits);
 
-        assignSplitsForUnbounded();
+        if (splits != null && !splits.isEmpty()) {
+            assignOrBuffer(splits);
+            preDiscoverSplits();
+        } else if (!splitterEnumerator.isAllSplitsFinished()) {
+            preDiscoverSplits();
+        } else {
+            signalNoMoreSplitsIfDone();
+        }
     }
 
-    private void assignSplitsForUnbounded() {
-        final Iterator<Map.Entry<Integer, String>> awaitingReader =
-                readersAwaitingSplit.entrySet().iterator();
-
-        while (awaitingReader.hasNext()) {
-            final Map.Entry<Integer, String> nextAwaiting = 
awaitingReader.next();
-
-            // if the reader that requested another split has failed in the 
meantime, remove
-            // it from the list of waiting readers
-            if 
(!context.registeredReaders().containsKey(nextAwaiting.getKey())) {
-                awaitingReader.remove();
-                continue;
+    private void assignOrBuffer(List<JdbcSourceSplit> splits) {
+        for (JdbcSourceSplit split : splits) {
+            if (!readersWaitingForSplits.isEmpty()) {
+                int subtaskId = readersWaitingForSplits.remove(0);
+                if (context.registeredReaders().containsKey(subtaskId)) {
+                    LOG.info(
+                            "Assigning discovered split {} to waiting subtask 
{}",
+                            split,
+                            subtaskId);
+                    context.assignSplit(split, subtaskId);
+                } else {
+                    unassigned.add(split);
+                }
+            } else {
+                unassigned.add(split);
             }
+        }
+    }
 
-            final int awaitingSubtask = nextAwaiting.getKey();
-            final Optional<JdbcSourceSplit> nextSplit = getNextSplit();
-            if (nextSplit.isPresent()) {
-                context.assignSplit(nextSplit.get(), awaitingSubtask);
-                awaitingReader.remove();
-            } else {
-                break;
+    private void signalNoMoreSplitsIfDone() {
+        if (asyncCallsPending.get() == 0
+                && splitterEnumerator.isAllSplitsFinished()
+                && unassigned.isEmpty()
+                && !readersWaitingForSplits.isEmpty()) {
+            for (int subtaskId : readersWaitingForSplits) {
+                context.signalNoMoreSplits(subtaskId);
+                LOG.info("No more splits available for subtask {}", subtaskId);
             }
+            readersWaitingForSplits.clear();
         }
     }
 }
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSqlSplitEnumeratorBase.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSqlSplitEnumeratorBase.java
index dd3eb34b..c47b7cef 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSqlSplitEnumeratorBase.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSqlSplitEnumeratorBase.java
@@ -19,6 +19,7 @@
 package org.apache.flink.connector.jdbc.core.datastream.source.enumerator;
 
 import org.apache.flink.annotation.PublicEvolving;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.SplitterEnumerator;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.split.JdbcSourceSplit;
 
 import javax.annotation.Nonnull;
@@ -33,7 +34,9 @@ import java.util.function.Supplier;
  * Base class for jdbc sql split enumerator.
  *
  * @param <SplitT> JDBC split type.
+ * @deprecated Use {@link SplitterEnumerator} instead.
  */
+@Deprecated
 @PublicEvolving
 public abstract class JdbcSqlSplitEnumeratorBase<SplitT> implements 
AutoCloseable, Serializable {
     private final char[] currentId = "0000000000".toCharArray();
@@ -44,6 +47,10 @@ public abstract class JdbcSqlSplitEnumeratorBase<SplitT> 
implements AutoCloseabl
         this.optionalSqlSplitEnumeratorState = optionalSqlSplitEnumeratorState;
     }
 
+    public Serializable enumeratorState() {
+        return this.optionalSqlSplitEnumeratorState;
+    }
+
     protected final String getNextId() {
         // because we just increment numbers, we increment the char 
representation directly,
         // rather than incrementing an integer and converting it to a string 
representation
@@ -78,6 +85,7 @@ public abstract class JdbcSqlSplitEnumeratorBase<SplitT> 
implements AutoCloseabl
      *
      * @param <SplitT> Split type.
      */
+    @Deprecated
     @PublicEvolving
     public interface Provider<SplitT> extends Serializable {
 
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/SqlTemplateSplitEnumerator.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/SqlTemplateSplitEnumerator.java
index b4565d95..3a16b376 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/SqlTemplateSplitEnumerator.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/SqlTemplateSplitEnumerator.java
@@ -19,6 +19,7 @@
 package org.apache.flink.connector.jdbc.core.datastream.source.enumerator;
 
 import org.apache.flink.annotation.VisibleForTesting;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.PreparedSplitterEnumerator;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.split.CheckpointedOffset;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.split.JdbcSourceSplit;
 import org.apache.flink.connector.jdbc.split.JdbcParameterValuesProvider;
@@ -37,7 +38,12 @@ import java.util.Collections;
 import java.util.List;
 import java.util.function.Supplier;
 
-/** A split enumerator based on sql-parameters grains. */
+/**
+ * A split enumerator based on sql-parameters grains.
+ *
+ * @deprecated Use {@link PreparedSplitterEnumerator} instead.
+ */
+@Deprecated
 public final class SqlTemplateSplitEnumerator extends 
JdbcSqlSplitEnumeratorBase<JdbcSourceSplit> {
 
     public static final Logger LOG = 
LoggerFactory.getLogger(SqlTemplateSplitEnumerator.class);
@@ -105,6 +111,7 @@ public final class SqlTemplateSplitEnumerator extends 
JdbcSqlSplitEnumeratorBase
     }
 
     /** The {@link TemplateSqlSplitEnumeratorProvider} for {@link 
SqlTemplateSplitEnumerator}. */
+    @Deprecated
     public static class TemplateSqlSplitEnumeratorProvider
             implements JdbcSqlSplitEnumeratorBase.Provider<JdbcSourceSplit> {
 
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/JdbcSqlSplitterEnumerator.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/JdbcSqlSplitterEnumerator.java
new file mode 100644
index 00000000..30bba69f
--- /dev/null
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/JdbcSqlSplitterEnumerator.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter;
+
+import org.apache.flink.annotation.Internal;
+import org.apache.flink.api.connector.source.Boundedness;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.config.ContinuousUnBoundingSettings;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.JdbcSqlSplitEnumeratorBase;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.SqlTemplateSplitEnumerator;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.split.JdbcSourceSplit;
+import 
org.apache.flink.connector.jdbc.datasource.connections.JdbcConnectionProvider;
+
+import javax.annotation.Nullable;
+
+import java.io.Serializable;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A splitter enumerator for JdbcSqlSplitEnumeratorBase.
+ *
+ * <p>This class is used to allow the retrofit of existing implementations of
+ * JdbcSqlSplitEnumeratorBase to the new SplitterEnumerator interface. It 
delegates the split
+ * enumeration to the underlying JdbcSqlSplitEnumeratorBase instance.
+ */
+@Deprecated
+@Internal
+public class JdbcSqlSplitterEnumerator implements SplitterEnumerator {
+    private final JdbcSqlSplitEnumeratorBase.Provider<?> provider;
+    private final Boundedness boundedness;
+    private final @Nullable ContinuousUnBoundingSettings 
continuousUnBoundingSettings;
+    private JdbcSqlSplitEnumeratorBase<?> base;
+    private volatile boolean finished;
+    private volatile boolean initialDelayApplied;
+
+    public JdbcSqlSplitterEnumerator(
+            JdbcSqlSplitEnumeratorBase.Provider<?> provider,
+            @Nullable ContinuousUnBoundingSettings 
continuousUnBoundingSettings) {
+        this.provider = provider;
+        this.base = provider.create();
+        this.finished = false;
+        this.boundedness =
+                Objects.nonNull(continuousUnBoundingSettings)
+                        ? Boundedness.CONTINUOUS_UNBOUNDED
+                        : Boundedness.BOUNDED;
+        this.continuousUnBoundingSettings = continuousUnBoundingSettings;
+    }
+
+    @Override
+    public Boundedness getBoundedness() {
+        return boundedness;
+    }
+
+    @Override
+    public void start(JdbcConnectionProvider connectionProvider) {
+        base.open();
+    }
+
+    @Override
+    public void close() {
+        base.close();
+    }
+
+    @Override
+    public synchronized boolean isAllSplitsFinished() {
+        if (boundedness == Boundedness.CONTINUOUS_UNBOUNDED) {
+            return false;
+        }
+        return this.finished;
+    }
+
+    @Override
+    public synchronized List<JdbcSourceSplit> enumerateSplits() {
+        try {
+            if (finished) {
+                return new ArrayList<>();
+            }
+            if (boundedness == Boundedness.CONTINUOUS_UNBOUNDED && 
!initialDelayApplied) {
+                initialDelayApplied = true;
+                Duration delayMs = 
continuousUnBoundingSettings.getInitialDiscoveryDelay();
+                if (delayMs != null) {
+                    Thread.sleep(delayMs.toMillis());
+                }
+            }
+            List<JdbcSourceSplit> splits = base.enumerateSplits(() -> true);
+            if (boundedness == Boundedness.BOUNDED) {
+                this.finished = true;
+            }
+            if (boundedness == Boundedness.CONTINUOUS_UNBOUNDED && 
splits.isEmpty()) {
+                Duration throttleMs = 
continuousUnBoundingSettings.getDiscoveryInterval();
+                if (throttleMs != null) {
+                    Thread.sleep(throttleMs.toMillis());
+                }
+            }
+            return splits;
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            return new ArrayList<>();
+        } catch (Exception e) {
+            throw new RuntimeException("Error enumerating splits", e);
+        }
+    }
+
+    @Override
+    public List<String> lineageQueries() {
+        List<String> queries = new ArrayList<>();
+        if (base instanceof SqlTemplateSplitEnumerator) {
+            queries.add(((SqlTemplateSplitEnumerator) base).getSqlTemplate());
+        }
+        return queries;
+    }
+
+    @Override
+    public Serializable serializableState() {
+        return base.enumeratorState();
+    }
+
+    @Override
+    public SplitterEnumerator restoreState(Serializable state) {
+        this.base = provider.restore(state);
+        return this;
+    }
+}
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/PreparedSplitterEnumerator.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/PreparedSplitterEnumerator.java
new file mode 100644
index 00000000..e0340381
--- /dev/null
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/PreparedSplitterEnumerator.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter;
+
+import org.apache.flink.annotation.PublicEvolving;
+import org.apache.flink.annotation.VisibleForTesting;
+import org.apache.flink.api.connector.source.Boundedness;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.split.JdbcSourceSplit;
+import 
org.apache.flink.connector.jdbc.datasource.connections.JdbcConnectionProvider;
+import org.apache.flink.util.Preconditions;
+
+import javax.annotation.Nullable;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/** A split enumerator based on sql-parameters grains. */
+@PublicEvolving
+public class PreparedSplitterEnumerator extends SqlSplitterEnumerator {
+
+    private final Serializable[][] sqlParameters;
+    private volatile boolean finished;
+
+    protected PreparedSplitterEnumerator(String sqlTemplate, Serializable[][] 
sqlParameters) {
+        super(Preconditions.checkNotNull(sqlTemplate));
+        this.sqlParameters = Preconditions.checkNotNull(sqlParameters);
+        this.finished = false;
+    }
+
+    public static PreparedSplitterEnumerator of(
+            String sqlTemplate, Serializable[][] sqlParameters) {
+        return new PreparedSplitterEnumerator(sqlTemplate, sqlParameters);
+    }
+
+    public static PreparedSplitterEnumerator of(String sqlTemplate) {
+        return new PreparedSplitterEnumerator(sqlTemplate, new 
Serializable[0][]);
+    }
+
+    public static PreparedSplitterEnumerator of(
+            String sqlTemplate, long minValue, long maxValue, long batchSize) {
+        PreparedSplitterNumericParameters parameters =
+                new PreparedSplitterNumericParameters(minValue, 
maxValue).withBatchSize(batchSize);
+        return of(sqlTemplate, parameters);
+    }
+
+    public static PreparedSplitterEnumerator of(
+            String sqlTemplate, long minValue, long maxValue, int batchNum) {
+        PreparedSplitterNumericParameters parameters =
+                new PreparedSplitterNumericParameters(minValue, 
maxValue).withBatchNum(batchNum);
+        return of(sqlTemplate, parameters);
+    }
+
+    public static PreparedSplitterEnumerator of(
+            String sqlTemplate, PreparedSplitterNumericParameters 
numericParameters) {
+        Serializable[][] parameters = numericParameters.getParameterValues();
+        return new PreparedSplitterEnumerator(sqlTemplate, parameters);
+    }
+
+    @Override
+    public Boundedness getBoundedness() {
+        return Boundedness.BOUNDED;
+    }
+
+    @Override
+    public void start(JdbcConnectionProvider connectionProvider) {}
+
+    @Override
+    public void close() {}
+
+    @Override
+    public synchronized boolean isAllSplitsFinished() {
+        return this.finished;
+    }
+
+    @VisibleForTesting
+    public Serializable[][] getSqlParameters() {
+        return sqlParameters;
+    }
+
+    @Override
+    public synchronized List<JdbcSourceSplit> enumerateSplits() {
+        if (finished) {
+            return new ArrayList<>();
+        }
+        List<JdbcSourceSplit> splitList = super.enumerateSplits();
+        this.finished = true;
+        return splitList;
+    }
+
+    @Override
+    public @Nullable Serializable serializableState() {
+        return null;
+    }
+
+    @Override
+    public PreparedSplitterEnumerator restoreState(Serializable state) {
+        return this;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof PreparedSplitterEnumerator)) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+        PreparedSplitterEnumerator that = (PreparedSplitterEnumerator) o;
+        return Objects.deepEquals(sqlParameters, that.sqlParameters);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), 
Arrays.deepHashCode(sqlParameters));
+    }
+}
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcNumericBetweenParametersProvider.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/PreparedSplitterNumericParameters.java
similarity index 51%
copy from 
flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcNumericBetweenParametersProvider.java
copy to 
flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/PreparedSplitterNumericParameters.java
index 617e6c52..c48763c3 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcNumericBetweenParametersProvider.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/PreparedSplitterNumericParameters.java
@@ -16,62 +16,34 @@
  * limitations under the License.
  */
 
-package org.apache.flink.connector.jdbc.split;
+package 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter;
 
+import org.apache.flink.annotation.Internal;
 import org.apache.flink.util.Preconditions;
 
 import java.io.Serializable;
 
 /**
- * This query parameters generator is an helper class to parameterize from/to 
queries on a numeric
- * column. The generated array of from/to values will be equally sized to 
fetchSize (apart from the
+ * This query parameters generator is a helper class to parameterize from/to 
queries on a numeric
+ * column. The generated array of from/to values will be equally sized to 
batchSize (apart from the
  * last one), ranging from minVal up to maxVal.
- *
- * <p>For example, if there's a table <CODE>BOOKS</CODE> with a numeric PK 
<CODE>id</CODE>, using a
- * query like:
- *
- * <PRE>
- * SELECT * FROM BOOKS WHERE id BETWEEN ? AND ?
- * </PRE>
- *
- * <p>You can take advantage of this class to automatically generate the 
parameters of the BETWEEN
- * clause, based on the passed constructor parameters.
  */
-public class JdbcNumericBetweenParametersProvider implements 
JdbcParameterValuesProvider {
+@Internal
+public class PreparedSplitterNumericParameters implements Serializable {
 
     private final long minVal;
     private final long maxVal;
-
     private long batchSize;
     private int batchNum;
 
-    /**
-     * NumericBetweenParametersProviderJdbc constructor.
-     *
-     * @param minVal the lower bound of the produced "from" values
-     * @param maxVal the upper bound of the produced "to" values
-     */
-    public JdbcNumericBetweenParametersProvider(long minVal, long maxVal) {
-        Preconditions.checkArgument(minVal <= maxVal, "minVal must not be 
larger than maxVal");
-        this.minVal = minVal;
-        this.maxVal = maxVal;
-    }
-
-    /**
-     * NumericBetweenParametersProviderJdbc constructor.
-     *
-     * @param fetchSize the max distance between the produced from/to pairs
-     * @param minVal the lower bound of the produced "from" values
-     * @param maxVal the upper bound of the produced "to" values
-     */
-    public JdbcNumericBetweenParametersProvider(long fetchSize, long minVal, 
long maxVal) {
-        Preconditions.checkArgument(minVal <= maxVal, "minVal must not be 
larger than maxVal");
+    public PreparedSplitterNumericParameters(long minVal, long maxVal) {
         this.minVal = minVal;
         this.maxVal = maxVal;
-        ofBatchSize(fetchSize);
+        this.batchNum = 0;
+        this.batchSize = 0;
     }
 
-    public JdbcNumericBetweenParametersProvider ofBatchSize(long batchSize) {
+    public PreparedSplitterNumericParameters withBatchSize(long batchSize) {
         Preconditions.checkArgument(batchSize > 0, "Batch size must be 
positive");
 
         long maxElemCount = (maxVal - minVal) + 1;
@@ -83,7 +55,7 @@ public class JdbcNumericBetweenParametersProvider implements 
JdbcParameterValues
         return this;
     }
 
-    public JdbcNumericBetweenParametersProvider ofBatchNum(int batchNum) {
+    public PreparedSplitterNumericParameters withBatchNum(int batchNum) {
         Preconditions.checkArgument(batchNum > 0, "Batch number must be 
positive");
 
         long maxElemCount = (maxVal - minVal) + 1;
@@ -91,15 +63,14 @@ public class JdbcNumericBetweenParametersProvider 
implements JdbcParameterValues
             batchNum = (int) maxElemCount;
         }
         this.batchNum = batchNum;
-        this.batchSize = new Double(Math.ceil((double) maxElemCount / 
batchNum)).longValue();
+        this.batchSize = (maxElemCount + batchNum - 1) / batchNum;
         return this;
     }
 
-    @Override
     public Serializable[][] getParameterValues() {
         Preconditions.checkState(
                 batchSize > 0,
-                "Batch size and batch number must be positive. Have you called 
`ofBatchSize` or `ofBatchNum`?");
+                "Batch size and batch number must be positive. Have you called 
`withBatchSize` or `withBatchNum`?");
 
         long maxElemCount = (maxVal - minVal) + 1;
         long bigBatchNum = maxElemCount - (batchSize - 1) * batchNum;
@@ -113,19 +84,4 @@ public class JdbcNumericBetweenParametersProvider 
implements JdbcParameterValues
         }
         return parameters;
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        JdbcNumericBetweenParametersProvider that = 
(JdbcNumericBetweenParametersProvider) o;
-        return minVal == that.minVal
-                && maxVal == that.maxVal
-                && batchSize == that.batchSize
-                && batchNum == that.batchNum;
-    }
 }
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/SlideTimingSplitterEnumerator.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/SlideTimingSplitterEnumerator.java
new file mode 100644
index 00000000..3c1d9a42
--- /dev/null
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/SlideTimingSplitterEnumerator.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter;
+
+import org.apache.flink.annotation.PublicEvolving;
+import org.apache.flink.annotation.VisibleForTesting;
+import org.apache.flink.api.connector.source.Boundedness;
+import 
org.apache.flink.connector.jdbc.datasource.connections.JdbcConnectionProvider;
+import org.apache.flink.util.Preconditions;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/** A split enumerator based on sql-parameters grains. */
+@PublicEvolving
+public class SlideTimingSplitterEnumerator extends SqlSplitterEnumerator {
+
+    private final long slideStepMillis;
+    private final long slideSpanMillis;
+    private final long splitGenerateDelayMillis;
+
+    private @Nonnull Long startMillis;
+
+    protected SlideTimingSplitterEnumerator(
+            String sqlTemplate,
+            Long startMillis,
+            long slideSpanMillis,
+            long slideStepMillis,
+            long splitGenerateDelayMillis) {
+        super(sqlTemplate);
+        this.startMillis = Preconditions.checkNotNull(startMillis);
+        this.slideStepMillis = slideStepMillis;
+        this.slideSpanMillis = slideSpanMillis;
+        this.splitGenerateDelayMillis = splitGenerateDelayMillis;
+    }
+
+    @Override
+    public Boundedness getBoundedness() {
+        return Boundedness.CONTINUOUS_UNBOUNDED;
+    }
+
+    @Override
+    public void start(JdbcConnectionProvider connectionProvider) {}
+
+    @Override
+    public void close() {}
+
+    @Override
+    public boolean isAllSplitsFinished() {
+        return false;
+    }
+
+    @VisibleForTesting
+    public Serializable[][] getSqlParameters() {
+        List<Serializable[]> tmpList = new ArrayList<>();
+        while (nextSplitAvailable(startMillis)) {
+            Serializable[] params = new Serializable[] {startMillis, 
startMillis + slideSpanMillis};
+            tmpList.add(params);
+            startMillis += slideStepMillis;
+        }
+        return tmpList.toArray(new Serializable[0][]);
+    }
+
+    @Override
+    public @Nullable Serializable serializableState() {
+        return this.startMillis;
+    }
+
+    @Override
+    public SlideTimingSplitterEnumerator restoreState(Serializable state) {
+        Preconditions.checkArgument((Long) state > 0L);
+        this.startMillis = (Long) state;
+        return this;
+    }
+
+    private boolean nextSplitAvailable(Long nextSpanStartMillis) {
+        final long delayedNextSpanStartMillis = nextSpanStartMillis + 
splitGenerateDelayMillis;
+        final long currentAvailableMillis = currentAvailableMillis();
+        return currentAvailableMillis >= delayedNextSpanStartMillis
+                && (currentAvailableMillis - delayedNextSpanStartMillis >= 
slideSpanMillis);
+    }
+
+    private Long currentAvailableMillis() {
+        return System.currentTimeMillis();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof SlideTimingSplitterEnumerator)) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+        SlideTimingSplitterEnumerator that = (SlideTimingSplitterEnumerator) o;
+        return slideStepMillis == that.slideStepMillis
+                && slideSpanMillis == that.slideSpanMillis
+                && splitGenerateDelayMillis == that.splitGenerateDelayMillis
+                && Objects.equals(startMillis, that.startMillis);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                super.hashCode(),
+                slideStepMillis,
+                slideSpanMillis,
+                splitGenerateDelayMillis,
+                startMillis);
+    }
+
+    public static SlideTimingSplitterEnumeratorBuilder builder() {
+        return new SlideTimingSplitterEnumeratorBuilder();
+    }
+
+    /** A slide split enumerator builder. */
+    public static class SlideTimingSplitterEnumeratorBuilder {
+        private String sqlTemplate;
+        private Long startMillis;
+        private Long slideSpanMillis;
+        private Long slideStepMillis;
+        private Long splitGenerateDelayMillis;
+
+        public SlideTimingSplitterEnumeratorBuilder setSqlTemplate(String 
sqlTemplate) {
+            this.sqlTemplate = sqlTemplate;
+            return this;
+        }
+
+        public SlideTimingSplitterEnumeratorBuilder setStartMillis(Long 
startMillis) {
+            this.startMillis = startMillis;
+            return this;
+        }
+
+        public SlideTimingSplitterEnumeratorBuilder setSlideSpanMillis(Long 
slideSpanMillis) {
+            this.slideSpanMillis = slideSpanMillis;
+            return this;
+        }
+
+        public SlideTimingSplitterEnumeratorBuilder setSlideStepMillis(Long 
slideStepMillis) {
+            this.slideStepMillis = slideStepMillis;
+            return this;
+        }
+
+        public SlideTimingSplitterEnumeratorBuilder 
setSplitGenerateDelayMillis(
+                Long splitGenerateDelayMillis) {
+            this.splitGenerateDelayMillis = splitGenerateDelayMillis;
+            return this;
+        }
+
+        public SlideTimingSplitterEnumerator build() {
+            Preconditions.checkArgument(startMillis > 0L, "'startMillis' must 
be greater than 0. ");
+            Preconditions.checkArgument(
+                    slideSpanMillis > 0 && slideStepMillis > 0,
+                    "parameters must satisfy slideSpanMillis > 0 and 
slideStepMillis > 0");
+            Preconditions.checkArgument(
+                    splitGenerateDelayMillis >= 0L,
+                    "parameters must satisfy splitGenerateDelayMillis >= 0");
+            return new SlideTimingSplitterEnumerator(
+                    sqlTemplate,
+                    startMillis,
+                    slideSpanMillis,
+                    slideStepMillis,
+                    splitGenerateDelayMillis);
+        }
+    }
+}
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/SplitterEnumerator.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/SplitterEnumerator.java
new file mode 100644
index 00000000..96f4947d
--- /dev/null
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/SplitterEnumerator.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter;
+
+import org.apache.flink.annotation.PublicEvolving;
+import org.apache.flink.api.connector.source.Boundedness;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.split.JdbcSourceSplit;
+import 
org.apache.flink.connector.jdbc.datasource.connections.JdbcConnectionProvider;
+
+import java.io.Serializable;
+import java.util.List;
+
+/** Interface for jdbc sql split enumerator. */
+@PublicEvolving
+public interface SplitterEnumerator extends AutoCloseable, Serializable {
+
+    /** Returns whether this enumerator produces a bounded or continuous 
unbounded stream. */
+    Boundedness getBoundedness();
+
+    /**
+     * Start the enumerator.
+     *
+     * @param connectionProvider The JDBC connection provider.
+     */
+    void start(JdbcConnectionProvider connectionProvider);
+
+    /** Close the enumerator. */
+    void close();
+
+    /** All splits have been enumerated. */
+    boolean isAllSplitsFinished();
+
+    /** Enumerate the JDBC splits. */
+    List<JdbcSourceSplit> enumerateSplits();
+
+    /**
+     * Get lineage queries for splits.
+     *
+     * @return The lineage queries.
+     */
+    List<String> lineageQueries();
+
+    /**
+     * Get the serializable state of the enumerator.
+     *
+     * @return The serializable state.
+     */
+    Serializable serializableState();
+
+    /**
+     * Restore the enumerator state from the given serializable state.
+     *
+     * @param state The serializable state.
+     * @return The restored SplitterEnumerator.
+     */
+    SplitterEnumerator restoreState(Serializable state);
+}
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/SqlSplitterEnumerator.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/SqlSplitterEnumerator.java
new file mode 100644
index 00000000..ef9c0db1
--- /dev/null
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/SqlSplitterEnumerator.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter;
+
+import org.apache.flink.annotation.VisibleForTesting;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.split.CheckpointedOffset;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.split.JdbcSourceSplit;
+import org.apache.flink.util.Preconditions;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/** A sql base template split enumerator. */
+public abstract class SqlSplitterEnumerator implements SplitterEnumerator {
+
+    private final char[] currentId = "0000000000".toCharArray();
+
+    private final String sqlTemplate;
+
+    protected SqlSplitterEnumerator(String sqlTemplate) {
+        this.sqlTemplate = Preconditions.checkNotNull(sqlTemplate);
+    }
+
+    @VisibleForTesting
+    protected abstract Serializable[][] getSqlParameters();
+
+    protected String getSqlTemplate() {
+        return this.sqlTemplate;
+    }
+
+    @Override
+    public List<JdbcSourceSplit> enumerateSplits() {
+        Serializable[][] params = getSqlParameters();
+        if (params == null || params.length == 0) {
+            return Collections.emptyList();
+        }
+        int paramLength = params.length;
+        List<JdbcSourceSplit> splitList = new ArrayList<>(paramLength);
+        for (Serializable[] paramArr : params) {
+            splitList.add(createSplit(paramArr));
+        }
+        return splitList;
+    }
+
+    @Override
+    public List<String> lineageQueries() {
+        return Collections.singletonList(getSqlTemplate());
+    }
+
+    protected JdbcSourceSplit createSplit(Serializable[] paramArr) {
+        return new JdbcSourceSplit(
+                getNextId(), getSqlTemplate(), paramArr, new 
CheckpointedOffset());
+    }
+
+    protected final String getNextId() {
+        // because we just increment numbers, we increment the char 
representation directly,
+        // rather than incrementing an integer and converting it to a string 
representation
+        // every time again (requires quite some expensive conversion logic).
+        incrementCharArrayByOne(currentId, currentId.length - 1);
+        return new String(currentId);
+    }
+
+    private static void incrementCharArrayByOne(char[] array, int pos) {
+        char c = array[pos];
+        c++;
+
+        if (c > '9') {
+            c = '0';
+            incrementCharArrayByOne(array, pos - 1);
+        }
+        array[pos] = c;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof SqlSplitterEnumerator)) {
+            return false;
+        }
+        SqlSplitterEnumerator that = (SqlSplitterEnumerator) o;
+        return Objects.equals(sqlTemplate, that.sqlTemplate);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(sqlTemplate);
+    }
+}
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/lineage/LineageUtils.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/lineage/LineageUtils.java
index 0a09671c..be5ea909 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/lineage/LineageUtils.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/lineage/LineageUtils.java
@@ -32,8 +32,8 @@ import io.openlineage.sql.DbTableMeta;
 import io.openlineage.sql.OpenLineageSql;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -56,7 +56,11 @@ public class LineageUtils {
     }
 
     public static Optional<String> tableNameOf(String query, boolean isSource) 
{
-        return OpenLineageSql.parse(Arrays.asList(query))
+        return tableNameOf(Collections.singletonList(query), isSource);
+    }
+
+    public static Optional<String> tableNameOf(List<String> queries, boolean 
isSource) {
+        return OpenLineageSql.parse(queries)
                 .map(
                         sqlMeta ->
                                 isSource
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/CompositeJdbcParameterValuesProvider.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/CompositeJdbcParameterValuesProvider.java
index eebeac5a..ded972ef 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/CompositeJdbcParameterValuesProvider.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/CompositeJdbcParameterValuesProvider.java
@@ -24,6 +24,7 @@ import org.apache.flink.util.Preconditions;
 import java.io.Serializable;
 
 /** Combine 2 {@link JdbcParameterValuesProvider} into 1. */
+@Deprecated
 @Internal
 public class CompositeJdbcParameterValuesProvider implements 
JdbcParameterValuesProvider {
     JdbcParameterValuesProvider a;
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcGenericParameterValuesProvider.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcGenericParameterValuesProvider.java
index d9637982..2d831231 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcGenericParameterValuesProvider.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcGenericParameterValuesProvider.java
@@ -20,13 +20,18 @@ package org.apache.flink.connector.jdbc.split;
 
 import org.apache.flink.annotation.PublicEvolving;
 import org.apache.flink.connector.jdbc.JdbcInputFormat;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.PreparedSplitterEnumerator;
 
 import java.io.Serializable;
 
 /**
  * This splits generator actually does nothing but wrapping the query 
parameters computed by the
  * user before creating the {@link JdbcInputFormat} instance.
+ *
+ * @deprecated Use {@link PreparedSplitterEnumerator} instead. As example:
+ *     PreparedSplitterEnumerator.of(query, parameters).
  */
+@Deprecated
 @PublicEvolving
 public class JdbcGenericParameterValuesProvider implements 
JdbcParameterValuesProvider {
 
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcNumericBetweenParametersProvider.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcNumericBetweenParametersProvider.java
index 617e6c52..c06a9b82 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcNumericBetweenParametersProvider.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcNumericBetweenParametersProvider.java
@@ -18,6 +18,7 @@
 
 package org.apache.flink.connector.jdbc.split;
 
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.PreparedSplitterEnumerator;
 import org.apache.flink.util.Preconditions;
 
 import java.io.Serializable;
@@ -36,7 +37,12 @@ import java.io.Serializable;
  *
  * <p>You can take advantage of this class to automatically generate the 
parameters of the BETWEEN
  * clause, based on the passed constructor parameters.
+ *
+ * @deprecated Use {@link PreparedSplitterEnumerator} instead. As example:
+ *     PreparedSplitterEnumerator.of(query, minValue, maxValue, batchSize) or
+ *     PreparedSplitterEnumerator.of(query, minValue, maxValue, batchNum).
  */
+@Deprecated
 public class JdbcNumericBetweenParametersProvider implements 
JdbcParameterValuesProvider {
 
     private final long minVal;
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcParameterValuesProvider.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcParameterValuesProvider.java
index 3f1c2aed..bb95829e 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcParameterValuesProvider.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcParameterValuesProvider.java
@@ -20,6 +20,7 @@ package org.apache.flink.connector.jdbc.split;
 
 import org.apache.flink.annotation.PublicEvolving;
 import org.apache.flink.connector.jdbc.JdbcInputFormat;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.SplitterEnumerator;
 
 import java.io.Serializable;
 
@@ -27,7 +28,10 @@ import java.io.Serializable;
  * This interface is used by the {@link JdbcInputFormat} to compute the list 
of parallel query to
  * run (i.e. splits). Each query will be parameterized using a row of the 
matrix provided by each
  * {@link JdbcParameterValuesProvider} implementation.
+ *
+ * @deprecated Use {@link SplitterEnumerator} instead.
  */
+@Deprecated
 @PublicEvolving
 public interface JdbcParameterValuesProvider extends Serializable {
 
diff --git 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcSlideTimingParameterProvider.java
 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcSlideTimingParameterProvider.java
index eae5891e..e94dadbd 100644
--- 
a/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcSlideTimingParameterProvider.java
+++ 
b/flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/split/JdbcSlideTimingParameterProvider.java
@@ -19,6 +19,7 @@
 package org.apache.flink.connector.jdbc.split;
 
 import org.apache.flink.annotation.PublicEvolving;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.SlideTimingSplitterEnumerator;
 import org.apache.flink.util.Preconditions;
 
 import javax.annotation.Nonnull;
@@ -27,7 +28,15 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
 
-/** The parameters provider generate parameters by slide timing window 
strategy. */
+/**
+ * The parameters provider generate parameters by slide timing window strategy.
+ *
+ * @deprecated Use {@link SlideTimingSplitterEnumerator} instead. As example:
+ *     SlideTimingSplitterEnumerator.builder() .setSqlTemplate(query) 
.setStartMills(startMills)
+ *     .setSlideSpanMills(slideSpanMills) .setSlideStepMills(slideStepMills)
+ *     .setSplitGenerateDelayMillis(splitGenerateDelayMillis) .build();
+ */
+@Deprecated
 @PublicEvolving
 public class JdbcSlideTimingParameterProvider implements 
JdbcParameterValuesProvider {
 
diff --git 
a/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceBuilderTest.java
 
b/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceBuilderTest.java
index d116433b..488ae807 100644
--- 
a/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceBuilderTest.java
+++ 
b/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceBuilderTest.java
@@ -22,8 +22,10 @@ import org.apache.flink.api.common.typeinfo.TypeHint;
 import org.apache.flink.api.common.typeinfo.TypeInformation;
 import org.apache.flink.connector.jdbc.JdbcConnectionOptions;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.config.ContinuousUnBoundingSettings;
-import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.SqlTemplateSplitEnumerator;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.JdbcSqlSplitterEnumerator;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.reader.extractor.ResultExtractor;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.split.CheckpointedOffset;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.split.JdbcSourceSplit;
 import 
org.apache.flink.connector.jdbc.datasource.connections.JdbcConnectionProvider;
 import 
org.apache.flink.connector.jdbc.datasource.connections.SimpleJdbcConnectionProvider;
 import 
org.apache.flink.connector.jdbc.split.JdbcGenericParameterValuesProvider;
@@ -53,7 +55,7 @@ class JdbcSourceBuilderTest {
     private final String dbUrl = "dbUrl";
     private final ResultExtractor<Row> extractor = 
ResultExtractor.ofRowResultExtractor();
     private final JdbcParameterValuesProvider parameterValuesProvider =
-            new JdbcNumericBetweenParametersProvider(0, 3);
+            new JdbcNumericBetweenParametersProvider(0, 3).ofBatchSize(1);
 
     private final TypeInformation<Row> typeInformation = new TypeHint<Row>() 
{}.getTypeInfo();
 
@@ -74,13 +76,15 @@ class JdbcSourceBuilderTest {
                 .isInstanceOf(IllegalArgumentException.class);
         assertThatThrownBy(() -> JdbcSource.builder().setSql(emptySql))
                 .isInstanceOf(IllegalArgumentException.class);
-        assertThatThrownBy(() -> JdbcSource.builder().setDBUrl(dbUrl).build())
-                .isInstanceOf(IllegalStateException.class);
+
         // For valid.
         JdbcSource<Row> jdbcSource = sourceBuilder.build();
-        SqlTemplateSplitEnumerator sqlEnumerator =
-                (SqlTemplateSplitEnumerator) 
jdbcSource.getSqlSplitEnumeratorProvider().create();
-        assertThat(sqlEnumerator.getSqlTemplate()).isEqualTo(validSql);
+        assertThat(jdbcSource.getSplitterEnumerator())
+                .isInstanceOf(JdbcSqlSplitterEnumerator.class);
+        JdbcSqlSplitterEnumerator sqlEnumerator =
+                (JdbcSqlSplitterEnumerator) jdbcSource.getSplitterEnumerator();
+
+        assertThat(sqlEnumerator.lineageQueries()).containsExactly(validSql);
     }
 
     @Test
@@ -89,10 +93,32 @@ class JdbcSourceBuilderTest {
                 .isInstanceOf(NullPointerException.class);
         JdbcSource<Row> jdbcSource =
                 
sourceBuilder.setJdbcParameterValuesProvider(parameterValuesProvider).build();
-        SqlTemplateSplitEnumerator sqlSplitEnumerator =
-                (SqlTemplateSplitEnumerator) 
jdbcSource.getSqlSplitEnumeratorProvider().create();
-        assertThat(sqlSplitEnumerator.getParameterValuesProvider())
-                .isEqualTo(parameterValuesProvider);
+        assertThat(jdbcSource.getSplitterEnumerator())
+                .isInstanceOf(JdbcSqlSplitterEnumerator.class);
+        JdbcSqlSplitterEnumerator sqlEnumerator =
+                (JdbcSqlSplitterEnumerator) jdbcSource.getSplitterEnumerator();
+        assertThat(sqlEnumerator.enumerateSplits())
+                .containsExactly(
+                        new JdbcSourceSplit(
+                                "0000000001",
+                                validSql,
+                                new Serializable[] {0L, 0L},
+                                new CheckpointedOffset()),
+                        new JdbcSourceSplit(
+                                "0000000002",
+                                validSql,
+                                new Serializable[] {1L, 1L},
+                                new CheckpointedOffset()),
+                        new JdbcSourceSplit(
+                                "0000000003",
+                                validSql,
+                                new Serializable[] {2L, 2L},
+                                new CheckpointedOffset()),
+                        new JdbcSourceSplit(
+                                "0000000004",
+                                validSql,
+                                new Serializable[] {3L, 3L},
+                                new CheckpointedOffset()));
     }
 
     @Test
diff --git 
a/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceStreamRelatedITCase.java
 
b/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceStreamRelatedITCase.java
index 23814eed..c45c9e64 100644
--- 
a/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceStreamRelatedITCase.java
+++ 
b/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceStreamRelatedITCase.java
@@ -149,6 +149,7 @@ class JdbcSourceStreamRelatedITCase implements 
DerbyTestBase, JdbcITCaseBase {
                 .addSink(new TestingSinkFunction());
         waitExpectation(client, env, () -> collectedRecords.size() >= 
TESTING_ENTRIES_SIZE);
 
+        assertThat(collectedRecords).hasSize(testEntries.size());
         
assertThat(collectedRecords).containsExactlyInAnyOrderElementsOf(testEntries);
     }
 
diff --git 
a/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSourceEnumeratorTest.java
 
b/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSourceEnumeratorTest.java
index 268fb73f..8b1c87e1 100644
--- 
a/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSourceEnumeratorTest.java
+++ 
b/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSourceEnumeratorTest.java
@@ -18,22 +18,21 @@
 
 package org.apache.flink.connector.jdbc.core.datastream.source.enumerator;
 
+import org.apache.flink.api.connector.source.Boundedness;
 import org.apache.flink.api.connector.source.SplitEnumeratorContext;
+import 
org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.SplitterEnumerator;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.split.CheckpointedOffset;
 import 
org.apache.flink.connector.jdbc.core.datastream.source.split.JdbcSourceSplit;
+import 
org.apache.flink.connector.jdbc.datasource.connections.JdbcConnectionProvider;
 import 
org.apache.flink.connector.testutils.source.reader.TestingSplitEnumeratorContext;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import javax.annotation.Nonnull;
-
-import java.io.IOException;
 import java.io.Serializable;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -108,12 +107,42 @@ class JdbcSourceEnumeratorTest {
 
         return new JdbcSourceEnumerator(
                 context,
-                new JdbcSqlSplitEnumeratorBase<JdbcSourceSplit>(null) {
+                new SplitterEnumerator() {
+                    @Override
+                    public Boundedness getBoundedness() {
+                        return Boundedness.BOUNDED;
+                    }
+
+                    @Override
+                    public void start(JdbcConnectionProvider 
connectionProvider) {}
+
+                    @Override
+                    public void close() {}
+
+                    @Override
+                    public boolean isAllSplitsFinished() {
+                        return true;
+                    }
+
                     @Override
-                    public @Nonnull List<JdbcSourceSplit> enumerateSplits(
-                            @Nonnull Supplier<Boolean> splitGettable) throws 
IOException {
+                    public List<JdbcSourceSplit> enumerateSplits() {
                         return Collections.emptyList();
                     }
+
+                    @Override
+                    public List<String> lineageQueries() {
+                        return Collections.emptyList();
+                    }
+
+                    @Override
+                    public Serializable serializableState() {
+                        return null;
+                    }
+
+                    @Override
+                    public SplitterEnumerator restoreState(Serializable state) 
{
+                        return null;
+                    }
                 },
                 null,
                 Arrays.stream(splits).collect(Collectors.toList()));
diff --git 
a/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/PreparedSplitterNumericParametersTest.java
 
b/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/PreparedSplitterNumericParametersTest.java
new file mode 100644
index 00000000..0b21d40c
--- /dev/null
+++ 
b/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/PreparedSplitterNumericParametersTest.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.Serializable;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class PreparedSplitterNumericParametersTest {
+
+    @Test
+    void testBatchSizeDivisible() {
+        Serializable[][] parameters =
+                new PreparedSplitterNumericParameters(-5, 
9).withBatchSize(3).getParameterValues();
+
+        long[][] expected = {
+            new long[] {-5, -3},
+            new long[] {-2, 0},
+            new long[] {1, 3},
+            new long[] {4, 6},
+            new long[] {7, 9}
+        };
+        check(expected, parameters);
+    }
+
+    @Test
+    void testBatchSizeNotDivisible() {
+        Serializable[][] parameters =
+                new PreparedSplitterNumericParameters(-5, 
11).withBatchSize(4).getParameterValues();
+
+        long[][] expected = {
+            new long[] {-5, -2},
+            new long[] {-1, 2},
+            new long[] {3, 5},
+            new long[] {6, 8},
+            new long[] {9, 11}
+        };
+        check(expected, parameters);
+    }
+
+    @Test
+    void testBatchSizeTooLarge() {
+        Serializable[][] parameters =
+                new PreparedSplitterNumericParameters(0, 
2).withBatchSize(5).getParameterValues();
+
+        long[][] expected = {new long[] {0, 2}};
+        check(expected, parameters);
+    }
+
+    @Test
+    void testBatchNumDivisible() {
+        Serializable[][] parameters =
+                new PreparedSplitterNumericParameters(-5, 
9).withBatchNum(5).getParameterValues();
+
+        long[][] expected = {
+            new long[] {-5, -3},
+            new long[] {-2, 0},
+            new long[] {1, 3},
+            new long[] {4, 6},
+            new long[] {7, 9}
+        };
+        check(expected, parameters);
+    }
+
+    @Test
+    void testBatchNumNotDivisible() {
+        Serializable[][] parameters =
+                new PreparedSplitterNumericParameters(-5, 
11).withBatchNum(5).getParameterValues();
+
+        long[][] expected = {
+            new long[] {-5, -2},
+            new long[] {-1, 2},
+            new long[] {3, 5},
+            new long[] {6, 8},
+            new long[] {9, 11}
+        };
+        check(expected, parameters);
+    }
+
+    @Test
+    void testBatchNumTooLarge() {
+        Serializable[][] parameters =
+                new PreparedSplitterNumericParameters(0, 
2).withBatchNum(5).getParameterValues();
+
+        long[][] expected = {
+            new long[] {0, 0},
+            new long[] {1, 1},
+            new long[] {2, 2}
+        };
+        check(expected, parameters);
+    }
+
+    @Test
+    void testBatchMaxMinTooLarge() {
+        Serializable[][] parameters =
+                new PreparedSplitterNumericParameters(2260418954055131340L, 
3875220057236942850L)
+                        .withBatchNum(3)
+                        .getParameterValues();
+
+        long[][] expected = {
+            new long[] {2260418954055131340L, 2798685988449068510L},
+            new long[] {2798685988449068511L, 3336953022843005680L},
+            new long[] {3336953022843005681L, 3875220057236942850L}
+        };
+        check(expected, parameters);
+    }
+
+    private void check(long[][] expected, Serializable[][] actual) {
+        assertThat(actual).hasDimensions(expected.length, expected[0].length);
+        for (int i = 0; i < expected.length; i++) {
+            for (int j = 0; j < expected[i].length; j++) {
+                assertThat(((Long) 
actual[i][j]).longValue()).isEqualTo(expected[i][j]);
+            }
+        }
+    }
+}
diff --git 
a/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/SlideTimingSplitterEnumeratorTest.java
 
b/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/SlideTimingSplitterEnumeratorTest.java
new file mode 100644
index 00000000..87e8e82c
--- /dev/null
+++ 
b/flink-connector-jdbc-core/src/test/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/splitter/SlideTimingSplitterEnumeratorTest.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.Serializable;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class SlideTimingSplitterEnumeratorTest {
+
+    @Test
+    void testBuilderWithValidParameters() {
+        SlideTimingSplitterEnumerator enumerator = defaultEnumerator();
+
+        assertThat(enumerator).isNotNull();
+    }
+
+    @Test
+    void testBuilderWithInvalidStartMills() {
+        assertThatThrownBy(() -> createEnumerator(0L, 500L, 100L))
+                .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void testBuilderWithInvalidSlideSpanMillsAndStepMills() {
+        assertThatThrownBy(() -> createEnumerator(1000L, 0L, 0L))
+                .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void testBuilderWithNegativeSplitGenerateDelayMillis() {
+        assertThatThrownBy(() -> createEnumerator(1000L, 500L, 100L, -1L))
+                .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void testEnumerateSplitsWithNoParameters() {
+        SlideTimingSplitterEnumerator enumerator =
+                new SlideTimingSplitterEnumerator(
+                        "SELECT * FROM table WHERE time >= ? AND time < ?", 
1000L, 500L, 100L, 0L) {
+                    @Override
+                    public Serializable[][] getSqlParameters() {
+                        return new Serializable[0][0];
+                    }
+                };
+        assertThat(enumerator.enumerateSplits()).isEmpty();
+    }
+
+    @Test
+    void testEquals() {
+        SlideTimingSplitterEnumerator enumerator1 = defaultEnumerator();
+
+        SlideTimingSplitterEnumerator enumerator2 = defaultEnumerator();
+
+        assertThat(enumerator1).isEqualTo(enumerator2);
+    }
+
+    @Test
+    void testHashCode() {
+        SlideTimingSplitterEnumerator enumerator1 = defaultEnumerator();
+
+        SlideTimingSplitterEnumerator enumerator2 = defaultEnumerator();
+
+        assertThat(enumerator1.hashCode()).isEqualTo(enumerator2.hashCode());
+    }
+
+    @Test
+    void testStart() {
+        SlideTimingSplitterEnumerator enumerator = defaultEnumerator();
+
+        assertThatCode(() -> 
enumerator.start(null)).doesNotThrowAnyException();
+    }
+
+    @Test
+    void testClose() {
+        SlideTimingSplitterEnumerator enumerator = defaultEnumerator();
+
+        assertThatCode(enumerator::close).doesNotThrowAnyException();
+    }
+
+    @Test
+    void testIsAllSplitsFinished() {
+        SlideTimingSplitterEnumerator enumerator = defaultEnumerator();
+
+        assertThat(enumerator.isAllSplitsFinished()).isFalse();
+    }
+
+    @Test
+    void testRestoreState() {
+        SlideTimingSplitterEnumerator enumerator = defaultEnumerator();
+
+        SlideTimingSplitterEnumerator restored = 
enumerator.restoreState(2000L);
+        assertThat(restored).isNotNull();
+    }
+
+    @Test
+    void testSerializableState() {
+        SlideTimingSplitterEnumerator enumerator = defaultEnumerator();
+
+        assertThat(enumerator.serializableState()).isNotNull();
+    }
+
+    SlideTimingSplitterEnumerator defaultEnumerator() {
+        return createEnumerator(1000L, 500L, 100L);
+    }
+
+    SlideTimingSplitterEnumerator createEnumerator(
+            Long startMillis, Long slideSpanMillis, Long slideStepMillis) {
+        return createEnumerator(startMillis, slideSpanMillis, slideStepMillis, 
0L);
+    }
+
+    SlideTimingSplitterEnumerator createEnumerator(
+            Long startMillis, Long slideSpanMillis, Long slideStepMillis, Long 
splitDelayMillis) {
+        return SlideTimingSplitterEnumerator.builder()
+                .setSqlTemplate("SELECT * FROM table WHERE time >= ? AND time 
< ?")
+                .setStartMillis(startMillis)
+                .setSlideSpanMillis(slideSpanMillis)
+                .setSlideStepMillis(slideStepMillis)
+                .setSplitGenerateDelayMillis(splitDelayMillis)
+                .build();
+    }
+}


Reply via email to