We need to re-think how JDBC drivers are detected to being “present” , as the current approach exposes a number of problems; I’m referring to, for example, [https://github.com/hibernate/hibernate-orm/blob/f2deb8f58e990ee87b6e00a736f6549d092da3d0/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLPGObjectJdbcType.java#L40-L60|https://github.com/hibernate/hibernate-orm/blob/f2deb8f58e990ee87b6e00a736f6549d092da3d0/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLPGObjectJdbcType.java#L40-L60|smart-link] and {{OracleArrayJdbcType}} is also an interesting examples example as it shows some more design issues. But we need a general pattern, and these two types I’ve called out here are only _examples_ of the issue I’m seeing; the pattern is rather wide-spread.
# Hibernate Reactive (for example) will use the PostgreSQL Dialect, but won’t use PGJDBC; there is an enum {{PostgreSQLDriverKind}} but it’s not protecting things effectively - for example some of the custom types are still having their class initialized (by accident?) even when it’s not PGJDBC, triggering warnings. # The Hibernate ORM’s classloader won’t necessarily match the application classloader; most commonly in application servers the application will have access to the JDBC driver, but the ORM will not. It’s also possible that the ORM’s classloader is unique while there are multiple applications using it, each on a different classloader - some of which will have access to the driver and some might not. # The example of the {{OracleArrayJdbcType}} exposes another issue: reflection is not defined upfront during class initialization, but a lazy function is being defined. Ideally if we could ensure all such functions were computed during metadata & Dialect initialization that would be great; alternatively such code will need to emit some kind of event to trigger GraalVM metadata registrations.
My suggestion would be to avoid storing such “presence flags” information in static, classloader bound state; especialy as the class definitions will be shared by multiple, different persistence units.
Also, one needs to be careful in separating the notion of “the class can be loaded” from “we’re actually connecting using that driver”; the two concepts are easily conflated, as they will be in our typical integration tests, but they need to be handled strictly separately.
For example it might be possible:
* to load types from the driver as it’s accessible, but we’re using a different database * we’re using the matching database (e.g. it matches via {{DatabaseMetaData}}#{{getDriverName}}), but the driver is not accessible
An aspect of concern is that the mismatch driver access / metadata would require some craft with integration tests.
Finally, since the pattern is rather wide-spread among a number of custom types, I wonder if we need some tooling help to spot such issues; e.g. reflective calls could theoretically be flagged by forbidden-apis, so to then specifically allow only the ones that have been vetted. |
|