diff --git a/README.md b/README.md
index 1fbbb39084..642d12bb88 100644
--- a/README.md
+++ b/README.md
@@ -78,10 +78,9 @@ The `neo4j-java-driver-all` includes an explicit module declaration ([module-inf
To run a simple query, the following can be used:
```java
var authToken = AuthTokens.basic("neo4j", "password");
-try (var driver = GraphDatabase.driver("bolt://localhost:7687", authToken); var session = driver.session()) {
- var result = session.run("CREATE (n)");
- var summary = result.consume();
- System.out.println(summary.counters().nodesCreated());
+try (var driver = GraphDatabase.driver("bolt://localhost:7687", authToken)) {
+ var result = driver.executableQuery("CREATE (n)").execute();
+ System.out.println(result.summary().counters().nodesCreated());
}
```
diff --git a/driver/clirr-ignored-differences.xml b/driver/clirr-ignored-differences.xml
index 5ea1d823ae..85002807cc 100644
--- a/driver/clirr-ignored-differences.xml
+++ b/driver/clirr-ignored-differences.xml
@@ -503,4 +503,39 @@
org.neo4j.driver.BookmarkManager executableQueryBookmarkManager()
+
+ org/neo4j/driver/RoutingControl
+ 6001
+ WRITERS
+
+
+
+ org/neo4j/driver/RoutingControl
+ 6001
+ READERS
+
+
+
+ org/neo4j/driver/RoutingControl
+ 2000
+
+
+
+ org/neo4j/driver/RoutingControl
+ 4001
+ java/lang/Comparable
+
+
+
+ org/neo4j/driver/RoutingControl
+ 4001
+ java/lang/constant/Constable
+
+
+
+ org/neo4j/driver/RoutingControl
+ 5001
+ java/lang/Enum
+
+
diff --git a/driver/src/main/java/org/neo4j/driver/Driver.java b/driver/src/main/java/org/neo4j/driver/Driver.java
index 25db420f9c..d0fd2bc7e6 100644
--- a/driver/src/main/java/org/neo4j/driver/Driver.java
+++ b/driver/src/main/java/org/neo4j/driver/Driver.java
@@ -25,7 +25,6 @@
import org.neo4j.driver.reactive.RxSession;
import org.neo4j.driver.types.TypeSystem;
import org.neo4j.driver.util.Experimental;
-import org.neo4j.driver.util.Preview;
/**
* Accessor for a specific Neo4j graph database.
@@ -72,7 +71,6 @@ public interface Driver extends AutoCloseable {
* @return new executable query instance
* @since 5.7
*/
- @Preview(name = "Driver Level Queries")
ExecutableQuery executableQuery(String query);
/**
@@ -81,7 +79,6 @@ public interface Driver extends AutoCloseable {
* @return bookmark manager, must not be {@code null}
* @since 5.7
*/
- @Preview(name = "Driver Level Queries")
BookmarkManager executableQueryBookmarkManager();
/**
diff --git a/driver/src/main/java/org/neo4j/driver/EagerResult.java b/driver/src/main/java/org/neo4j/driver/EagerResult.java
index c21ac6f328..5f2a23ef0b 100644
--- a/driver/src/main/java/org/neo4j/driver/EagerResult.java
+++ b/driver/src/main/java/org/neo4j/driver/EagerResult.java
@@ -20,13 +20,11 @@
import java.util.List;
import org.neo4j.driver.summary.ResultSummary;
-import org.neo4j.driver.util.Preview;
/**
* An in-memory result of executing a Cypher query that has been consumed in full.
* @since 5.5
*/
-@Preview(name = "Driver Level Queries")
public interface EagerResult {
/**
* Returns the keys of the records this result contains.
diff --git a/driver/src/main/java/org/neo4j/driver/ExecutableQuery.java b/driver/src/main/java/org/neo4j/driver/ExecutableQuery.java
index 2ecce9ebe8..c6efeb5012 100644
--- a/driver/src/main/java/org/neo4j/driver/ExecutableQuery.java
+++ b/driver/src/main/java/org/neo4j/driver/ExecutableQuery.java
@@ -26,7 +26,6 @@
import java.util.stream.Collectors;
import org.neo4j.driver.internal.EagerResultValue;
import org.neo4j.driver.summary.ResultSummary;
-import org.neo4j.driver.util.Preview;
/**
* An executable query that executes a query in a managed transaction with automatic retries on retryable errors.
@@ -96,7 +95,6 @@
*
* @since 5.7
*/
-@Preview(name = "Driver Level Queries")
public interface ExecutableQuery {
/**
* Sets query parameters.
@@ -166,7 +164,6 @@ default T execute(Collector recordCollector) {
* @param the final value type
* @since 5.5
*/
- @Preview(name = "Driver Level Queries")
@FunctionalInterface
interface ResultFinisher {
/**
diff --git a/driver/src/main/java/org/neo4j/driver/QueryConfig.java b/driver/src/main/java/org/neo4j/driver/QueryConfig.java
index d9fd3dce8b..5a0e13b00a 100644
--- a/driver/src/main/java/org/neo4j/driver/QueryConfig.java
+++ b/driver/src/main/java/org/neo4j/driver/QueryConfig.java
@@ -24,13 +24,11 @@
import java.io.Serializable;
import java.util.Objects;
import java.util.Optional;
-import org.neo4j.driver.util.Preview;
/**
* Query configuration used by {@link Driver#executableQuery(String)} and its variants.
* @since 5.5
*/
-@Preview(name = "Driver Level Queries")
public final class QueryConfig implements Serializable {
@Serial
private static final long serialVersionUID = -2632780731598141754L;
@@ -154,7 +152,7 @@ public String toString() {
* Builder used to configure {@link QueryConfig} which will be used to execute a query.
*/
public static final class Builder {
- private RoutingControl routing = RoutingControl.WRITERS;
+ private RoutingControl routing = RoutingControl.WRITE;
private String database;
private String impersonatedUser;
private BookmarkManager bookmarkManager;
diff --git a/driver/src/main/java/org/neo4j/driver/RoutingControl.java b/driver/src/main/java/org/neo4j/driver/RoutingControl.java
index b203bc06b4..709e21cefe 100644
--- a/driver/src/main/java/org/neo4j/driver/RoutingControl.java
+++ b/driver/src/main/java/org/neo4j/driver/RoutingControl.java
@@ -18,20 +18,22 @@
*/
package org.neo4j.driver;
-import org.neo4j.driver.util.Preview;
+import java.io.Serializable;
+import org.neo4j.driver.internal.InternalRoutingControl;
/**
* Defines routing mode for query.
* @since 5.5
*/
-@Preview(name = "Driver Level Queries")
-public enum RoutingControl {
+public sealed interface RoutingControl extends Serializable permits InternalRoutingControl {
/**
* Routes to the leader of the cluster.
+ * @since 5.8
*/
- WRITERS,
+ RoutingControl WRITE = InternalRoutingControl.WRITE;
/**
* Routes to the followers in the cluster.
+ * @since 5.8
*/
- READERS
+ RoutingControl READ = InternalRoutingControl.READ;
}
diff --git a/driver/src/main/java/org/neo4j/driver/internal/InternalExecutableQuery.java b/driver/src/main/java/org/neo4j/driver/internal/InternalExecutableQuery.java
index 85e918d576..160e93f1f0 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/InternalExecutableQuery.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/InternalExecutableQuery.java
@@ -27,6 +27,7 @@
import org.neo4j.driver.Query;
import org.neo4j.driver.QueryConfig;
import org.neo4j.driver.Record;
+import org.neo4j.driver.RoutingControl;
import org.neo4j.driver.SessionConfig;
import org.neo4j.driver.TransactionCallback;
@@ -77,10 +78,9 @@ public T execute(Collector recordCollector, ResultFinish
var summary = result.consume();
return resultFinisher.finish(result.keys(), finishedValue, summary);
};
- return switch (config.routing()) {
- case WRITERS -> session.executeWrite(txCallback);
- case READERS -> session.executeRead(txCallback);
- };
+ return config.routing().equals(RoutingControl.READ)
+ ? session.executeRead(txCallback)
+ : session.executeWrite(txCallback);
}
}
diff --git a/driver/src/main/java/org/neo4j/driver/internal/InternalRoutingControl.java b/driver/src/main/java/org/neo4j/driver/internal/InternalRoutingControl.java
new file mode 100644
index 0000000000..b197893c02
--- /dev/null
+++ b/driver/src/main/java/org/neo4j/driver/internal/InternalRoutingControl.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.neo4j.driver.internal;
+
+import java.io.Serial;
+import java.util.Objects;
+import org.neo4j.driver.RoutingControl;
+
+public final class InternalRoutingControl implements RoutingControl {
+ public static final RoutingControl WRITE = new InternalRoutingControl("WRITE");
+ public static final RoutingControl READ = new InternalRoutingControl("READ");
+
+ @Serial
+ private static final long serialVersionUID = 6766432177358809940L;
+
+ private final String mode;
+
+ private InternalRoutingControl(String mode) {
+ Objects.requireNonNull(mode, "mode must not be null");
+ this.mode = mode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ InternalRoutingControl that = (InternalRoutingControl) o;
+ return mode.equals(that.mode);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mode);
+ }
+}
diff --git a/driver/src/test/java/org/neo4j/driver/QueryConfigTest.java b/driver/src/test/java/org/neo4j/driver/QueryConfigTest.java
index ab4d23a40e..e47d3f8ea1 100644
--- a/driver/src/test/java/org/neo4j/driver/QueryConfigTest.java
+++ b/driver/src/test/java/org/neo4j/driver/QueryConfigTest.java
@@ -23,9 +23,10 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
+import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mockito;
import org.neo4j.driver.summary.ResultSummary;
import org.neo4j.driver.testutil.TestUtil;
@@ -36,14 +37,18 @@ void shouldReturnDefaultValues() {
var config = QueryConfig.defaultConfig();
var manager = Mockito.mock(BookmarkManager.class);
- assertEquals(RoutingControl.WRITERS, config.routing());
+ assertEquals(RoutingControl.WRITE, config.routing());
assertTrue(config.database().isEmpty());
assertTrue(config.impersonatedUser().isEmpty());
assertEquals(manager, config.bookmarkManager(manager).get());
}
+ static List routingControls() {
+ return List.of(RoutingControl.READ, RoutingControl.WRITE);
+ }
+
@ParameterizedTest
- @EnumSource(RoutingControl.class)
+ @MethodSource("routingControls")
void shouldUpdateRouting(RoutingControl routing) {
var config = QueryConfig.builder().withRouting(routing).build();
assertEquals(routing, config.routing());
diff --git a/driver/src/test/java/org/neo4j/driver/internal/InternalExecutableQueryTest.java b/driver/src/test/java/org/neo4j/driver/internal/InternalExecutableQueryTest.java
index 2c1fc78c30..2367b5627b 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/InternalExecutableQueryTest.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/InternalExecutableQueryTest.java
@@ -34,7 +34,7 @@
import java.util.stream.Collector;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.neo4j.driver.BookmarkManager;
import org.neo4j.driver.Driver;
@@ -114,8 +114,12 @@ void shouldUpdateConfig() {
assertEquals(config, executableQuery.config());
}
+ static List routingControls() {
+ return List.of(RoutingControl.READ, RoutingControl.WRITE);
+ }
+
@ParameterizedTest
- @EnumSource(RoutingControl.class)
+ @MethodSource("routingControls")
@SuppressWarnings("unchecked")
void shouldExecuteAndReturnResult(RoutingControl routingControl) {
// GIVEN
@@ -126,10 +130,7 @@ void shouldExecuteAndReturnResult(RoutingControl routingControl) {
given(driver.session(any(SessionConfig.class))).willReturn(session);
var txContext = mock(TransactionContext.class);
BiFunction, Object> executeMethod =
- switch (routingControl) {
- case WRITERS -> Session::executeWrite;
- case READERS -> Session::executeRead;
- };
+ routingControl.equals(RoutingControl.READ) ? Session::executeRead : Session::executeWrite;
given(executeMethod.apply(session, any())).willAnswer(answer -> {
TransactionCallback> txCallback = answer.getArgument(0);
return txCallback.execute(txContext);
diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ExecuteQuery.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ExecuteQuery.java
index bfc75a7931..035d0fc21a 100644
--- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ExecuteQuery.java
+++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ExecuteQuery.java
@@ -45,8 +45,8 @@ public TestkitResponse process(TestkitState testkitState) {
var routing = data.getConfig().getRouting();
if (data.getConfig().getRouting() != null) {
switch (routing) {
- case "w" -> configBuilder.withRouting(RoutingControl.WRITERS);
- case "r" -> configBuilder.withRouting(RoutingControl.READERS);
+ case "w" -> configBuilder.withRouting(RoutingControl.WRITE);
+ case "r" -> configBuilder.withRouting(RoutingControl.READ);
default -> throw new IllegalArgumentException();
}
}