Class Database

java.lang.Object
com.pyranid.Database

@ThreadSafe public final class Database extends Object
Main class for performing database access operations.
Since:
1.0.0
Author:
Mark Allen
  • Method Details

    • withDataSource

      public static @NonNull Database.Builder withDataSource(@NonNull DataSource dataSource)
      Provides a Database builder for the given DataSource.
      Parameters:
      dataSource - data source used to create the Database builder
      Returns:
      a Database builder
    • currentTransaction

      Gets a reference to the current transaction, if any.
      Returns:
      the current transaction
    • transaction

      public void transaction(@NonNull TransactionalOperation transactionalOperation)
      Performs an operation transactionally.

      The transaction will be automatically rolled back if an exception bubbles out of transactionalOperation.

      Nested calls to transaction(...) are independent transactions with independent JDBC connections; they do not automatically join an outer transaction. Use participate(Transaction, TransactionalOperation) to join an existing transaction explicitly. A transaction is scoped to the DataSource instance that created it; a Database using a different DataSource fails fast instead of joining it.

      Parameters:
      transactionalOperation - the operation to perform transactionally
    • transaction

      public void transaction(@NonNull TransactionOptions transactionOptions, @NonNull TransactionalOperation transactionalOperation)
      Performs an operation transactionally with the given options.

      The transaction will be automatically rolled back if an exception bubbles out of transactionalOperation.

      Nested calls to transaction(...) are independent transactions with independent JDBC connections; they do not automatically join an outer transaction. Use participate(Transaction, TransactionalOperation) to join an existing transaction explicitly. A transaction is scoped to the DataSource instance that created it; a Database using a different DataSource fails fast instead of joining it.

      Parameters:
      transactionOptions - options to apply to the transaction
      transactionalOperation - the operation to perform transactionally
      Since:
      4.2.0
    • transaction

      public <T> @NonNull Optional<T> transaction(@NonNull ReturningTransactionalOperation<T> transactionalOperation)
      Performs an operation transactionally and optionally returns a value.

      The transaction will be automatically rolled back if an exception bubbles out of transactionalOperation.

      Nested calls to transaction(...) are independent transactions with independent JDBC connections; they do not automatically join an outer transaction. Use participate(Transaction, ReturningTransactionalOperation) to join an existing transaction explicitly. A transaction is scoped to the DataSource instance that created it; a Database using a different DataSource fails fast instead of joining it.

      Type Parameters:
      T - the type to be returned
      Parameters:
      transactionalOperation - the operation to perform transactionally
      Returns:
      the result of the transactional operation
    • transaction

      public <T> @NonNull Optional<T> transaction(@NonNull TransactionOptions transactionOptions, @NonNull ReturningTransactionalOperation<T> transactionalOperation)
      Performs an operation transactionally with the given options, optionally returning a value.

      The transaction will be automatically rolled back if an exception bubbles out of transactionalOperation.

      Nested calls to transaction(...) are independent transactions with independent JDBC connections; they do not automatically join an outer transaction. Use participate(Transaction, ReturningTransactionalOperation) to join an existing transaction explicitly. A transaction is scoped to the DataSource instance that created it; a Database using a different DataSource fails fast instead of joining it.

      Type Parameters:
      T - the type to be returned
      Parameters:
      transactionOptions - options to apply to the transaction
      transactionalOperation - the operation to perform transactionally
      Returns:
      the result of the transactional operation
      Since:
      4.2.0
    • transactionWithRetry

      public @NonNull TransactionRetryResult<Void> transactionWithRetry(@NonNull RetryPolicy retryPolicy, @NonNull TransactionalOperation transactionalOperation)
      Performs an operation transactionally, retrying according to the given retry policy.

      The entire transaction closure may run more than once. Keep non-idempotent external side effects outside the closure unless they are safe to repeat.

      Pyranid consults the retry policy only after the database outcome is known to be rolled back. This includes a recognized serialization failure reported by physical commit when the follow-up rollback succeeds. Other commit failures and all rollback failures are terminal because their outcome is indeterminate.

      Unlike transaction(TransactionalOperation) and related transaction methods, retrying methods return TransactionRetryResult so callers can inspect failures that were recovered before success.

      This method fails fast if called inside an active transaction for this Database. Retrying a nested unit cannot restart the outer transaction safely.

      Parameters:
      retryPolicy - retry policy to apply
      transactionalOperation - the operation to perform transactionally
      Returns:
      retry result containing any failures retried before success
      Since:
      4.4.0
    • transactionWithRetry

      public @NonNull TransactionRetryResult<Void> transactionWithRetry(@NonNull RetryPolicy retryPolicy, @NonNull TransactionOptions transactionOptions, @NonNull TransactionalOperation transactionalOperation)
      Performs an operation transactionally with the given options, retrying according to the given retry policy.

      The entire transaction closure may run more than once. Keep non-idempotent external side effects outside the closure unless they are safe to repeat.

      Pyranid consults the retry policy only after the database outcome is known to be rolled back. This includes a recognized serialization failure reported by physical commit when the follow-up rollback succeeds. Other commit failures and all rollback failures are terminal because their outcome is indeterminate.

      Unlike transaction(TransactionOptions, TransactionalOperation) and related transaction methods, retrying methods return TransactionRetryResult so callers can inspect failures that were recovered before success.

      This method fails fast if called inside an active transaction for this Database. Retrying a nested unit cannot restart the outer transaction safely.

      Parameters:
      retryPolicy - retry policy to apply
      transactionOptions - options to apply to each transaction attempt
      transactionalOperation - the operation to perform transactionally
      Returns:
      retry result containing any failures retried before success
      Since:
      4.4.0
    • transactionWithRetry

      public <T> @NonNull TransactionRetryResult<T> transactionWithRetry(@NonNull RetryPolicy retryPolicy, @NonNull ReturningTransactionalOperation<T> transactionalOperation)
      Performs an operation transactionally and optionally returns a value, retrying according to the given retry policy.

      The entire transaction closure may run more than once. Keep non-idempotent external side effects outside the closure unless they are safe to repeat.

      Pyranid consults the retry policy only after the database outcome is known to be rolled back. This includes a recognized serialization failure reported by physical commit when the follow-up rollback succeeds. Other commit failures and all rollback failures are terminal because their outcome is indeterminate.

      Unlike transaction(ReturningTransactionalOperation) and related transaction methods, retrying methods return TransactionRetryResult so callers can inspect failures that were recovered before success.

      This method fails fast if called inside an active transaction for this Database. Retrying a nested unit cannot restart the outer transaction safely.

      Type Parameters:
      T - the type to be returned
      Parameters:
      retryPolicy - retry policy to apply
      transactionalOperation - the operation to perform transactionally
      Returns:
      retry result containing the successful transaction value and any failures retried before success
      Since:
      4.4.0
    • transactionWithRetry

      public <T> @NonNull TransactionRetryResult<T> transactionWithRetry(@NonNull RetryPolicy retryPolicy, @NonNull TransactionOptions transactionOptions, @NonNull ReturningTransactionalOperation<T> transactionalOperation)
      Performs an operation transactionally with the given options and optionally returns a value, retrying according to the given retry policy.

      The entire transaction closure may run more than once. Keep non-idempotent external side effects outside the closure unless they are safe to repeat.

      Pyranid consults the retry policy only after the database outcome is known to be rolled back. This includes a recognized serialization failure reported by physical commit when the follow-up rollback succeeds. Other commit failures and all rollback failures are terminal because their outcome is indeterminate.

      Unlike transaction(TransactionOptions, ReturningTransactionalOperation) and related transaction methods, retrying methods return TransactionRetryResult so callers can inspect failures that were recovered before success.

      This method fails fast if called inside an active transaction for this Database. Retrying a nested unit cannot restart the outer transaction safely.

      Type Parameters:
      T - the type to be returned
      Parameters:
      retryPolicy - retry policy to apply
      transactionOptions - options to apply to each transaction attempt
      transactionalOperation - the operation to perform transactionally
      Returns:
      retry result containing the successful transaction value and any failures retried before success
      Since:
      4.4.0
    • participate

      public void participate(@NonNull Transaction transaction, @NonNull TransactionalOperation transactionalOperation)
      Performs an operation in the context of a pre-existing transaction.

      No commit or rollback on the transaction will occur when transactionalOperation completes.

      However, if an exception bubbles out of transactionalOperation, the transaction will be marked as rollback-only.

      The transaction must have been created by this Database, or by another Database using the same DataSource instance.

      If this thread is interrupted while waiting for another participant to release the transaction connection, Pyranid restores the interrupt flag and throws DatabaseException.

      Parameters:
      transaction - the transaction in which to participate
      transactionalOperation - the operation that should participate in the transaction
    • participate

      public <T> @NonNull Optional<T> participate(@NonNull Transaction transaction, @NonNull ReturningTransactionalOperation<T> transactionalOperation)
      Performs an operation in the context of a pre-existing transaction, optionally returning a value.

      No commit or rollback on the transaction will occur when transactionalOperation completes.

      However, if an exception bubbles out of transactionalOperation, the transaction will be marked as rollback-only.

      The transaction must have been created by this Database, or by another Database using the same DataSource instance.

      If this thread is interrupted while waiting for another participant to release the transaction connection, Pyranid restores the interrupt flag and throws DatabaseException.

      Type Parameters:
      T - the type to be returned
      Parameters:
      transaction - the transaction in which to participate
      transactionalOperation - the operation that should participate in the transaction
      Returns:
      the result of the transactional operation
    • query

      public @NonNull Query query(@NonNull String sql)
      Creates a fluent builder for executing SQL.

      Named parameters use the :paramName syntax and are bound via Query.bind(String, Object). Positional parameters via ? are not supported. Pyranid ignores parameter-looking text inside SQL string literals, quoted identifiers, comments, PostgreSQL dollar-quoted strings, and SQL Server-style bracket-quoted identifiers. PostgreSQL JSONB/hstore ?, ?|, and ?& operators are supported; when running against DatabaseType.POSTGRESQL, Pyranid emits pgjdbc's escaped ?? form automatically. Unterminated quotes and comments fail fast.

      Example:

      Optional<Employee> employee = database.query("SELECT * FROM employee WHERE id = :id")
        .bind("id", 42)
        .fetchObject(Employee.class);
      
      Parameters:
      sql - SQL containing :paramName placeholders
      Returns:
      a fluent builder for binding parameters and executing
      Since:
      4.0.0
    • performHealthCheck

      public void performHealthCheck(@NonNull Duration timeout)
      Performs a portable connectivity check using JDBC Connection.isValid(int).

      This method borrows a fresh connection from this database's DataSource, calls Connection.isValid(int), and closes the connection before returning. It does not participate in an active Pyranid transaction, if one exists.

      JDBC accepts timeout values in whole seconds. Positive sub-second durations are rounded up to one second; Duration.ZERO passes a timeout of 0 to the driver.

      Parameters:
      timeout - maximum time to wait for driver validation
      Throws:
      IllegalArgumentException - if timeout is negative or too large for JDBC's integer-second timeout
      DatabaseException - if connection acquisition fails, validation throws, or the driver reports the connection is not valid
      Since:
      4.2.0
    • readDatabaseMetaData

      public void readDatabaseMetaData(@NonNull DatabaseMetaDataReader databaseMetaDataReader)
      Exposes a temporary handle to JDBC DatabaseMetaData, which provides comprehensive vendor-specific information about this database as a whole.

      This method acquires DatabaseMetaData on its own newly-borrowed connection, which it manages internally.

      It does not participate in the active transaction, if one exists.

      The connection is closed as soon as DatabaseMetaDataReader.read(DatabaseMetaData) completes.

      See DatabaseMetaData Javadoc for details.

    • useRawConnection

      public <T> @NonNull Optional<T> useRawConnection(@NonNull RawConnectionOperation<T> rawConnectionOperation)
      Performs raw JDBC work with a Pyranid-managed Connection.

      If called inside a Pyranid transaction, this operation uses the transaction's connection and participates in that transaction. Otherwise, Pyranid borrows a connection for the duration of the callback and closes it afterwards.

      The Connection passed to rawConnectionOperation is a guarded handle. Normal JDBC operations are delegated to the underlying driver connection, but lifecycle, transaction-management, and connection-wide state methods such as Connection.close(), Connection.commit(), Connection.rollback(), Connection.setAutoCommit(boolean), Connection.setCatalog(String), Connection.setSchema(String), and Connection.setNetworkTimeout(java.util.concurrent.Executor, int) throw IllegalStateException. Use Pyranid transaction APIs instead. Wrapper.unwrap(Class) may return a guarded, callback-scoped proxy for a vendor interface, but never a castable physical Connection; the proxy blocks lifecycle methods and expires with the callback. JDBC objects created from this handle are also guarded: Statement.getConnection() and DatabaseMetaData.getConnection() return the Pyranid-managed handle, and ResultSet.getStatement() returns a guarded statement. Guarded statements, resultsets, and metadata refuse driver-specific unwrap(...) calls that could expose the driver's underlying connection.

      The connection handle is valid only for the duration of the callback. Do not close it, retain it, or use it after this method returns.

      Type Parameters:
      T - the type to be returned
      Parameters:
      rawConnectionOperation - the raw JDBC operation to perform
      Returns:
      the operation result
      Throws:
      DatabaseException - if connection acquisition, callback execution, or cleanup fails
      Since:
      4.2.0
    • withNotificationSession

      public void withNotificationSession(@NonNull Set<@NonNull String> channels, @NonNull NotificationSessionOperation operation) throws InterruptedException
      Performs an operation with one callback-scoped database-notification listener session.

      This method is synchronous and blocking. It acquires at most one listener connection from this Database's configured DataSource, registers every requested channel, invokes operation at most once, expires the supplied NotificationSession, and completes cleanup before returning or throwing. It never reconnects.

      The configured source must preserve one physical backend session for the entire checkout. For PostgreSQL, direct connections and session pooling are suitable. Using PgBouncer transaction or statement pooling as the listener source is unsupported: registration can appear to succeed before backend-session affinity is lost and notification delivery silently stops. Pyranid does not inspect or validate proxy topology. Applications whose ordinary source cannot provide the required affinity should construct a separate Database over a suitable listener source and invoke this method on that instance.

      Notifications are lossy hints. Durable applications should normally reconcile authoritative state as the first callback action. The operation may use ordinary database methods, but those methods acquire or select their connection normally and never reuse the listener connection.

      A terminal receive failure is retained by the session and rethrown after cleanup even if operation catches it and returns. A retained transport Error propagates as that exact, unwrapped instance. If the callback instead throws a distinct Error, that callback error remains primary and the retained transport failure is suppressed beneath it.

      Parameters:
      channels - fixed, nonempty set of nonblank channels to register
      operation - operation to invoke after every channel has been registered
      Throws:
      NullPointerException - if channels, a channel, or operation is null
      IllegalArgumentException - if the set is empty or a channel violates common or backend-specific limits
      IllegalStateException - if any Pyranid transaction is active on the calling thread
      InterruptedException - if cooperative interruption wins after any required cleanup
      UnsupportedOperationException - if the database dialect or runtime driver cannot receive notifications
      DatabaseException - if connection acquisition, setup, receive, callback execution, or cleanup fails
      Since:
      4.6.0
    • withNotificationSession

      public void withNotificationSession(@NonNull String channel, @NonNull NotificationSessionOperation operation) throws InterruptedException
      Performs an operation with one callback-scoped database-notification listener session for a single channel.

      This is the single-channel convenience form of withNotificationSession(Set, NotificationSessionOperation). The listener connection comes from this Database's configured DataSource, which must preserve physical backend-session affinity for the entire checkout. For PostgreSQL, using PgBouncer transaction or statement pooling as the listener source is unsupported: registration can appear to succeed before affinity is lost and delivery silently stops. Pyranid does not inspect or validate proxy topology.

      A terminal receive failure is retained and rethrown after cleanup even if operation catches it and returns. A retained transport Error propagates as the exact, unwrapped instance unless a distinct callback Error takes precedence as described by the set-based overload.

      Parameters:
      channel - nonblank channel to register
      operation - operation to invoke after the channel has been registered
      Throws:
      NullPointerException - if channel or operation is null
      IllegalArgumentException - if the channel violates common or backend-specific limits
      IllegalStateException - if any Pyranid transaction is active on the calling thread
      InterruptedException - if cooperative interruption wins after any required cleanup
      UnsupportedOperationException - if the database dialect or runtime driver cannot receive notifications
      DatabaseException - if connection acquisition, setup, receive, callback execution, or cleanup fails
      Since:
      4.6.0
    • sendNotification

      public void sendNotification(@NonNull String channel)
      Sends a transient database notification without specifying a payload.

      Sending follows ordinary Pyranid statement and transaction selection. On PostgreSQL, a send inside a Pyranid transaction becomes visible only if that transaction commits. Payload representation is database-specific; PostgreSQL converts the resulting null payload to the empty string.

      Parameters:
      channel - nonblank notification channel
      Throws:
      NullPointerException - if channel is null
      IllegalArgumentException - if the channel violates common or backend-specific limits
      UnsupportedOperationException - if the database dialect does not support notification sends
      DatabaseException - if the send fails
      Since:
      4.6.0
    • sendNotification

      public void sendNotification(@NonNull String channel, @Nullable String payload)
      Sends a transient database notification.

      Sending follows ordinary Pyranid statement and transaction selection, including connection ownership, parameter binding and redaction, statement logging, timeout configuration, and metrics. Payload nullability and null/empty-string handling are database-specific; Pyranid performs no generic normalization.

      On PostgreSQL this executes bound pg_notify(?, ?) SQL. PostgreSQL converts a null payload to the empty string. Delivery occurs only after commit and is discarded by rollback; notification delivery itself remains non-durable and may be coalesced.

      Parameters:
      channel - nonblank notification channel
      payload - notification payload, possibly null or empty
      Throws:
      NullPointerException - if channel is null
      IllegalArgumentException - if the channel or payload violates common or backend-specific limits
      UnsupportedOperationException - if the database dialect does not support notification sends
      DatabaseException - if the send fails
      Since:
      4.6.0
    • isNotificationListeningSupported

      Reports whether the configured database dialect and currently loadable runtime adapter expose the APIs required to attempt a notification-listening session.

      This method resolves the full database type. If it has not been configured or cached, resolution may acquire a metadata connection and may throw DatabaseException. It does not acquire a listener session, inspect pool or proxy mode, prove backend-session affinity, unwrap a physical listener connection, or emit notification-session lifecycle metrics.

      Returns:
      true if notification listening can be attempted with the current dialect and runtime
      Throws:
      DatabaseException - if automatic database-type detection fails
      Since:
      4.6.0
    • getDatabaseType

      public @NonNull DatabaseType getDatabaseType()
      Gets the database type for this database.

      If Database.Builder.databaseType(DatabaseType) was not configured and the database type has not already been detected, this method may acquire a connection and inspect DatabaseMetaData. Configure an explicit database type to avoid runtime detection.

      Returns:
      the database type
      Throws:
      DatabaseException - if automatic database type detection fails
      Since:
      3.0.0
    • getTimeZone

      public @NonNull ZoneId getTimeZone()
      Since:
      3.0.0
    • getAmbiguousTimestampBindingStrategy

      How should Pyranid bind Instant and OffsetDateTime parameters when JDBC parameter metadata cannot identify whether the target is TIMESTAMP or TIMESTAMP WITH TIME ZONE?
      Returns:
      behavior to use when timestamp target metadata is unavailable or non-identifying
      Since:
      4.2.0
    • getParameterRedactor

      Gets the configured redactor used for non-secure parameters in diagnostics.
      Returns:
      the configured parameter redactor
      Since:
      4.4.0
    • getMetricsCollector