If this project saved you some time or made your day a little easier, a star would mean a lot — it helps others find it too.
Java library with some common DB API, a special JDBC version and a JPA version based on EclipseLink.
Licensed under the Apache 2.0 license.
Add the following to your pom.xml to use this artifact, where x.y.z is to be replaced with the last released version:
<dependency>
<groupId>com.helger.db</groupId>
<artifactId>ph-db-api</artifactId>
<version>x.y.z</version>
</dependency><dependency>
<groupId>com.helger.db</groupId>
<artifactId>ph-db-jdbc</artifactId>
<version>x.y.z</version>
</dependency><dependency>
<groupId>com.helger.db</groupId>
<artifactId>ph-db-jpa</artifactId>
<version>x.y.z</version>
</dependency><dependency>
<groupId>com.helger.db</groupId>
<artifactId>ph-db-flyway</artifactId>
<version>x.y.z</version>
</dependency>Note: prior to v8.0.0 the group ID was com.helger
All three implementation modules emit vendor neutral telemetry through the ph-telemetry facades Telemetry (tracing) and TelemetryMetrics (metrics).
Without a registered ITelemetryTracerSPI / ITelemetryMeterSPI, every span and every instrument degrades to a cheap no-op - so a deployment without an observability backend pays (almost) nothing.
To get real data, add ph-telemetry-otel (or any other SPI implementation) to your application and register it via META-INF/services.
All emitted names are constants of CDBTelemetry in ph-db-api, so dashboards, alerting rules and tests can reference the literally same strings.
Where the OpenTelemetry database semantic conventions define a name, that name is used verbatim (db.query.text, db.operation.name, db.response.returned_rows, db.system.name, db.namespace, error.type and the db.client.operation.duration instrument) - everything else is namespaced with phdb..
Note that the value of db.system.name is the ph-db EDatabaseSystemType ID (db2, h2, mysql, oracle, postgresql, sqlserver).
All duration instruments record seconds, because that is the unit the stable db.client.operation.duration convention prescribes.
| Span | Emitted by | Notes |
|---|---|---|
SELECT / INSERT / ... (db.query as fallback) |
DBExecutor |
one span per executed statement; the span name is the SQL operation |
phdb.jdbc.transaction |
DBExecutor.performInTransaction |
one span per transaction level, with phdb.jdbc.transaction.outcome = committed, rolled-back or nested |
phdb.jpa.transaction |
JPAEnabledManager.doInTransaction |
|
phdb.jpa.select |
JPAEnabledManager.doSelect / doSelectStatic |
|
phdb.flyway.migrate |
FlywayMigrationRunner.runFlyway |
one span per migration run |
| Instrument | Type | Unit |
|---|---|---|
db.client.operation.duration |
Histogram | s |
phdb.jdbc.statements |
Counter | {statement} |
phdb.jdbc.transactions |
Counter | {transaction} |
phdb.jdbc.connections |
Counter | {connection} |
phdb.jdbc.connections.active |
UpDownCounter | {connection} |
phdb.jdbc.connection.acquire.duration |
Histogram | s |
phdb.jpa.operations |
Counter | {operation} |
phdb.flyway.migrations |
Counter | {migration} |
phdb.flyway.migrate.duration |
Histogram | s |
Only bounded values are used as metric attributes - the SQL text is a span attribute only.
DBExecutor does not know which database it talks to, so the db.system.name attribute is only emitted if it was provided:
aExecutor.setDatabaseSystemType (EDatabaseSystemType.POSTGRESQL);The emission can be switched off per DBExecutor instance, and globally for all JPA operations:
// no spans and no metrics from this executor
aExecutor.setTelemetry (false);
// keep the spans, but never attach the SQL text
aExecutor.setTelemetrySQLText (false);
// no spans and no metrics from any JPAEnabledManager
JPAEnabledManager.setTelemetryEnabled (false);Prepared statements only carry the parameterized SQL text, but the SQL passed to DBExecutor.executeStatement (String) and DBExecutor.queryAll (String) may contain literal values - use setTelemetrySQLText (false) if such values must not leave the process.
v8.5.0 - 2026-09-06
- Added optional ph-telemetry support to
ph-db-jdbc,ph-db-jpaandph-db-flyway. Without a registered telemetry SPI, all emission degrades to cheap no-ops.DBExecutoremits a span per executed statement (named after the SQL operation, as the OpenTelemetry conventions demand), a span per transaction, and the metricsdb.client.operation.duration,phdb.jdbc.statements,phdb.jdbc.transactions,phdb.jdbc.connections,phdb.jdbc.connections.activeandphdb.jdbc.connection.acquire.duration.JPAEnabledManageremits a span perdoInTransaction/doSelectplus the metricsdb.client.operation.durationandphdb.jpa.operations.FlywayMigrationRunneremits a span per migration run plus the metricsphdb.flyway.migrationsandphdb.flyway.migrate.duration. All emitted span, metric and attribute names are constants of the new classCDBTelemetryinph-db-api. The OpenTelemetry database semantic conventions are followed where they define a name; everything else is namespaced withphdb.. All duration instruments use seconds as their unit, becausedb.client.operation.durationprescribes it. - Added
DBExecutor.setDatabaseSystemType (EDatabaseSystemType)to provide thedb.system.nametelemetry attribute - the attribute is omitted if it is not set. - Added
DBExecutor.setTelemetry (boolean)andDBExecutor.setTelemetrySQLText (boolean)as well as the staticJPAEnabledManager.setTelemetryEnabled (boolean)to disable the telemetry emission. All of them default to enabled. DisablesetTelemetrySQLTextif the SQL passed toexecuteStatementorqueryAllmay contain literal values that must not leave the process - prepared statements only carry the parameterized SQL text anyway. - Fixed the transaction boundaries of
DBExecutor.performInTransaction- thanks to @vinit-thummar for reporting and fixing it in issue #2. Every statement and every query executed inside a transaction committed the shared connection right after it was executed, so a later rollback could not undo the already committed work, and the intermediate states of a transaction became visible to concurrent readers on other connections. Commit and rollback now only happen at the outermost transaction boundary. Note that a failed SQL operation or a failed nested transaction now marks the whole transaction for rollback, even if the caller evaluates the returnedESuccessor the updated row count and continues instead of throwing - so a "try the insert, and on failure do the update instead" pattern no longer works inside a transaction, because no savepoints are used. As before,performInTransactionrequires a connection with auto-commit disabled, and a nestedperformInTransactionjoins the outer transaction instead of starting a new one. Because a connection with auto-commit enabled silently degrades a transaction to no transaction at all - every statement is committed on its own and the rollback has no effect - this case is now logged as an error when the outermost transaction is started.
v8.4.3 - 2026-09-03
- Added the new overloads
DBPagingHelper.getOrderByClause (IPagingSpec, IDBColumnNameResolver, Iterable)andgetOrderByAndPagingClause (EDatabaseSystemType, IPagingSpec, IDBColumnNameResolver, Iterable), that take the default sort fields to be used if the paging specification contains no usable one - because none was requested, or because none could be resolved. Providing them is strongly recommended whenever the result is paged: without anORDER BYa query returns the rows of a page in an undefined order, so that consecutive pages may overlap or lose rows. Contrary to the requested sort fields, which come from a client, the default sort fields come from the application and are therefore expected to be resolvable - an unresolvable default sort field is logged on the error level instead of the warning level.
v8.4.2 - 2026-08-30
- Requires at least ph-commons 12.4.0
- Added the new package
com.helger.db.api.paginginph-db-api, that creates the SQL clauses for a paged and sorted query from the data store independentIPagingSpecof ph-commons 12.4.0.DBPagingHelper.getPagingClause (EDatabaseSystemType, IPagingSpec)creates the database system specific clause to limit a query to a single page -LIMIT .. OFFSET ..for MySQL and the SQL standardOFFSET .. ROWS FETCH NEXT .. ROWS ONLYfor DB2, H2, Oracle, PostgreSQL and SQL Server. "All rows starting at an offset" is supported as well; for MySQL it uses the documented workaround of a very large row count (DBPagingHelper.MYSQL_ALL_ROWS).DBPagingHelper.getOrderByClause (IPagingSpec, IDBColumnNameResolver)creates the matchingORDER BYclause, andgetOrderByAndPagingClause (...)combines the two and warns if paging is applied without an order, because which rows a page contains is undefined that way. The new interfaceIDBColumnNameResolvermaps the logical field name of aSortFieldonto the SQL column expression. It is the security boundary of the whole sorting: the field names typically originate from a UI and are therefore attacker controlled, while the returned expression ends up in the statement verbatim, because a column cannot be a JDBC parameter. Unknown field names are ignored, andIDBColumnNameResolver.createFromMap (Map)provides a whitelist based implementation. Note that a paging specification requesting 0 rows is rejected with anIllegalArgumentException, because there is no portable SQL for it - such a query should not be executed at all.
v8.4.1 - 2026-07-17
JdbcConfigurationConfignow logs the deprecation warning for a legacy*.millis/*.msconfiguration key only once per key, instead of on every access.- Security:
H2Helper.buildJDBCStringnow rejects connection property names containing;or=and property values containing;, preventing injection of additional H2 URL settings (e.g.INIT=RUNSCRIPT). - Security:
AbstractGlobalEntityManagerFactoryno longer includes the plaintext JDBC password in itsIllegalStateExceptionmessages; the property map is now masked before being logged. - Security: added
JDBCHelper.getMaskedConnectionString(String)which maskspassword=/pwd=values in a JDBC connection string. It is now applied when logging JDBC URLs inAbstractGlobalEntityManagerFactory,DataSourceProviderFromJdbcConfigurationandLoggingH2EventListener. - Extended
IFlywayConfigurationwith thevalidateOnMigrateflag (defaultfalse), configurable viaFlywayConfigurationBuilderConfigkeyvalidate-on-migrate. Previously Flyway validation was unconditionally disabled. DBExecutornow fails fast with anIllegalStateExceptionif an instance that has an open transaction is used concurrently from another thread, instead of silently executing on the foreign transaction's connection.- Fixed
ConnectionFromDataSource.setValidityCheckTimeoutSecondsclamping any positive timeout to0; it now correctly keeps positive values and floors negatives at0. The connection is now also closed when the validity check fails, avoiding a connection pool leak. - Fixed a
NullPointerExceptioninAbstractGlobalEntityManagerFactorywhen the optional additional factory properties map wasnull.
v8.4.0 - 2026-05-01
- Updated to Flyway 12.5.0
IExecutionTimeExceededCallback.onExecutionTimeExceedednow takesDurationparameters (aExecutionDuration,aLimitDuration) instead oflongmilliseconds — breaking signature change.LoggingExecutionTimeExceededCallbackwas updated accordingly.DBExecutornow stores the execution duration warning threshold asjava.time.Durationinternally. Added new primary settersetExecutionWarnDuration(Duration)and gettergetExecutionWarnDuration(); the previously namedsetExecutionDurationWarn(Duration)/getExecutionDuration()/isExecutionDurationWarnEnabled()and the millis-basedsetExecutionDurationWarnMS(long)/getExecutionDurationWarnMS()are retained as@Deprecated(forRemoval = true)and delegate to the new methods. The newisExecutionWarnDurationEnabled()requires a strictly positive duration (> 0).DBExecutor.onExecutionTimeExceeded(String, long)was changed toonExecutionTimeExceeded(String, Duration)to match the new callback signature.JPAEnabledManager.onExecutionTimeExceeded(String, long)was changed toonExecutionTimeExceeded(String, Duration). Added newgetDefaultExecutionWarnDuration()andsetDefaultExecutionDuration(Duration)plus aDEFAULT_EXECUTION_WARN_DURATIONconstant; the existinggetDefaultExecutionWarnTime(),setDefaultExecutionWarnTime(int)andDEFAULT_EXECUTION_WARN_TIME_MSare now@Deprecated(forRemoval = true).
v8.3.0 - 2026-05-01
- Removed OSGI bundling
JdbcConfigurationConfignow accepts the duration grammar from ph-commons 12.2.5 (ConfigDurationParser) on five new configuration keys:execution-time-warning,pooling.max-wait,pooling.between-evictions-runs,pooling.min-evictable-idle,pooling.remove-abandoned-timeout. Values like5s,2m,1h 30mare parsed tojava.time.Duration. The legacy*.millis/*.mskeys remain supported for backward compatibility; the duration key wins when both are set, and a parse failure on the duration key falls back to the legacy key.- Added five
java.time.Duration-typed accessors onIJdbcDataSourceConfiguration/IJdbcConfiguration:getJdbcPoolingMaxWait(),getJdbcPoolingBetweenEvictionRuns(),getJdbcPoolingMinEvictableIdle(),getJdbcPoolingRemoveAbandonedTimeout(),getJdbcExecutionTimeWarning(). The Duration getters are now the primary API; the existinggetJdbc*Millis()long-millis getters are retained as@Deprecatedthin wrappers. JdbcConfiguration(POJO) now stores its five duration values asDurationinternally. A new primary constructor acceptsDurationparameters; the existing long-millis constructor is@Deprecatedand delegates to it. Added matchingDEFAULT_*Durationconstants alongside the existingDEFAULT_*_MILLISlongs.DataSourceProviderFromJdbcConfigurationnow consumes the newDurationaccessors directly, removing theDuration.ofMillis(...)wrappers around millis getters.- The legacy
*.millis/*.msconfiguration keys, the correspondingSUFFIX_*_MILLIS/SUFFIX_*_MSconstants, thegetConfigKey*Millis*()accessors, and thegetJdbc*Millis()getters onIJdbcDataSourceConfiguration/IJdbcConfiguration/JdbcConfiguration/JdbcConfigurationConfigare now@Deprecated. A WARN-level log message is emitted at runtime when a legacy*.millis/*.msconfiguration key is read, pointing at the new duration-grammar key.
v8.2.1 - 2026-04-12
- Extended
IJdbcDataSourceConfiguration,JdbcConfigurationandJdbcConfigurationConfigwith thetestOnBorrowpooling parameter
v8.2.0 - 2026-04-12
- Added new submodule
ph-db-flywaywithFlywayMigrationRunnerutility class for shared Flyway database migration setup - Moved Flyway configuration classes (
FlywayConfiguration,FlywayConfigurationBuilderConfig,IFlywayConfiguration) fromph-db-api(com.helger.db.api.flyway) toph-db-flyway(com.helger.db.flyway) — breaking package change - Extended
IFlywayConfigurationwithdebugModeandrepairModeflags
v8.1.3 - 2026-04-07
- Extended
IJdbcConfiguration,JdbcConfigurationandJdbcConfigurationConfigwith connection pooling parameters: max connections, max wait, between eviction runs, min evictable idle, and remove abandoned timeout DataSourceProviderFromJdbcConfigurationnow directly handles the pooling parameter in a standard way- Added optional
flywayHistoryTableparameter toIFlywayConfigurationfor customizing the Flyway history table name
v8.1.2 - 2026-02-22
- Updated to Apache Commons Pool 2.13.1
- Updated to Apache Commons DBCP 2.14.0
- Updated to EclipseLink 4.0.9
DBResultFieldconstructor now takes an empty String as well- Renamed
EDatabaseSystemType.MSSQLtoSQLSERVER
v8.1.1 - 2025-12-10
- Added specific support for Oracle Timestamp handling
v8.1.0 - 2025-11-16
- Updated to ph-commons 12.1.0
- Using JSpecify annotations
v8.0.1 - 2025-09-19
- Added new class
DBSystemHelper
v8.0.0 - 2025-08-25
- Requires Java 17 as the minimum version
- Updated to ph-commons 12.0.0
- Updated to MySQLConnector/J 9.4.0
- Changed the Maven group ID from
com.helgertocom.helger.db
v7.1.0 - 2025-04-11
- Updated to Apache Commons Pool 2.12.1
- Added new enum
EDatabaseSystemType - Added new package
com.helger.db.api.flyway - Added new package
com.helger.db.api.config
v7.0.6 - 2024-09-20
- Updated to Protobuf 4.28.2 to fix CVE-2024-7254
v7.0.5 - 2024-08-09
- Updated to MySQLConnector/J 9.0.0
- Updated to Protobuf 4.x
v7.0.4 - 2024-03-27
- Updated to ph-commons 11.1.5
- Updated to MySQLConnector/J 8.3.0
- Updated to Apache Commons DBCP 2.12.0
- Created Java 21 compatibility
v7.0.3 - 2023-12-10
- Updated all dependencies
v7.0.2 - 2023-07-31
- Updated to ph-commons 11.1
v7.0.1 - 2023-01-12
- Updated to MySQLConnector/J 8.0.31 with new Maven coordinates
- Updated to Protobuf 3.21.12
v7.0.0 - 2023-01-09
- Using Java 11 as the baseline
- Updated to ph-commons 11
- Updated to EclipseLink 4.0.0
v6.7.4 - 2022-02-21
- Updated to H2 2.0.210
- Fixed a
NullPointerExceptionin CLOB handling
v6.7.3 - 2021-11-24
- Updated to MySQLConnector/J 8.0.25
- Added new class
DBValueHelper - Added new class
AbstractJDBCEnabledManager
v6.7.2 - 2021-09-19
- Updated to Apache Commons Pool 2.11.1
- Extended the
DBExecutorAPI slightly
v6.7.1 - 2021-08-20
- Updated to ph-commons 10.1
- Updated to MySQLConnector/J 8.0.25
- Updated to Apache Commons DBCP 2.9.0
- Updated to Apache Commons Pool 2.10.0
- Extended
DBResultRowwith additional methods
v6.7.0 - 2021-04-06
- Added option to enabled/disable the connection state handling (and disabled it by default)
v6.6.0 - 2021-03-21
- Updated to ph-commons 10
- Updated to EclipseLink 2.7.8
- Updated to MySQLConnector/J 8.0.23
- Added new class
ConnectionFromDataSourcethat has increased flexibility
v6.5.0 - 2020-11-27
- Added conversion from CLOB to String - thanks to GG
- Commented out the parameter check in favour of the default JDBC driver - thanks to GG for pointing that out
- Removed the usage of
Optionalin theDBExecutorto more easily differentiate between "Error" and "Not found"
v6.4.0 - 2020-11-02
- Updated to MySQLConnector/J 8.0.21
- Improved debug logging in
DBExecutor - Made
DBExecutorconsistently not thread-safe - Made some
DBExecturmethods static
v6.3.1 - 2020-09-30
- Updated to Apache Commons Pool 2.9.0
- Updated to Apache Commons DBCP 2.8.0
v6.3.0 - 2020-08-24
- Renamed
AbsractConnectortoAbstractDBConnector - Removed
AbstractDBConnector.getDatabaseName - Added class
AbstractDBConnector - Dropped some specific connector implementations
v6.2.1 - 2020-08-20
- Updated to EclipseLink 2.7.7
- Updated to Apache Commons Pool 2.8.1
- Updated DBResultRow API
v6.2.0 - 2020-04-23
- Updated to Apache Commons Pool 2.8.0
- Updated to MySQLConnector/J 8.0.19
- Updated to EclipseLink 2.7.6
- Extended JDBCHelper return types
- Added simple transaction support in
DBExecutor - Updated to ph-commons 9.4.1
v6.1.5 - 2019-10-25
- Updated to Apache Commons Pool 2.7.0
- Updated to Apache Commons DBCP 2.7.0
- Updated to MySQLConnector/J 8.0.18
- Updated to H2 1.4.200
- Updated to EclipseLink 2.7.5
- The
EclipseLinkLoggerlogs all error levels belowWARNINGasInfo
v6.1.4 - 2019-03-27
- Updated to H2 1.4.199
- Replacing "javax.persistence 2.2.1" with "jakarta.persistence 2.2.2"
v6.1.3 - 2019-03-12
- Updated to EclipseLink 2.7.4
- Updated to MySQLConnector/J 8.0.15
- Updated to Apache Commons Pool 2.6.1
- Updated to Apache Commons DBCP 2.6.0
- Updated to H2 1.4.198
v6.1.2 - 2018-11-22
- Updated to EclipseLink 2.7.3
- Updated to MySQLConnector/J 8.0.13
- Updated to ph-commons 9.2.0
v6.1.1 - 2018-07-24
- Fixed OSGI ServiceProvider configuration
- Updated to EclipseLink 2.7.2
- Updated to Apache Commons DBCP 2.5.0
- Updated to Apache Commons Pool 2.6.0
- Catching an throwing Exception only (instead of Throwable)
v6.1.0 - 2018-04-23
- Updated to Apache Commons DBCP 2.2.0
- Updated to EclipseLink 2.7.1
JPAEnabledManagernow has the possibility to disable the execution time warning
v6.0.0 - 2017-12-20
- Updated to ph-commons 9.0.0
- Updated to H2 1.4.196
- Updated to EclipseLink 2.7.0
- Updated to Apache Commons Pool2 2.5.0
v5.0.1 - 2016-08-21
- Updated to ph-commons 8.4.x
v5.0.0 - 2016-06-11
- Requires at least JDK8
My personal Coding Styleguide | It is appreciated if you star the GitHub project if you like it.