diff --git a/java/23ai-jdbc-vector-search/README.md b/java/23ai-jdbc-vector-search/README.md
new file mode 100644
index 00000000..c12c9d59
--- /dev/null
+++ b/java/23ai-jdbc-vector-search/README.md
@@ -0,0 +1 @@
+[Oracle AI Vector Search for Java Developers with the Oracle Database 23ai](https://juarezjunior.medium.com/oracle-ai-vector-search-for-java-developers-with-the-oracle-database-23ai-e29321d23fa0)
\ No newline at end of file
diff --git a/java/23ai-jdbc-vector-search/pom.xml b/java/23ai-jdbc-vector-search/pom.xml
new file mode 100644
index 00000000..a6dfb830
--- /dev/null
+++ b/java/23ai-jdbc-vector-search/pom.xml
@@ -0,0 +1,82 @@
+
+
+
+ 4.0.0
+
+ com.oracle.dev.jdbc
+ 23ai-jdbc-vector-search
+ 1.0-SNAPSHOT
+
+ 23ai-jdbc-vector-search
+ Oracle AI Vector Search for Java Developers with the Oracle
+ Database 23ai
+
+ https://juarezjunior.medium.com/oracle-ai-vector-search-for-java-developers-with-the-oracle-database-23ai-e29321d23fa0
+
+
+ UTF-8
+ 21
+ 21
+
+
+
+
+ com.oracle.database.jdbc
+ ojdbc11
+ 23.4.0.24.05
+
+
+ com.oracle.database.jdbc
+ ucp
+ 23.4.0.24.05
+
+
+
+
+
+
+ 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/23ai-jdbc-vector-search/sql/23ai-jdbc-vector-search.sql b/java/23ai-jdbc-vector-search/sql/23ai-jdbc-vector-search.sql
new file mode 100644
index 00000000..01092479
--- /dev/null
+++ b/java/23ai-jdbc-vector-search/sql/23ai-jdbc-vector-search.sql
@@ -0,0 +1,29 @@
+SELECT * FROM V$VERSION;
+
+CREATE USER VECTOR_USER IDENTIFIED BY QUOTA UNLIMITED ON USERS;
+GRANT DB_DEVELOPER_ROLE TO VECTOR_USER;
+GRANT CREATE SESSION TO VECTOR_USER;
+
+GRANT SELECT ANY TABLE ON SCHEMA VECTOR_USER TO VECTOR_USER;
+GRANT SELECT ANY TABLE ON SCHEMA VECTOR_USER TO VECTOR_USER;
+GRANT SELECT ANY TABLE ON SCHEMA VECTOR_USER TO VECTOR_USER;
+GRANT SELECT ANY TABLE ON SCHEMA VECTOR_USER TO VECTOR_USER;
+
+ALTER SESSION SET CURRENT_SCHEMA = VECTOR_USER;
+
+CREATE TABLE VECTOR_USER.ORACLE_AI_VECTOR_SEARCH_DEMO (ID NUMBER GENERATED ALWAYS as IDENTITY(START with 1 INCREMENT by 1) PRIMARY KEY, VECTOR_DATA VECTOR(3, FLOAT64));
+COMMIT;
+
+DESCRIBE VECTOR_USER.ORACLE_AI_VECTOR_SEARCH_DEMO;
+
+SELECT * FROM VECTOR_USER.ORACLE_AI_VECTOR_SEARCH_DEMO;
+
+DELETE FROM VECTOR_USER.ORACLE_AI_VECTOR_SEARCH_DEMO;
+COMMIT;
+
+DROP TABLE VECTOR_USER.ORACLE_AI_VECTOR_SEARCH_DEMO PURGE;
+
+
+
+
+
diff --git a/java/23ai-jdbc-vector-search/src/main/java/com/oracle/dev/jdbc/OracleAIVectorSearchWithJava.java b/java/23ai-jdbc-vector-search/src/main/java/com/oracle/dev/jdbc/OracleAIVectorSearchWithJava.java
new file mode 100644
index 00000000..d2106e18
--- /dev/null
+++ b/java/23ai-jdbc-vector-search/src/main/java/com/oracle/dev/jdbc/OracleAIVectorSearchWithJava.java
@@ -0,0 +1,181 @@
+/*
+ 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.dev.jdbc;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Properties;
+
+import oracle.jdbc.OracleType;
+import oracle.ucp.jdbc.PoolDataSource;
+import oracle.ucp.jdbc.PoolDataSourceFactory;
+
+public class OracleAIVectorSearchWithJava {
+
+ private final static String URL = "jdbc:oracle:thin:@localhost:1521/FREEPDB1";
+ private final static String USERNAME = System.getenv("DB_23AI_USERNAME");
+ private final static String PASSWORD = System.getenv("DB_23AI_PASSWORD");
+
+ private String insertSql = "INSERT INTO ORACLE_AI_VECTOR_SEARCH_DEMO (VECTOR_DATA) VALUES (?)";
+ private String querySql = "SELECT ID, VECTOR_DATA FROM ORACLE_AI_VECTOR_SEARCH_DEMO";
+ private String querySqlWithBind = "SELECT ID, VECTOR_DATA FROM ORACLE_AI_VECTOR_SEARCH_DEMO ORDER BY VECTOR_DISTANCE(VECTOR_DATA, ?, COSINE)";
+
+ public static void main(String[] args) throws SQLException {
+ OracleAIVectorSearchWithJava oracleAIVectorSearch = new OracleAIVectorSearchWithJava();
+ oracleAIVectorSearch.execute();
+ }
+
+ private void execute() throws SQLException {
+
+ System.out.println("Starting JDBC connection with PooledDataSource...");
+ try (Connection conn = getConnectionFromPooledDataSource()) {
+ System.out.println("Connected to Oracle Database 23ai! " + "\n");
+
+ System.out
+ .println("Inserting VECTOR with oracle.jdbc.OracleType.VECTOR...");
+ insertVector(conn);
+ System.out.println(
+ "VECTOR with oracle.jdbc.OracleType.VECTOR inserted!" + "\n");
+
+ System.out
+ .println("Inserting VECTOR with oracle.jdbc.OracleType.VARCHAR2...");
+ insertVectorWithVarChar2(conn);
+ System.out.println(
+ "VECTOR with oracle.jdbc.OracleType.VARCHAR2 inserted!" + "\n");
+
+ System.out.println("Inserting VECTOR with Batch API...");
+ insertVectorWithBatchAPI(conn);
+ System.out.println("VECTOR with Batch API inserted!" + "\n");
+
+ System.out.println("Retrieving VECTOR as double array...");
+ retrieveVectorAsArray(conn);
+ System.out.println("VECTOR retrieved!" + "\n");
+
+ System.out.println("Retrieving VECTOR as String...");
+ retrieveVectorAsString(conn);
+ System.out.println("VECTOR retrieved!" + "\n");
+
+ System.out.println(
+ "Retrieving VECTOR with bound VECTOR (L2 - Euclidean distance)...");
+ retrieveVectorWithBoundVector(conn);
+ System.out.println("VECTOR retrieved!");
+
+ }
+ }
+
+ private void insertVector(Connection connection) throws SQLException {
+ PreparedStatement insertStatement = connection.prepareStatement(insertSql,
+ new String[]{"ID"});
+ float[] vector = {1.1f, 2.2f, 3.3f};
+ System.out.println("SQL DML: " + insertSql);
+ System.out.println("VECTOR to be inserted: " + Arrays.toString(vector));
+ insertStatement.setObject(1, vector, OracleType.VECTOR_FLOAT64);
+ insertStatement.executeUpdate();
+ }
+
+ private void insertVectorWithVarChar2(Connection connection)
+ throws SQLException {
+ PreparedStatement insertStatement = connection.prepareStatement(insertSql,
+ new String[]{"ID"});
+ float[] vector = {1.1f, 2.2f, 3.3f};
+ System.out.println("SQL DML: " + insertSql);
+ System.out.println("VECTOR to be inserted: " + Arrays.toString(vector));
+ insertStatement.setObject(1, Arrays.toString(vector), OracleType.VARCHAR2);
+ insertStatement.executeUpdate();
+ }
+
+ private void insertVectorWithBatchAPI(Connection connection)
+ throws SQLException {
+
+ float[][] vectors = {{1.1f, 2.2f, 3.3f}, {1.3f, 7.2f, 4.3f},
+ {5.9f, 5.2f, 7.3f}};
+ System.out.println("SQL DML: " + insertSql);
+ System.out.println("VECTORs to be inserted as a batch: "
+ + Arrays.toString(vectors[0]) + ", " + Arrays.toString(vectors[1])
+ + ", " + Arrays.toString(vectors[2]));
+ try (PreparedStatement insertStatement = connection
+ .prepareStatement(insertSql, new String[]{"ID"})) {
+ for (float[] vector : vectors) {
+ insertStatement.setObject(1, vector, OracleType.VECTOR_FLOAT64);
+ insertStatement.addBatch();
+ }
+ insertStatement.executeBatch();
+ }
+ }
+
+ private void retrieveVectorAsArray(Connection connection)
+ throws SQLException {
+ PreparedStatement queryStatement = connection.prepareStatement(querySql);
+ System.out.println("SQL DML: " + querySql);
+ ResultSet resultSet = queryStatement.executeQuery();
+ float[] vector = null;
+ while (resultSet.next()) {
+ vector = resultSet.getObject(2, float[].class);
+ }
+ System.out.println("Retrieved VECTOR: " + Arrays.toString(vector));
+ }
+
+ private void retrieveVectorAsString(Connection connection)
+ throws SQLException {
+ PreparedStatement queryStatement = connection.prepareStatement(querySql);
+ System.out.println("SQL DML: " + querySql);
+ ResultSet resultSet = queryStatement.executeQuery();
+ String vector = null;
+ while (resultSet.next()) {
+ vector = (String) resultSet.getObject(2);
+ }
+ System.out.println("Retrieved VECTOR: " + vector);
+ }
+
+ private void retrieveVectorWithBoundVector(Connection connection)
+ throws SQLException {
+ PreparedStatement queryStatement = connection
+ .prepareStatement(querySqlWithBind);
+ float[] inputVector = {1.0f, 2.2f, 3.3f};
+ System.out.println("SQL DML: " + querySqlWithBind);
+ System.out.println("Bound VECTOR: " + Arrays.toString(inputVector));
+ queryStatement.setObject(1, inputVector, OracleType.VECTOR_FLOAT64);
+ ResultSet resultSet = queryStatement.executeQuery();
+ resultSet.next();
+ float[] outputVector = resultSet.getObject(2, float[].class);
+ System.out.println("Retrieved VECTOR: " + Arrays.toString(outputVector));
+ }
+
+ private Connection getConnectionFromPooledDataSource() throws SQLException {
+ // Create pool-enabled data source instance
+ PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
+ // set connection properties on the data source
+ pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
+ pds.setURL(URL);
+ pds.setUser(USERNAME);
+ pds.setPassword(PASSWORD);
+ // Configure pool properties with a Properties instance
+ Properties prop = new Properties();
+ prop.setProperty("oracle.jdbc.vectorDefaultGetObjectType", "String");
+ pds.setConnectionProperties(prop);
+ // Override any pool properties directly
+ pds.setInitialPoolSize(10);
+ // Get a database connection from the pool-enabled data source
+ Connection conn = pds.getConnection();
+ return conn;
+ }
+
+}