diff --git a/java/jdbc-reactive-extensions-intro/pom.xml b/java/jdbc-reactive-extensions-intro/pom.xml
new file mode 100644
index 00000000..f00a4998
--- /dev/null
+++ b/java/jdbc-reactive-extensions-intro/pom.xml
@@ -0,0 +1,74 @@
+
+
+
+ 4.0.0
+
+ com.oracle.jdbc.reactive
+ jdbc-reactive-extensions-intro
+ 1.0-SNAPSHOT
+ jdbc-reactive-extensions-intro
+
+
+ UTF-8
+ 21
+ 21
+
+
+
+
+ com.oracle.database.jdbc
+ ojdbc17-production
+ 23.7.0.25.01
+ pom
+
+
+
+
+
+
+
+ maven-clean-plugin
+ 3.1.0
+
+
+ maven-site-plugin
+ 3.7.1
+
+
+ maven-project-info-reports-plugin
+ 3.0.0
+
+
+
+ maven-resources-plugin
+ 3.0.2
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+
+ maven-surefire-plugin
+ 2.22.1
+
+
+ maven-jar-plugin
+ 3.0.2
+
+
+ maven-install-plugin
+ 2.5.2
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+
+
+
+
+
diff --git a/java/jdbc-reactive-extensions-intro/src/main/java/com/oracle/jdbc/reactive/DatabaseConfig.java b/java/jdbc-reactive-extensions-intro/src/main/java/com/oracle/jdbc/reactive/DatabaseConfig.java
new file mode 100644
index 00000000..9aea8534
--- /dev/null
+++ b/java/jdbc-reactive-extensions-intro/src/main/java/com/oracle/jdbc/reactive/DatabaseConfig.java
@@ -0,0 +1,120 @@
+/*
+ Copyright (c) 2024, Oracle and/or its affiliates.
+
+ This software is dual-licensed to you under the Universal Permissive License
+ (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
+ 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
+ either license.
+
+ 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
+
+ https://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 com.oracle.jdbc.reactive;
+
+import oracle.jdbc.OracleConnection;
+import oracle.jdbc.pool.OracleDataSource;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.sql.SQLException;
+import java.util.Properties;
+
+public class DatabaseConfig {
+
+ private static final ConfigData CONFIG_DATA = loadConfig();
+
+ private static ConfigData loadConfig() {
+ Properties properties = new Properties();
+ try {
+ var fileStream = Files.newInputStream(Path.of("src/main/resources/config.properties"));
+ properties.load(fileStream);
+ return new ConfigData(properties.getProperty("DRIVER"), properties.getProperty("USER"),
+ properties.getProperty("PASSWORD"), properties.getProperty("HOST"),
+ Integer.parseInt(properties.getProperty("PORT")), properties.getProperty("DATABASE"),
+ properties.getProperty("DB_TABLE_NAME"));
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static OracleDataSource getDataSource() throws SQLException {
+ if (CONFIG_DATA == null) {
+ throw new IllegalStateException("Configuration data is not available, check file path.");
+ }
+ OracleDataSource dataSource = new OracleDataSource();
+ dataSource
+ .setURL("jdbc:oracle:thin:@" + CONFIG_DATA.host() + ":" + CONFIG_DATA.port() + "/" + CONFIG_DATA.database());
+ dataSource.setUser(CONFIG_DATA.user());
+ dataSource.setPassword(CONFIG_DATA.password());
+ return dataSource;
+ }
+
+ public static OracleConnection getConnection() throws SQLException {
+ return (OracleConnection) getDataSource().getConnection();
+ }
+
+ public static String getDbTableName() {
+ if (CONFIG_DATA == null) {
+ throw new IllegalStateException("Configuration could not be loaded.");
+ }
+ return CONFIG_DATA.dbTableName();
+ }
+
+ public static String getDriver() {
+ if (CONFIG_DATA == null) {
+ throw new IllegalStateException("Configuration could not be loaded.");
+ }
+ return CONFIG_DATA.driver();
+ }
+
+ public static String getUser() {
+ if (CONFIG_DATA == null) {
+ throw new IllegalStateException("Configuration could not be loaded.");
+ }
+ return CONFIG_DATA.user();
+ }
+
+ public static String getPassword() {
+ if (CONFIG_DATA == null) {
+ throw new IllegalStateException("Configuration could not be loaded.");
+ }
+ return CONFIG_DATA.password();
+ }
+
+ public static String getHost() {
+ if (CONFIG_DATA == null) {
+ throw new IllegalStateException("Configuration could not be loaded.");
+ }
+ return CONFIG_DATA.host();
+ }
+
+ public static int getPort() {
+ if (CONFIG_DATA == null) {
+ throw new IllegalStateException("Configuration could not be loaded.");
+ }
+ return CONFIG_DATA.port();
+ }
+
+ public static String getDatabase() {
+ if (CONFIG_DATA == null) {
+ throw new IllegalStateException("Configuration could not be loaded.");
+ }
+ return CONFIG_DATA.database();
+ }
+
+ private record ConfigData(String driver, String user, String password, String host, int port, String database,
+ String dbTableName) {
+ }
+}
\ No newline at end of file
diff --git a/java/jdbc-reactive-extensions-intro/src/main/java/com/oracle/jdbc/reactive/SQLStatementWithAsynchronousJDBC.java b/java/jdbc-reactive-extensions-intro/src/main/java/com/oracle/jdbc/reactive/SQLStatementWithAsynchronousJDBC.java
new file mode 100644
index 00000000..ac65d50b
--- /dev/null
+++ b/java/jdbc-reactive-extensions-intro/src/main/java/com/oracle/jdbc/reactive/SQLStatementWithAsynchronousJDBC.java
@@ -0,0 +1,178 @@
+/*
+ Copyright (c) 2024, Oracle and/or its affiliates.
+
+ This software is dual-licensed to you under the Universal Permissive License
+ (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License
+ 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
+ either license.
+
+ 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
+
+ https://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 com.oracle.jdbc.reactive;
+
+import java.sql.SQLException;
+import java.util.concurrent.Flow;
+import java.util.concurrent.SubmissionPublisher;
+import java.util.stream.IntStream;
+
+import oracle.jdbc.OracleConnection;
+import oracle.jdbc.OraclePreparedStatement;
+import oracle.jdbc.OracleResultSet;
+import oracle.jdbc.OracleStatement;
+
+public class SQLStatementWithAsynchronousJDBC {
+
+ public static void main(String[] args) throws InterruptedException {
+ SQLStatementWithAsynchronousJDBC asyncSQL = new SQLStatementWithAsynchronousJDBC();
+ try (OracleConnection conn = DatabaseConfig.getConnection()) {
+ asyncSQL.createTable(conn);
+ IntStream.rangeClosed(0, 3).forEach(i -> asyncSQL.insertData(conn, i, "Java " + i, "Duke " + i));
+ asyncSQL.readData(conn);
+ Thread.sleep(5000);
+ asyncSQL.dropTable(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private Flow.Publisher insertData(OracleConnection connection, int id, String firstName, String lastName) {
+ try {
+ final OraclePreparedStatement insertStatement = (OraclePreparedStatement) connection
+ .prepareStatement("INSERT INTO employee_names (id, first_name, last_name) VALUES (?, ?, ?)");
+ insertStatement.setInt(1, id);
+ insertStatement.setString(2, firstName);
+ insertStatement.setString(3, lastName);
+
+ Flow.Publisher insertPublisher = insertStatement.unwrap(OraclePreparedStatement.class)
+ .executeAsyncOracle();
+
+ insertPublisher.subscribe(new Flow.Subscriber() {
+ private Flow.Subscription subscription;
+
+ public void onSubscribe(Flow.Subscription subscription) {
+ this.subscription = subscription;
+ subscription.request(1L);
+ }
+
+ public void onNext(Boolean item) {
+ }
+
+ public void onError(Throwable throwable) {
+ closeStatement();
+ throwable.printStackTrace();
+ }
+
+ public void onComplete() {
+ closeStatement();
+ }
+
+ void closeStatement() {
+ try {
+ if (insertStatement != null && !insertStatement.isClosed()) {
+ insertStatement.close();
+ }
+ } catch (SQLException closeException) {
+ closeException.printStackTrace();
+ }
+ }
+ });
+
+ return insertPublisher;
+ } catch (SQLException e) {
+ e.printStackTrace();
+ SubmissionPublisher publisher = new SubmissionPublisher<>();
+ publisher.close();
+ return publisher;
+ }
+ }
+
+ public Flow.Publisher readData(OracleConnection connection) {
+ try {
+ final OraclePreparedStatement readStatement = (OraclePreparedStatement) connection
+ .prepareStatement("SELECT * FROM employee_names WHERE first_name LIKE ?");
+ readStatement.setString(1, "Jav%");
+
+ Flow.Publisher readPublisher = readStatement.unwrap(OraclePreparedStatement.class)
+ .executeQueryAsyncOracle();
+
+ readPublisher.subscribe(new Flow.Subscriber() {
+ private Flow.Subscription subscription;
+
+ public void onSubscribe(Flow.Subscription subscription) {
+ this.subscription = subscription;
+ subscription.request(Long.MAX_VALUE);
+ }
+
+ public void onNext(OracleResultSet resultSet) {
+ try {
+ while (resultSet.next()) {
+ int id = resultSet.getInt("id");
+ String firstName = resultSet.getString("first_name");
+ String lastName = resultSet.getString("last_name");
+ System.out.println("ID: " + id + ", First Name: " + firstName + ", Last Name: " + lastName);
+ }
+ System.out.println("Finished receiving stream data successfully. \nPreparing to drop table...");
+ } catch (SQLException e) {
+ onError(e);
+ }
+ }
+
+ public void onError(Throwable throwable) {
+ closeStatement();
+ throwable.printStackTrace();
+ }
+
+ public void onComplete() {
+ closeStatement();
+ }
+
+ void closeStatement() {
+ try {
+ if (readStatement != null && !readStatement.isClosed()) {
+ readStatement.close();
+ }
+ } catch (SQLException closeException) {
+ closeException.printStackTrace();
+ }
+ }
+ });
+ return readPublisher;
+ } catch (SQLException e) {
+ e.printStackTrace();
+ SubmissionPublisher publisher = new SubmissionPublisher<>();
+ publisher.close();
+ return publisher;
+ }
+ }
+
+ private void createTable(OracleConnection connection) {
+ String createTableSQL = "CREATE TABLE employee_names (id NUMBER PRIMARY KEY, first_name VARCHAR2(50), last_name VARCHAR2(50))";
+ try (OracleStatement createTableStatement = (OracleStatement) connection.createStatement()) {
+ createTableStatement.execute(createTableSQL);
+ System.out.println("Table 'employee_names' created successfully.");
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void dropTable(OracleConnection connection) {
+ String dropTableSQL = "DROP TABLE employee_names";
+ try (OracleStatement dropTableStatement = (OracleStatement) connection.createStatement()) {
+ dropTableStatement.execute(dropTableSQL);
+ System.out.println("Table 'employee_names' dropped successfully.");
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/java/jdbc-reactive-extensions-intro/src/main/resources/config.properties b/java/jdbc-reactive-extensions-intro/src/main/resources/config.properties
new file mode 100644
index 00000000..562d101d
--- /dev/null
+++ b/java/jdbc-reactive-extensions-intro/src/main/resources/config.properties
@@ -0,0 +1,7 @@
+HOST=
+PORT=
+DATABASE=
+USER=
+SCHEMA=
+DB_TABLE_NAME=employee_names
\ No newline at end of file
diff --git a/java/jdbc-reactive-extensions-intro/src/sql/jdbc-reactive-extensions-intro.sql b/java/jdbc-reactive-extensions-intro/src/sql/jdbc-reactive-extensions-intro.sql
new file mode 100644
index 00000000..1c1f69b4
--- /dev/null
+++ b/java/jdbc-reactive-extensions-intro/src/sql/jdbc-reactive-extensions-intro.sql
@@ -0,0 +1,33 @@
+-- THIS TABLE IS CREATED FROM JAVA CODE -> SQLStatementWithAsynchronousJDBC.java -> createTable() method
+-- CREATE TABLE employee_names (id NUMBER PRIMARY KEY, first_name VARCHAR2(50), last_name VARCHAR2(50))
+
+--IF YOU WANT TO CONFIRM YOU HAVE A TABLE WHILE ON DEBUGBING MODE
+DESCRIBE employee_names;
+
+--IF YOU WANT TO CHECK THE TABLE RECORDS WHILE ON DEBUGGING MODE
+SELECT * FROM employee_names;
+
+/*
+
+INSERT INTO employee_names (id, first_name, last_name) VALUES (1, 'John', 'Doe');
+
+INSERT INTO employee_names (id, first_name, last_name)
+VALUES (2, 'Jane', 'Smith');
+
+INSERT INTO employee_names (id, first_name, last_name)
+VALUES (3, 'David', 'Lee');
+
+INSERT INTO employee_names (id, first_name, last_name)
+VALUES (4, 'Emily', 'Jones');
+
+INSERT INTO employee_names (id, first_name, last_name)
+VALUES (5, 'Michael', 'Brown');
+COMMIT;
+
+*/
+
+/*
+-- TABLE IS DROPPED FROM JAVA CODE -> SQLStatementWithAsynchronousJDBC.java -> dropTable() method
+DROP TABLE employee_names;
+COMMIT;
+*/