diff --git a/samples/arrow-flight-sql/java/README.md b/samples/arrow-flight-sql/java/README.md
new file mode 100644
index 00000000000000..1eb574e2ab6626
--- /dev/null
+++ b/samples/arrow-flight-sql/java/README.md
@@ -0,0 +1,35 @@
+
+
+# How to use:
+
+ 1. mvn clean install -U
+ 2. mvn package
+ 3. java --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED -cp java-0.1.jar doris.arrowflight.demo.Main "sql" "fe_ip" "fe_arrow_flight_port" "fe_query_port"
+
+# What can this demo do:
+
+ This is a java demo for doris arrow flight sql, you can use this to test various connection
+ methods for sending queries to the doris arrow flight server, help you understand how to use arrow flight sql
+ and test performance. You should install maven prior to run this demo.
+
+# Performance test
+
+ Section 6.2 of https://github.com/apache/doris/issues/25514 is the performance test
+ results of the doris arrow flight sql using java.
\ No newline at end of file
diff --git a/samples/arrow-flight-sql/java/pom.xml b/samples/arrow-flight-sql/java/pom.xml
new file mode 100644
index 00000000000000..695c01aa5eb4f4
--- /dev/null
+++ b/samples/arrow-flight-sql/java/pom.xml
@@ -0,0 +1,168 @@
+
+
+ * 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 doris.arrowflight.demo;
+
+import org.apache.arrow.vector.ValueVector;
+import org.apache.arrow.vector.VectorSchemaRoot;
+import org.apache.arrow.vector.ipc.ArrowReader;
+import org.apache.arrow.vector.types.pojo.Field;
+
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Iterate over each batch in ArrowReader.
+ * ArrowReader is the iterator returned by ADBC Client when executing a query.
+ */
+public class ArrowBatchReader {
+
+ @FunctionalInterface
+ public interface LoadArrowBatchFunc {
+ void load(ArrowReader reader) throws IOException;
+ }
+
+ /**
+ * Print one row in VectorSchemaRoot, if the output format is incorrect, may need to modify
+ * the output method of different types of ValueVector.
+ */
+ public static void printRow(VectorSchemaRoot root, int rowIndex) {
+ if (root == null || rowIndex < 0 || rowIndex >= root.getRowCount()) {
+ System.out.println("Invalid row index: " + rowIndex);
+ return;
+ }
+
+ System.out.print("> ");
+ for (Field field : root.getSchema().getFields()) {
+ ValueVector vector = root.getVector(field.getName());
+ if (vector != null) {
+ if (vector instanceof org.apache.arrow.vector.DateDayVector) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ int dayOffset = ((org.apache.arrow.vector.DateDayVector) vector).get(rowIndex);
+ LocalDate date = LocalDate.ofEpochDay(dayOffset);
+ System.out.print(date.format(formatter));
+ } else if (vector instanceof org.apache.arrow.vector.BitVector) {
+ System.out.print(((org.apache.arrow.vector.BitVector) vector).get(rowIndex) == 1);
+ } else {
+ // other types field
+ System.out.print(vector.getObject(rowIndex).toString());
+ }
+ System.out.print(", ");
+ }
+ }
+ System.out.println();
+ }
+
+ /**
+ * Iterate over each batch in ArrowReader with the least cost, only record the number of rows and batches,
+ * usually used to test performance.
+ */
+ public static LoadArrowBatchFunc loadArrowBatch = reader -> {
+ int rowCount = 0;
+ int batchCount = 0;
+ while (reader.loadNextBatch()) {
+ VectorSchemaRoot root = reader.getVectorSchemaRoot();
+ if (batchCount == 0) {
+ System.out.println("> " + root.getSchema().toString());
+ printRow(root, 1); // only print first line
+ }
+ rowCount += root.getRowCount();
+ batchCount += 1;
+ }
+ System.out.println("> batchCount: " + batchCount + ", rowCount: " + rowCount);
+ };
+
+ /**
+ * Iterate over each batch in ArrowReader and convert the batch to String, this will take more time.
+ */
+ public static LoadArrowBatchFunc loadArrowBatchToString = reader -> {
+ int rowCount = 0;
+ List
+ * 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 doris.arrowflight.demo;
+
+public class Configuration {
+ public String sql = ""; // require
+ public String ip = "127.0.0.1"; // require
+ public String arrowFlightPort = "9090"; // require
+ public String mysqlPort = "9030";
+ public int retryTimes = 2; // The first execution is cold run
+ public String user = "root";
+ public String password = "";
+
+ Configuration(String[] args) {
+ for (int i = 0; i < args.length; i++) {
+ switch (i) {
+ case 0 -> sql = args[i];
+ case 1 -> ip = args[i];
+ case 2 -> arrowFlightPort = args[i];
+ case 3 -> mysqlPort = args[i];
+ case 4 -> retryTimes = Integer.parseInt(args[i]);
+ case 5 -> user = args[i];
+ case 6 -> password = args[i];
+ }
+ }
+ }
+}
diff --git a/samples/arrow-flight-sql/java/src/main/java/doris/arrowflight/demo/FlightAdbcDriver.java b/samples/arrow-flight-sql/java/src/main/java/doris/arrowflight/demo/FlightAdbcDriver.java
new file mode 100644
index 00000000000000..b4a5f6bc7cb147
--- /dev/null
+++ b/samples/arrow-flight-sql/java/src/main/java/doris/arrowflight/demo/FlightAdbcDriver.java
@@ -0,0 +1,92 @@
+/**
+ * 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 doris.arrowflight.demo;
+
+import doris.arrowflight.demo.ArrowBatchReader.LoadArrowBatchFunc;
+import org.apache.arrow.adbc.core.AdbcConnection;
+import org.apache.arrow.adbc.core.AdbcDatabase;
+import org.apache.arrow.adbc.core.AdbcDriver;
+import org.apache.arrow.adbc.core.AdbcStatement;
+import org.apache.arrow.adbc.core.AdbcStatement.QueryResult;
+import org.apache.arrow.adbc.driver.flightsql.FlightSqlDriver;
+import org.apache.arrow.flight.Location;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.ipc.ArrowReader;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Use the Arrow Flight ADBC driver to connect to the Doris Arrow Flight server and execute query.
+ */
+public class FlightAdbcDriver {
+ private static void connectAndExecute(Configuration configuration, LoadArrowBatchFunc loadArrowReader) {
+ final BufferAllocator allocator = new RootAllocator();
+ FlightSqlDriver driver = new FlightSqlDriver(allocator);
+ Map
+ * 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 doris.arrowflight.demo;
+
+import doris.arrowflight.demo.ArrowBatchReader.LoadArrowBatchFunc;
+import org.apache.arrow.adbc.core.AdbcConnection;
+import org.apache.arrow.adbc.core.AdbcDatabase;
+import org.apache.arrow.adbc.core.AdbcDriver;
+import org.apache.arrow.adbc.core.AdbcStatement;
+import org.apache.arrow.adbc.driver.jdbc.JdbcDriver;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.ipc.ArrowReader;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Use the Arrow Flight JDBC driver to connect to the Doris Arrow Flight server and execute query.
+ * Unlike the Java JDBC DriverManager, this is a JDBC Driver provided by Arrow Flight, which may contain
+ * some optimizations (although no performance advantage was observed).
+ */
+public class FlightJdbcDriver {
+ private static void connectAndExecute(Configuration configuration, LoadArrowBatchFunc loadArrowReader) {
+ String DB_URL = "jdbc:arrow-flight-sql://" + configuration.ip + ":" + configuration.arrowFlightPort
+ + "?useServerPrepStmts=false" + "&cachePrepStmts=true&useSSL=false&useEncryption=false";
+ final Map
+ * 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 doris.arrowflight.demo;
+
+import com.google.protobuf.Any;
+import com.google.protobuf.ByteString;
+import org.apache.arrow.flight.FlightClient;
+import org.apache.arrow.flight.FlightInfo;
+import org.apache.arrow.flight.FlightStream;
+import org.apache.arrow.flight.Location;
+import org.apache.arrow.flight.Ticket;
+import org.apache.arrow.flight.auth2.BearerCredentialWriter;
+import org.apache.arrow.flight.grpc.CredentialCallOption;
+import org.apache.arrow.flight.sql.impl.FlightSql.TicketStatementQuery;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.VectorSchemaRoot;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Manually execute Arrow Flight SQL Rpc process, usually used for debug.
+ */
+public class FlightSqlClient {
+ public record FlightInfoResult
+ * 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 doris.arrowflight.demo;
+
+import doris.arrowflight.demo.JdbcResultSetReader.LoadJdbcResultSetFunc;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.Objects;
+
+/**
+ * Use the Java JDBC DriverManager to connect to the Doris Arrow Flight server and execute query.
+ * Usually, DriverManager is used to connect to the database using the Mysql protocol in Java. only need to replace
+ * `jdbc:mysql` in the URI with `jdbc:arrow-flight-sql` to connect to the database using the Arrow Flight SQL protocol
+ * (provided that the database implements the Arrow Flight server).
+ */
+public class JdbcDriverManager {
+ private static void connectAndExecute(Configuration configuration, String urlPrefix, String port,
+ LoadJdbcResultSetFunc loadJdbcResultSetFunc) {
+ String DB_URL = urlPrefix + "://" + configuration.ip + ":" + port + "?useServerPrepStmts=false"
+ + "&cachePrepStmts=true&useSSL=false&useEncryption=false";
+ try {
+ long start = System.currentTimeMillis();
+ Connection conn = DriverManager.getConnection(DB_URL, configuration.user, configuration.password);
+ Statement stmt = conn.createStatement();
+ stmt.execute(configuration.sql);
+
+ final ResultSet resultSet = stmt.getResultSet();
+ loadJdbcResultSetFunc.load(resultSet);
+ System.out.printf("> cost: %d ms.\n\n", (System.currentTimeMillis() - start));
+
+ stmt.close();
+ conn.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void run(Configuration configuration) {
+ System.out.println("*************************************");
+ System.out.println("| JdbcDriverManager |");
+ System.out.println("*************************************");
+
+ try {
+ if (!Objects.equals(configuration.mysqlPort, "")) {
+ Class.forName("com.mysql.cj.jdbc.Driver");
+ System.out.println("JdbcDriverManager > jdbc:mysql > loadJdbcResult");
+ connectAndExecute(configuration, "jdbc:mysql", configuration.mysqlPort,
+ JdbcResultSetReader.loadJdbcResult);
+ System.out.println("JdbcDriverManager > jdbc:mysql > loadJdbcResultToString");
+ connectAndExecute(configuration, "jdbc:mysql", configuration.mysqlPort,
+ JdbcResultSetReader.loadJdbcResultToString);
+ }
+
+ Class.forName("org.apache.arrow.driver.jdbc.ArrowFlightJdbcDriver");
+ System.out.println("JdbcDriverManager > jdbc:arrow-flight-sql > loadJdbcResultToString");
+ connectAndExecute(configuration, "jdbc:arrow-flight-sql", configuration.arrowFlightPort,
+ JdbcResultSetReader.loadJdbcResult);
+ System.out.println("JdbcDriverManager > jdbc:arrow-flight-sql > loadJdbcResultToString");
+ connectAndExecute(configuration, "jdbc:arrow-flight-sql", configuration.arrowFlightPort,
+ JdbcResultSetReader.loadJdbcResultToString);
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void main(String[] args) throws ClassNotFoundException {
+ Configuration configuration = new Configuration(args);
+ for (int i = 0; i < configuration.retryTimes; i++) {
+ run(configuration);
+ }
+ }
+}
diff --git a/samples/arrow-flight-sql/java/src/main/java/doris/arrowflight/demo/JdbcResultSetReader.java b/samples/arrow-flight-sql/java/src/main/java/doris/arrowflight/demo/JdbcResultSetReader.java
new file mode 100644
index 00000000000000..221fbb239cc703
--- /dev/null
+++ b/samples/arrow-flight-sql/java/src/main/java/doris/arrowflight/demo/JdbcResultSetReader.java
@@ -0,0 +1,65 @@
+/**
+ * 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 doris.arrowflight.demo;
+
+import java.io.IOException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Iterate over each row in jdbc ResultSet.
+ */
+public class JdbcResultSetReader {
+
+ @FunctionalInterface
+ public interface LoadJdbcResultSetFunc {
+ void load(ResultSet resultSet) throws IOException, SQLException;
+ }
+
+ public static LoadJdbcResultSetFunc loadJdbcResult = resultSet -> {
+ int rowCount = 0;
+ final int columnCount = resultSet.getMetaData().getColumnCount();
+ while (resultSet.next()) {
+ rowCount += 1;
+ }
+ System.out.println("> rowCount: " + rowCount + ", columnCount: " + columnCount);
+ };
+
+ public static LoadJdbcResultSetFunc loadJdbcResultToString = resultSet -> {
+ int rowCount = 0;
+ final int columnCount = resultSet.getMetaData().getColumnCount();
+ List
+ * 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 doris.arrowflight.demo;
+
+public class Main {
+ public static void main(String[] args) {
+ Configuration configuration = new Configuration(args);
+ for (int i = 0; i < configuration.retryTimes; i++) {
+ FlightAdbcDriver.run(configuration);
+ FlightJdbcDriver.run(configuration);
+ JdbcDriverManager.run(configuration);
+ }
+ FlightSqlClient.run(configuration);
+ }
+}
diff --git a/samples/arrow-flight-sql/java/src/test/java/doris/arrowflight/demo/ConfigurationTest.java b/samples/arrow-flight-sql/java/src/test/java/doris/arrowflight/demo/ConfigurationTest.java
new file mode 100644
index 00000000000000..a6582fae4f2ac8
--- /dev/null
+++ b/samples/arrow-flight-sql/java/src/test/java/doris/arrowflight/demo/ConfigurationTest.java
@@ -0,0 +1,37 @@
+/**
+ * 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 doris.arrowflight.demo;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit test for simple App.
+ */
+public class ConfigurationTest {
+
+ /**
+ * Rigorous Test :-)
+ */
+ @Test
+ public void shouldAnswerWithTrue() {
+ assertTrue(true);
+ }
+}