What’s in ‘Oracle DB 23c Free Developer Release’ for Java Developers
Free as in Free Beer
The “Oracle Database 23c Free — Developer Release” is a full-featured, no-click-through, developer focussed Oracle database. It has been released under the Free Use Terms & Conditions license; see more details here.
It brings new features for Java developers. This blog post highlights my favorite features; others will be covered in depth in subsequent blog posts.
My Favorites Cool Java Features
I am totally biased here! I’ll cover Java support for pipelined database operations, Relational-JSON Duality Views, and Self-driven diagnosability.
1 — Java and Pipelined Database Operations
A database pipeline consists of a sequence of multiple database requests submitted without waiting for a response between operations. The database sends a response when the results of each query are ready.
Pipelined database operations foster an asynchronous programming model, in which a user thread returns immediately upon submitting a SQL statement for execution without waiting for its execution and the ResultSets.
Java developers would leverage database pipelining through the Oracle JDBC Reactive Extensions, the Reactive Streams libraries, Virtual Threads, and the standard JDBC Batching.
JDBC Reactive Extensions and Database Pipelining
Introduced in Oracle JDBC release 21.1, the JDBC Reactive Extensions implement the Java Flow API and extend it with the asynchronous forms of connection creation, SQL execution, row data fetching LOB I/O, and transaction control (i.e., commit/rollback). The API is fully compliant with the Reactive Streams 1.0.3 specification and interoperates with the ecosystem of reactive libraries including Spring, jOOQ, Helidon, Hibernate, Project Reactor, RxJava, Akka Streams, and more.
The following code introduces the concept of calling AsyncOracle methods — part of Oracle JDBCs’ Reactive Extensions — to compose a pipeline.
In the later examples, the intricacies of closing statements and consuming results for reactive and synchronous-using-Virtual-Threads programming models will be shown.
In the example above, a call to unwrap(OraclePreparedStatement.class) is used to access the methods of the Reactive Extensions API i.e., executeUpdateAsyncOracle and executeQueryAsyncOracle. These methods return a Flow.Publisher. As the result of each SQL statement is received, the Publisher emits a ResultSet to a Subscriber. How exactly? JDBC “listens” for the database response to each SQL statement by polling the network using a single Selector across the driver. Upon the reception of the database response, the Selector invokes a callback that reads the message.
Reactive Streams Libraries and Database Pipelining
Reactive libraries such as Project Reactor, RxJava, Akka Streams, Spring, jOOQ, Helidon MP Reactive Streams, Hibernate Reactive, and so on — expose an API that is similar to java.util.stream.Stream. The code example from the previous section can be expanded to use the API of Project Reactor.
In the example above, instances of Mono and Flux are created by calling their “from” methods. The argument to the method is an org.reactivestreams.Publisher, which is adapted from a Flow.Publisher by calling org.reactivestreams.FlowAdapters.toPublisher. A “subscribe” method is called to asynchronously consume the results with a callback function. To consume a result of multiple rows, a call to Mono.flatMapMany converts a stream of a single ResultSet into a stream of multiple row values.
Virtual Threads and Database Pipelining
Looking ahead, Virtual Threads (Project Loom) is an alternative to Reactive Streams and is touted as the future of Java scalability.
Oracle JDBC has been instrumented with Virtual Threads since the 21.1 release. Specifically, the driver will not cause thread pinning by holding an intrinsic lock during I/O calls. For more details, see: http://cr.openjdk.java.net/~rpressler/loom/loom/sol1_part1.html#pinning
With Virtual Threads, asynchronous APIs like Reactive Streams can easily be translated into synchronous code. A Virtual Thread only needs to block its execution until an asynchronous result has been received. To demonstrate this, the code example from the previous section can be re-written as follows:
Just like the code sample of the previous section, each Flow.Publisher is converted into a Mono or Flux from the Project Reactor API. However, with Virtual Threads, there is no longer a need to consume the results asynchronously. Instead of calling the subscribe method with a callback function, a Virtual Thread can simply call the block() method. The block method blocks the Virtual Thread until the result of the asynchronous call is received. The result may then be processed in a traditional style using imperative programming.
Standard JDBC Batching
The Oracle JDBC driver will start a database pipeline as soon as you start using the JDBC batching APIs i.e., stmt.addBatch() or prepstmt.addBatch(); the driver generates asynchronous calls under the covers, using the JDBC Reactive Extensions APIs.
2 — Java and the JSON-Relational Duality Views
Many applications require simplicity (self-describing, self-contained, and schema-less), ease of development (i.e., access JSON data programmatically), hierarchical data, common interchange format (i.e., easy to transmit across the network), and ease of conversion to Java types of JSON — and by the same token — the efficiency of space management, the queryability, consistency, and the powerful analytics and reporting capabilities of SQL . The JSON-relational duality views consist in presenting relational data as JSON documents. This feature gives developers the best of the Relational and JSON worlds. More details can be found here.
How to Setup and Use JSON-Relational Duality views?
- Create and populate the regular relational tables (or reuse existing ones). E.g., Students, Teachers, and Classes tables.
- Create one or several JSON-Relational Duality Views based on use cases, using either a SQL visual tool, a SQL CLI, or programmatically using JDBC Statement.execute(“ …”).
Each view consists in leveraging several capabilities. The following SQL code fragment maps base table columns to JSON name/values pairs.
The following SQL code extends the previous one using a subquery to assemble nested data into a JSON array.
The following SQL statement completes the previous ones by specifying update rules.
That’s it! You can now work directly with the JSON documents using the following query which returns John’s schedule as a JSON document.
Using the oracle.sql.json package in Oracle JDBC, you can retrieve the value of specific properties from John’s schedule.
JSON document transfer to the Java app can be performed either in binary JSON (OSON, suitable for large documents) or String (for small documents).
3 — JDBC Self-Driven Diagnosability
Every Java developer using the Oracle database will appreciate the following statement: “No more switching between the production jar and the debug jar to investigate an issue”!
A single ojdbc jar (e.g., ojdbc8.jar, ojdbc11.jar) for all use cases (production, debug, metrics). In other words, no more ojdbc8_g.jar or ojdbc11_g.jar for debugging, no more ojdbc8dms.jar or ojdbc11dms.jar for the Oracle Dynamic Monitoring Service (DMS) metrics, and no more ojdbc8dms_g.jar or ojdbc11dms_g.jar for DMS debugging.
Although the debug and metrics jars have been eliminated, the core JDBC jar furnishes enhanced logging, debugging (diagnose-on-first-failure), and execution tracing.
Logging: the core JDBC Jars (i.e., ojdbc8.jar or ojdbc11.jar) include the logging capabilities which need to be turned on using the following properties:
-Doracle.jdbc.diagnostic.enableLogging=true
in addition, the location of the logging configuration file
-Djava.util.logging.config.file=./logging.config
and the content of the logging configuration file
handlers = java.util.logging.ConsoleHandler
oracle.jdbc.level = FINEST
java.util.logging.ConsoleHandler.level = FINEST
java.util.logging.ConsoleHandler.formatter =
oracle.jdbc.diagnostics.OracleSimpleFormatter
Diagnose-on-first-failure: this feature diagnoses the first occurrence of a failure. It records the critical execution state in memory, then dumps the recording on error. It’s ON by default however, you may disable it either by setting the CONNECTION_PROPERTY_ENABLE_DIAGNOSE_FIRST_FAILURE property to FALSE (the default value) or via the DiagnosticMBeans interface.
For security, each diagnosability feature has two modes, public and sensitive. In the public mode, these features do not record or persist sensitive information. This severely limits the amount of information captured and reduces the effectiveness of the diagnosability features. In the sensitive mode, these features record and persist sensitive information. Java developers can share traces with the redaction of sensitive data. The sensitive mode can only be enabled by a privileged user and is controlled by two switches; one for enabling and another one for permitting. Only the permit property is required. If the permit property is included in the java command line then sensitive mode can be enabled and disabled programmatically at runtime, or via MBean. It can also be enabled via the enable property as shown.
-Doracle.jdbc.diagnostic.enableSensitiveDiagnostics=true
-Doracle.jdbc.diagnostic.permitSensitiveDiagnostics=true
You must configure java.util.logging (see above) to get diagnostic output on diagnose-on-first-failure.
Execution Trace: this feature records the execution sequence details.
The JDBC driver has also been instrumented with a hook for tracing database round trips; this hook enables the support of OpenTelemetry.
In a nutshell, OpenTelemetry is enabled using 3rd party libraries such as OpenTelemetry-JDBC without code change. All top-level calls can be monitored; the Java agent creates a span for JDBC calls.
To be covered in detail in an upcoming blog post.
DMS Metrics: The DMS metrics are recorded if the dms.jar is in the classpath.
Caution: you must balance the volume of state information recording against the cost of recording. As execution trace has a significant cost involved, you are advised to enable it only in limited contexts.
Other Java Goodies
- Native Boolean datatype in oracle.jdbc.OracleType.
- JDBC-Thin support for passwords up to 1024 bytes in length.
- JDBC-Thin generic support for token-based authentication including Oracle Cloud Infrastructure (OCI) Identity Access Management (IAM), and Azure Active Directory (Azure AD). See more here.
- JDBC-Thin support for RADIUS and RADIUS two-factor authentication where the RADIUS server asks for a valid response to a displayed challenge. See more here.
- JDBC Support for Directory-based Sharding and Split Partition Set.
No public Java API however, under the covers, during the partition-set split, the client-side connection pools receive ONS events about data in a chunk being split and moved across partition sets and update their sharding topology appropriately. - UCP Support for XA Transactions with Oracle Database Sharding. Java applications that use the UCP native data source in WebLogic Server to connect to Sharded Oracle databases can participate in JTA/XA transactions managed by the WebLogic Transaction Manager (TM).
- Oracle JVM Support for HTTP and TCP access while disabling other OS calls. See more details in Database Security in a Multitenant Environment.
Wrap Up
I trust that this post has inspired you to design new projects or re-architect existing ones, based on the new Java features in Oracle Database 23c Free — Developer Release.
Thank you for reading.
@kmensah #javaOracleDB http://oracle.com/jdbc