"""
+ }
+
+ emailext(
+ subject: subject,
+ body: body,
+ to: buildEnv.notificationRecipients
+ )
+ }
+}
+
+@NonCPS
+String getParallelResult( RunWrapper build, String parallelBranchName ) {
+ def visitor = new PipelineNodeGraphVisitor( build.rawBuild )
+ def branch = visitor.pipelineNodes.find{ it.type == FlowNodeWrapper.NodeType.PARALLEL && parallelBranchName == it.displayName }
+ if ( branch == null ) {
+ echo "Couldn't find parallel branch name '$parallelBranchName'. Available parallel branch names:"
+ visitor.pipelineNodes.findAll{ it.type == FlowNodeWrapper.NodeType.PARALLEL }.each{
+ echo " - ${it.displayName}"
+ }
+ return null;
+ }
+ return branch.status.result
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index dc84c8ec51c1..fabf623322a5 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,7 @@ It also provides an implementation of the JPA specification, which is the standa
This is the repository of its source code: see [Hibernate.org](https://hibernate.org/orm/) for additional information.
-[](https://ci.hibernate.org/job/hibernate-orm-main-h2-main/)
-[](https://lgtm.com/projects/g/hibernate/hibernate-orm/context:java)
+[](https://ci.hibernate.org/job/hibernate-orm-pipeline/job/5.6/)
Building from sources
=========
diff --git a/build.gradle b/build.gradle
index 15ab92bf1c80..df33316ae2bd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,25 +14,17 @@ buildscript {
classpath 'org.hibernate.build.gradle:hibernate-matrix-testing:3.0.0.Final'
classpath 'org.hibernate.build.gradle:version-injection-plugin:1.0.0'
classpath 'gradle.plugin.com.github.lburgazzoli:gradle-karaf-plugin:0.5.1'
- classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.7'
+ classpath 'org.asciidoctor:asciidoctor-gradle-jvm:3.3.2'
classpath 'de.thetaphi:forbiddenapis:3.0.1'
}
}
plugins {
id 'me.champeau.buildscan-recipes' version '0.2.3'
- id 'io.github.gradle-nexus.publish-plugin' version '1.1.0'
- id 'nu.studer.credentials' version '2.1'
id 'org.hibernate.build.xjc' version '2.0.1' apply false
- id 'org.hibernate.build.maven-repo-auth' version '3.0.3' apply false
id 'biz.aQute.bnd' version '5.1.1' apply false
}
-ext {
- sonatypeOssrhUser = project.findProperty( 'SONATYPE_OSSRH_USER' )
- sonatypeOssrhPassword = project.findProperty( 'SONATYPE_OSSRH_PASSWORD' )
-}
-
File versionFile = file( "${rootProject.projectDir}/gradle/version.properties" )
ext {
@@ -51,15 +43,6 @@ ext {
group = 'org.hibernate'
version = project.ormVersion.fullName
-nexusPublishing {
- repositories {
- sonatype {
- username = project.sonatypeOssrhUser
- password = project.sonatypeOssrhPassword
- }
- }
-}
-
allprojects {
repositories {
mavenCentral()
diff --git a/changelog.txt b/changelog.txt
index 35e957f9c6f1..61d9ea5793f6 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -3,6 +3,274 @@ Hibernate 5 Changelog
Note: Please refer to JIRA to learn more about each issue.
+Changes in 5.6.15.Final (February 06, 2023)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32121
+
+** Bug
+ * [HHH-16049] - Setting a property to its current value with bytecode enhancement enabled results in unnecessary SQL Update in some (many) cases
+ * [HHH-15665] - Mariadb is missing identifier quote on SEQUENCE QUERY
+ * [HHH-15618] - Procedure should accept TypedParameterValue as parameter
+
+** Improvement
+ * [HHH-15693] - Introduce a fast-path access for ClassLoaderService being retrieved from ServiceRegistry
+ * [HHH-15690] - HQLQueryPlan to have a direct reference to QueryTranslatorFactory
+ * [HHH-15685] - Improve efficiency of Dialect lookup in Loader and HqlSqlWalker
+
+** Patch
+ * [HHH-15792] - Explicitly add JavaDoc to make @deprecated hint for createSQLQuery visible in Eclipse
+
+
+Changes in 5.6.14.Final (November 04, 2022)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32120
+
+** Improvement
+ * [HHH-15662] - ClasscastException caused by check for Managed rather than ManagedEntity
+
+
+Changes in 5.6.13.Final (November 03, 2022)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32112
+
+** Bug
+ * [HHH-15634] - Lazy basic property does not get updated on change
+ * [HHH-15561] - Function "IDENTITY" not found when inserting audited revision using Hibernate Envers
+ * [HHH-15554] - Merge of an Entity with an immutable composite user type throws Exception
+
+** Improvement
+ * [HHH-15649] - Additional performance fixes relating to Klass's _secondary_super_cache interaction with entity enhancement
+ * [HHH-15639] - Upgrade to ByteBuddy 1.12.18
+ * [HHH-15637] - Upgrade to Byteman 4.0.20
+ * [HHH-15616] - Mitigate performance impact of entity enhancement on Klass's _secondary_super_cache
+ * [HHH-15585] - Add support for DB2 aliases for schema validation
+ * [HHH-15575] - Make getter org.hibernate.criterion.SimpleExpression#getOp() public
+
+** Task
+ * [HHH-15594] - Remove Oracle RDS and all test matrix uses
+
+
+Changes in 5.6.12.Final (September 27, 2022)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32105
+
+** Bug
+ * [HHH-15523] - Missing use of SqlStringGenerationContext in MapBinder#getFromAndWhereFormula
+ * [HHH-15522] - Hibernate.isInitialized method not working for Envers Collections
+ * [HHH-15520] - ValueGeneration on @OneToOne leads to boot error
+ * [HHH-15505] - Getter of loaded entity returns null when using bytecode enhancement on entity whose field is defined both in mapped superclass and concrete entity
+ * [HHH-15235] - PropertyAccessException on OneToOne mapping after migration to Hibernate 5.6
+ * [HHH-15216] - Cannot change MetadataProvider implementation because JPAXMLOverriddenMetadataProvider is final and precisely expected by a cast operator
+ * [HHH-15045] - onFlushDirty() invoked on parent entity in a @OneToOne relationship when no table columns are changed
+ * [HHH-14943] - byNaturalId API creates unparseable query (AND keyword instead of WHERE)
+
+** Deprecation
+ * [HHH-15536] - Deprecate SharedSessionContractImplementor#getTransactionStartTimestamp() and CacheTransactionSynchronization#getCurrentTransactionStartTimestamp()
+
+** New Feature
+ * [HHH-15508] - Backport Session#getReference(Object) to branch 5.6
+
+** Task
+ * [HHH-15555] - Remove use of @AutomaticFeature in GraalVM module
+ * [HHH-15538] - Move Jenkinsfile timeout around shell command
+
+
+Changes in 5.6.11.Final (August 30, 2022)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32089
+
+** Bug
+ * [HHH-15468] - contributor-build.yml has no explicit permissions set
+ * [HHH-15454] - Primitive type requested from tuple throws exception
+ * [HHH-15440] - @OneToOne and @OptimisticLock(excluded = true) not working correctly
+ * [HHH-15425] - org.hibernate.QueryException: could not resolve property is thrown when Hibernate criteria tries to select the id of an association annotated with @NotFound
+ * [HHH-15359] - The entity returned by a merge doesn't contain @ManyToMany relation when the collection resides in @Embeddable
+ * [HHH-15100] - Limitation of metamodel imports cache causes severe performance drops in large projects
+
+** Improvement
+ * [HHH-15466] - Compatibility with Jandex 3.0.0
+
+** Task
+ * [HHH-15451] - Upgrade PostgreSQL JDBC driver to 42.5.0
+ * [HHH-15388] - Upgrade to Micrometer 1.9.3
+
+
+Changes in 5.6.10.Final (July 07, 2022)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32076
+
+** Bug
+ * [HHH-15281] - INSERTs/UPDATEs no longer executed as JDBC Batch statements if hibernate.temp.use_jdbc_metadata_defaults is set to false
+ * [HHH-15218] - @OptimisticLocking(DIRTY) leads to wrong query during delete of circular reference
+ * [HHH-7525] - @Formula annotation with native query returning entity value causes NullPointerException
+
+** Improvement
+ * [HHH-15325] - Avoid allocations from BitSet.stream() in AbstractEntityPersister.resolveDirtyAttributeIndexes()
+
+** Task
+ * [HHH-15322] - Allow JNDI lookups using the osgi scheme
+
+
+Changes in 5.6.9.Final (May 14, 2022)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32067
+
+** Bug
+ * [HHH-15270] - Inconsistent precedence of orm.xml implicit catalog over "default_catalog" in XML-mapped entities
+ * [HHH-15265] - SchemaExport.execute does not add the configured schema to comments
+ * [HHH-15212] - SchemaExport.execute does not replace the ${schema}-placeholder in HBM database-object with configured schema
+ * [HHH-15142] - CriteriaQuery with Like predicate fails when repeated with java.lang.IllegalArgumentException: Parameter value [] did not match expected type [java.lang.String (n/a)]
+ * [HHH-15134] - Update a bytecode enhanced Entity with a Version attribute causes OptimisticLockException
+ * [HHH-15091] - EntityManager.persist does not verify the existence of the one side of a many-to-one relationship, introduced 5.4.17
+
+** Improvement
+ * [HHH-4384] - @JoinColumn must be set for @AssociationOverride to work
+
+** Task
+ * [HHH-15274] - Small optimisation for how LazyAttributeLoadingInterceptor is dealing with lazy fields
+ * [HHH-15222] - Introduce an helper class SPI for decorating a Session instance when the instance is lazily provided
+ * [HHH-15178] - Backport Jenkinsfile and GH actions
+
+
+Changes in 5.6.8.Final (April 13, 2022)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32056
+
+** Bug
+ * [HHH-15147] - hibernate-jpamodelgen-jakarta annotation processor ignores jakarta.* annotations
+ * [HHH-15141] - Bytecode enhancement fails for a protected, embedded field in a MappedSuperclass from a different package than the entity
+ * [HHH-15118] - PooledOptimizer generates duplicate ids when several JVMs initialize optimizer and sequence value is the initial value
+ * [HHH-14487] - PropertyAccessStrategyMapImpl imports wrong class
+ * [HHH-13694] - Numeric Overflow Exception when retrieving the Meta-data for sequences from Oracle Database
+
+** Task
+ * [HHH-15209] - Upgrade to bytebuddy 1.12.9
+ * [HHH-15146] - Run tests against hibernate-jpamodelgen-jakarta
+
+
+Changes in 5.6.7.Final (March 16, 2022)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32053
+
+** Improvement
+ * [HHH-15124] - Relax usage of DeprecationLogger: avoid some confusing reports
+ * [HHH-15067] - Make NonNullableTransientDependencies.(String propertyName, Object transientEntity) method public
+
+
+Changes in 5.6.6.Final (March 15, 2022)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32031
+
+** Bug
+ * [HHH-15115] - Deleting an entity with Joined inheritance and default schema set is throwing and error
+ * [HHH-15113] - Exception setting ParameterExpressions on Update Queries
+ * [HHH-15105] - Getting the CacheRegionStatistics before executing a query leads to a NPE later on
+ * [HHH-15097] - Hibernate fails to detect SQL type for AttributeConverter to UUID
+ * [HHH-15084] - JpaCompliantLifecycleStrategy uses deprecated BeanManager method that's gone in CDI 4.0
+ * [HHH-15082] - JDBC Statement leaks after exceptions other than SQLException during insert/update/...
+ * [HHH-15069] - Backwards-incompatible changes in SequenceStyleGenerator (and others) following default_schema changes
+ * [HHH-15060] - Fix handling of associations with @NotFound
+ * [HHH-15051] - Association with id class misses property mapping for target FK attributes
+ * [HHH-14932] - Spatial support for PostgreSQL 10+ uses invalid WKB dialect
+ * [HHH-14817] - hibernate-core-jakarta source jar does not contain source
+ * [HHH-15090] - Access to public field with extended bytecode enhancement returns null for entity lazy-loaded from polymorphic toOne association
+
+** Improvement
+ * [HHH-15106] - fk() SQM function
+ * [HHH-15094] - Handle http://hibernate.org and https://* for all DTDs in LocalXmlResourceResolver
+
+** Task
+ * [HHH-15119] - Upgrade to ByteBuddy 1.12.8
+ * [HHH-14996] - Upgrade to JBoss Logging Processor (and matching Annotations) 2.2.1.Final
+
+
+Changes in 5.6.5.Final (January 25, 2022)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32029
+
+** Bug
+ * [HHH-15044] - Revert HHH-14826 fix because the provided test was wrong
+ * [HHH-15041] - H2Dialect does not work properly with h2 2.0.202 due to new DDL type requirements
+ * [HHH-15014] - H2Dialect does not work properly with h2 2.0.202 on sub selects with tuples
+ * [HHH-15009] - H2Dialect does not work properly with h2 2.0.202 and updating schema
+ * [HHH-14985] - H2Dialect does not work properly with h2 2.0.202 on inserts
+
+
+Changes in 5.6.4.Final (January 19, 2022)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32012
+
+** Bug
+ * [HHH-15032] - Fix backwards incompatible SPI change that happened in 5.6.2 due to introducing SqlStringGenerationContext
+ * [HHH-15022] - Bug After Upgrade Hibernate from 5.6.1.Final to 5.6.3.Final
+ * [HHH-15002] - H2Dialect does not work properly with h2 2.0.202 and booleans types
+
+** Task
+ * [HHH-15036] - Disable DefaultCatalogAndSchemaTest when testing against MariaDB < 10.3
+ * [HHH-15033] - Restrict JNDI lookups to "java" scheme
+ * [HHH-15031] - Upgrade to ByteBuddy 1.12.7
+ * [HHH-15028] - Upgrade to JBoss Logging 3.4.3.Final
+ * [HHH-15026] - Upgrade to Log4J 2.17.1
+ * [HHH-15024] - Upgrade to Jandex 2.4.2.Final
+ * [HHH-15018] - OracleTypesHelper shouldn't log stacktraces when the Oracle JDBC driver isn't loadable
+ * [HHH-14998] - Upgrade to GraalVM 21.3.0
+ * [HHH-14988] - Upgrade to ByteBuddy 1.12.5
+ * [HHH-14987] - Upgrade to Log4j 2.17.0
+
+
+Changes in 5.6.3.Final (December 15, 2021)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32006
+
+** Bug
+ * [HHH-14972] - log4j2 <= 2.14.1 has an RCE (CVE-2021-44228)
+ * [HHH-14948] - Metamodel imports cache increases indefinitely for dynamically generated HQL aliases eventually leading to an OOM
+ * [HHH-14935] - Type annotation is deprecated without an available replacement
+
+** Task
+ * [HHH-14979] - Upgrade to Log4J 2 2.16.0
+
+
+Changes in 5.6.2.Final (December 08, 2021)
+------------------------------------------------------------------------------------------------------------------------
+
+https://hibernate.atlassian.net/projects/HHH/versions/32001
+
+** Bug
+ * [HHH-14956] - Invalid link to MetadataBuilderContributor javadocs in Configurations docs
+ * [HHH-14937] - SybaseDialect does not support schema anymore
+ * [HHH-14936] - JdbcConnectionContext in hibernate-testing throws NPE when user/password are not provided in configuration
+ * [HHH-14935] - Type annotation is deprecated without an available replacement
+ * [HHH-14927] - "Current" documentation is 5.5 instead of 5.6
+ * [HHH-14926] - fix asciidoc error in 'test-case-guide.adoc'
+ * [HHH-14922] - Inconsistent precedence of orm.xml implicit catalog/schema over "default_catalog"/"default_schema"
+ * [HHH-14918] - Key-to-one to id-class entity with key-to-one doesn't work anymore
+ * [HHH-14916] - JPA Critera query Join on Fetch not working
+ * [HHH-14540] - Interceptor instance is shared between ORM session and Enver's temporary session resulting in multiple calls.
+ * [HHH-14211] - @Lob String mapping broken
+
+** Improvement
+ * [HHH-14921] - Definition of the default catalog/schema on session factory creation
+ * [HHH-14903] - Method getConfiguredJdbcBatchSize can be optimised for StatelessSession as well
+ * [HHH-14897] - Allow ordering with nulls first/last from JPA implementation
+
+** Task
+ * [HHH-14938] - Upgrade to MySQL Connector/J 8.0.27
+
+
Changes in 5.6.1.Final (October 27, 2021)
------------------------------------------------------------------------------------------------------------------------
@@ -308,7 +576,7 @@ https://hibernate.atlassian.net/projects/HHH/versions/31844
* [HHH-14257] - An Entity A with a map collection having as index an Embeddable with a an association to the Entity A fails with a NPE
* [HHH-14251] - Invalid SQL for @Embedded UPDATE
* [HHH-14249] - MultiLineImport fails when script contains blank spaces or tabs at the end of the last sql statement
-
+ * [HHH-14216] - Second-level cache doesn't support @OneToOne
Changes in 5.4.14.Final (April 6, 2020)
------------------------------------------------------------------------------------------------------------------------
diff --git a/ci/build.sh b/ci/build.sh
index 30176554d921..49f32442aed7 100755
--- a/ci/build.sh
+++ b/ci/build.sh
@@ -3,9 +3,17 @@
goal=
if [ "$RDBMS" == "derby" ]; then
goal="-Pdb=derby"
+elif [ "$RDBMS" == "hsqldb" ]; then
+ goal="-Pdb=hsqldb"
+elif [ "$RDBMS" == "mysql8" ]; then
+ goal="-Pdb=mysql_ci"
+elif [ "$RDBMS" == "mysql" ]; then
+ goal="-Pdb=mysql_ci"
elif [ "$RDBMS" == "mariadb" ]; then
goal="-Pdb=mariadb_ci"
-elif [ "$RDBMS" == "postgresql" ]; then
+elif [ "$RDBMS" == "postgresql_9_5" ]; then
+ goal="-Pdb=pgsql_ci"
+elif [ "$RDBMS" == "postgresql_13" ]; then
goal="-Pdb=pgsql_ci"
elif [ "$RDBMS" == "oracle" ]; then
# I have no idea why, but these tests don't work on GH Actions
@@ -16,6 +24,8 @@ elif [ "$RDBMS" == "mssql" ]; then
goal="-Pdb=mssql_ci"
elif [ "$RDBMS" == "hana" ]; then
goal="-Pdb=hana_ci"
+elif [ "$RDBMS" == "sybase" ]; then
+ goal="-Pdb=sybase_ci"
fi
exec ./gradlew check ${goal} -Plog-test-progress=true --stacktrace
diff --git a/ci/database-start.sh b/ci/database-start.sh
index e603a9631bfe..997a450ee9f0 100755
--- a/ci/database-start.sh
+++ b/ci/database-start.sh
@@ -8,14 +8,18 @@ elif [ "$RDBMS" == 'mysql8' ]; then
bash $DIR/../docker_db.sh mysql_8_0
elif [ "$RDBMS" == 'mariadb' ]; then
bash $DIR/../docker_db.sh mariadb
-elif [ "$RDBMS" == 'postgresql' ]; then
+elif [ "$RDBMS" == 'postgresql_9_5' ]; then
bash $DIR/../docker_db.sh postgresql_9_5
+elif [ "$RDBMS" == 'postgresql_13' ]; then
+ bash $DIR/../docker_db.sh postgresql_13
elif [ "$RDBMS" == 'db2' ]; then
bash $DIR/../docker_db.sh db2
elif [ "$RDBMS" == 'oracle' ]; then
- bash $DIR/../docker_db.sh oracle
+ bash $DIR/../docker_db.sh oracle_18
elif [ "$RDBMS" == 'mssql' ]; then
bash $DIR/../docker_db.sh mssql
elif [ "$RDBMS" == 'hana' ]; then
bash $DIR/../docker_db.sh hana
+elif [ "$RDBMS" == 'sybase' ]; then
+ bash $DIR/../docker_db.sh sybase
fi
\ No newline at end of file
diff --git a/ci/jpa-2.2-tck.Jenkinsfile b/ci/jpa-2.2-tck.Jenkinsfile
index 396536f53c0b..427be4da3fbd 100644
--- a/ci/jpa-2.2-tck.Jenkinsfile
+++ b/ci/jpa-2.2-tck.Jenkinsfile
@@ -7,6 +7,11 @@ pipeline {
tools {
jdk 'OpenJDK 8 Latest'
}
+ options {
+ rateLimitBuilds(throttle: [count: 1, durationName: 'day', userBoost: true])
+ buildDiscarder(logRotator(numToKeepStr: '3', artifactNumToKeepStr: '3'))
+ disableConcurrentBuilds(abortPrevious: true)
+ }
parameters {
booleanParam(name: 'NO_SLEEP', defaultValue: true, description: 'Whether the NO_SLEEP patch should be applied to speed up the TCK execution')
}
diff --git a/ci/jpa-3.0-tck.Jenkinsfile b/ci/jpa-3.0-tck.Jenkinsfile
index 3d1aab779c82..150c84175025 100644
--- a/ci/jpa-3.0-tck.Jenkinsfile
+++ b/ci/jpa-3.0-tck.Jenkinsfile
@@ -7,6 +7,11 @@ pipeline {
tools {
jdk 'OpenJDK 8 Latest'
}
+ options {
+ rateLimitBuilds(throttle: [count: 1, durationName: 'day', userBoost: true])
+ buildDiscarder(logRotator(numToKeepStr: '3', artifactNumToKeepStr: '3'))
+ disableConcurrentBuilds(abortPrevious: true)
+ }
parameters {
choice(name: 'IMAGE_JDK', choices: ['jdk8', 'jdk11'], description: 'The JDK base image version to use for the TCK image.')
string(name: 'TCK_VERSION', defaultValue: '3.0.0', description: 'The version of the Jakarta JPA TCK i.e. `2.2.0` or `3.0.1`')
diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile
new file mode 100644
index 000000000000..59c9c661b7bf
--- /dev/null
+++ b/ci/release/Jenkinsfile
@@ -0,0 +1,286 @@
+#! /usr/bin/groovy
+/*
+ * See https://github.com/hibernate/hibernate-jenkins-pipeline-helpers
+ */
+@Library('hibernate-jenkins-pipeline-helpers') _
+
+import org.hibernate.jenkins.pipeline.helpers.version.Version
+
+// --------------------------------------------
+// Global build configuration
+env.PROJECT = "orm"
+env.JIRA_KEY = "HHH"
+def RELEASE_ON_SCHEDULE = false // Set to `true` *only* on branches where you want a scheduled release.
+
+print "INFO: env.PROJECT = ${env.PROJECT}"
+print "INFO: env.JIRA_KEY = ${env.JIRA_KEY}"
+
+// --------------------------------------------
+// Build conditions
+
+// Avoid running the pipeline on branch indexing
+if (currentBuild.getBuildCauses().toString().contains('BranchIndexingCause')) {
+ print "INFO: Build skipped due to trigger being Branch Indexing"
+ currentBuild.result = 'NOT_BUILT'
+ return
+}
+
+def manualRelease = currentBuild.getBuildCauses().toString().contains( 'UserIdCause' )
+def cronRelease = currentBuild.getBuildCauses().toString().contains( 'TimerTriggerCause' )
+
+// Only do automatic release on branches where we opted in
+if ( !manualRelease && !cronRelease ) {
+ print "INFO: Build skipped because automated releases on push are disabled on this branch."
+ currentBuild.result = 'NOT_BUILT'
+ return
+}
+
+if ( !manualRelease && cronRelease && !RELEASE_ON_SCHEDULE ) {
+ print "INFO: Build skipped because automated releases are disabled on this branch. See constant RELEASE_ON_SCHEDULE in ci/release/Jenkinsfile"
+ currentBuild.result = 'NOT_BUILT'
+ return
+}
+
+// --------------------------------------------
+// Reusable methods
+
+def checkoutReleaseScripts() {
+ dir('.release/scripts') {
+ checkout scmGit(branches: [[name: '*/main']], extensions: [],
+ userRemoteConfigs: [[credentialsId: 'ed25519.Hibernate-CI.github.com',
+ url: 'https://github.com/hibernate/hibernate-release-scripts.git']])
+ }
+}
+
+
+// --------------------------------------------
+// Pipeline
+
+pipeline {
+ agent {
+ label 'Release'
+ }
+ triggers {
+ // Run every week Sunday midnight
+ cron('0 0 * * 0')
+ }
+ tools {
+ jdk 'OpenJDK 11 Latest'
+ }
+ options {
+ buildDiscarder logRotator(daysToKeepStr: '30', numToKeepStr: '10')
+ disableConcurrentBuilds(abortPrevious: false)
+ preserveStashes()
+ }
+ parameters {
+ string(
+ name: 'RELEASE_VERSION',
+ defaultValue: '',
+ description: 'The version to be released, e.g. 6.2.1.Final. Mandatory for manual releases, to prevent mistakes.',
+ trim: true
+ )
+ string(
+ name: 'DEVELOPMENT_VERSION',
+ defaultValue: '',
+ description: 'The next version to be used after the release, e.g. 6.2.2-SNAPSHOT. If not set, determined automatically from the release version.',
+ trim: true
+ )
+ booleanParam(
+ name: 'RELEASE_DRY_RUN',
+ defaultValue: false,
+ description: 'If true, just simulate the release, without pushing any commits or tags, and without uploading any artifacts or documentation.'
+ )
+ }
+ stages {
+ stage('Release check') {
+ steps {
+ script {
+ print "INFO: params.RELEASE_VERSION = ${params.RELEASE_VERSION}"
+ print "INFO: params.DEVELOPMENT_VERSION = ${params.DEVELOPMENT_VERSION}"
+ print "INFO: params.RELEASE_DRY_RUN? = ${params.RELEASE_DRY_RUN}"
+
+ checkoutReleaseScripts()
+
+ def currentVersion = Version.parseDevelopmentVersion( sh(
+ script: ".release/scripts/determine-current-version.sh ${env.PROJECT}",
+ returnStdout: true
+ ).trim() )
+ echo "Workspace version: ${currentVersion}"
+
+ def releaseVersion
+ def developmentVersion
+
+ def lastCommitter = sh(script: 'git show -s --format=\'%an\'', returnStdout: true).trim()
+ def secondLastCommitter = sh(script: 'git show -s --format=\'%an\' HEAD~1', returnStdout: true).trim()
+ def isCiLastCommiter = lastCommitter == 'Hibernate-CI' && secondLastCommitter == 'Hibernate-CI'
+
+ echo "Last two commits were performed by '${lastCommitter}'/'${secondLastCommitter}'."
+ echo "Is 'Hibernate-CI' the last commiter: '${isCiLastCommiter}'."
+
+ if ( manualRelease ) {
+ echo "Release was requested manually"
+
+ if ( !params.RELEASE_VERSION ) {
+ throw new IllegalArgumentException(
+ 'Missing value for parameter RELEASE_VERSION. This parameter must be set explicitly to prevent mistakes.'
+ )
+ }
+ releaseVersion = Version.parseReleaseVersion( params.RELEASE_VERSION )
+
+ if ( !releaseVersion.toString().startsWith( currentVersion.family + '.' ) ) {
+ throw new IllegalArgumentException( "RELEASE_VERSION = $releaseVersion, which is different from the family of CURRENT_VERSION = $currentVersion. Did you make a mistake?" )
+ }
+ }
+ else {
+ echo "Release was triggered automatically"
+
+ // Avoid doing an automatic release for commits from a release
+
+ if (isCiLastCommiter) {
+ print "INFO: Automatic release skipped because last commits were for the previous release"
+ currentBuild.getRawBuild().getExecutor().interrupt(Result.NOT_BUILT)
+ sleep(1) // Interrupt is not blocking and does not take effect immediately.
+ return
+ }
+
+ releaseVersion = Version.parseReleaseVersion( sh(
+ script: ".release/scripts/determine-release-version.sh ${currentVersion}",
+ returnStdout: true
+ ).trim() )
+ }
+ echo "Release version: ${releaseVersion}"
+
+ if ( !params.DEVELOPMENT_VERSION ) {
+ developmentVersion = Version.parseDevelopmentVersion( sh(
+ script: ".release/scripts/determine-development-version.sh ${releaseVersion}",
+ returnStdout: true
+ ).trim() )
+ }
+ else {
+ developmentVersion = Version.parseDevelopmentVersion( params.DEVELOPMENT_VERSION )
+ }
+ echo "Development version: ${developmentVersion}"
+
+ env.RELEASE_VERSION = releaseVersion.toString()
+ env.DEVELOPMENT_VERSION = developmentVersion.toString()
+ env.SCRIPT_OPTIONS = params.RELEASE_DRY_RUN ? "-d" : ""
+ env.JRELEASER_DRY_RUN = params.RELEASE_DRY_RUN
+
+ // Determine version id to check if Jira version exists
+ sh ".release/scripts/determine-jira-version-id.sh ${env.JIRA_KEY} ${releaseVersion.withoutFinalQualifier}"
+ }
+ }
+ }
+ stage('Release prepare') {
+ steps {
+ script {
+ checkoutReleaseScripts()
+
+ configFileProvider([
+ configFile(fileId: 'release.config.ssh', targetLocation: "${env.HOME}/.ssh/config"),
+ configFile(fileId: 'release.config.ssh.knownhosts', targetLocation: "${env.HOME}/.ssh/known_hosts")
+ ]) {
+ sshagent(['ed25519.Hibernate-CI.github.com', 'hibernate-ci.frs.sourceforge.net']) {
+ // set release version
+ // update changelog from JIRA
+ // tags the version
+ // changes the version to the provided development version
+ withEnv([
+ "DISABLE_REMOTE_GRADLE_CACHE=true",
+ // Increase the amount of memory for this part since asciidoctor doc rendering consumes a lot of metaspace
+ "GRADLE_OPTS=-Dorg.gradle.jvmargs='-Dlog4j2.disableJmx -Xmx4g -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8'"
+ ]) {
+ sh ".release/scripts/prepare-release.sh -j -b ${env.GIT_BRANCH} -v ${env.DEVELOPMENT_VERSION} ${env.PROJECT} ${env.RELEASE_VERSION}"
+ }
+ }
+ }
+ }
+ }
+ }
+ stage('Publish') {
+ steps {
+ script {
+ checkoutReleaseScripts()
+
+ configFileProvider([
+ configFile(fileId: 'release.config.ssh', targetLocation: "${env.HOME}/.ssh/config"),
+ configFile(fileId: 'release.config.ssh.knownhosts', targetLocation: "${env.HOME}/.ssh/known_hosts")
+ ]) {
+ withCredentials([
+ usernamePassword(credentialsId: 'central.sonatype.com', passwordVariable: 'JRELEASER_MAVENCENTRAL_TOKEN', usernameVariable: 'JRELEASER_MAVENCENTRAL_USERNAME'),
+ // https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html#account_setup
+ usernamePassword(credentialsId: 'gradle-plugin-portal-api-key', passwordVariable: 'GRADLE_PUBLISH_SECRET', usernameVariable: 'GRADLE_PUBLISH_KEY'),
+ gitUsernamePassword(credentialsId: 'username-and-token.Hibernate-CI.github.com', gitToolName: 'Default'),
+ file(credentialsId: 'release.gpg.private-key', variable: 'RELEASE_GPG_PRIVATE_KEY_PATH'),
+ string(credentialsId: 'release.gpg.passphrase', variable: 'JRELEASER_GPG_PASSPHRASE'),
+ string(credentialsId: 'Hibernate-CI.github.com', variable: 'JRELEASER_GITHUB_TOKEN')
+ ]) {
+ sshagent(['ed25519.Hibernate-CI.github.com', 'jenkins.in.relation.to', 'hibernate-ci.frs.sourceforge.net']) {
+ // performs documentation upload and Sonatype release
+ // push to github
+ withEnv([
+ "DISABLE_REMOTE_GRADLE_CACHE=true"
+ ]) {
+ def notesFiles = findFiles(glob: 'release_notes.md')
+ if ( notesFiles.length < 1 ) {
+ throw new IllegalStateException( "Could not locate `release_notes.md`" )
+ }
+ if ( notesFiles.length > 1 ) {
+ throw new IllegalStateException( "Located more than 1 `release_notes.md`" )
+ }
+
+ sh ".release/scripts/publish.sh -j --notes=${notesFiles[0].path} ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION} ${env.GIT_BRANCH} "
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ stage('Release on Jira') {
+ steps {
+ script {
+ checkoutReleaseScripts()
+
+ withCredentials([string(credentialsId: 'release-webhook.hibernate.atlassian.net', variable: 'JIRA_WEBHOOK_SECRET')]) {
+ sh ".release/scripts/jira-release.sh ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION}"
+ }
+ }
+ }
+ }
+ stage('Update website') {
+ steps {
+ script {
+ checkoutReleaseScripts()
+
+ configFileProvider([
+ configFile(fileId: 'release.config.ssh', targetLocation: "${env.HOME}/.ssh/config"),
+ configFile(fileId: 'release.config.ssh.knownhosts', targetLocation: "${env.HOME}/.ssh/known_hosts")
+ ]) {
+ withCredentials([
+ gitUsernamePassword(credentialsId: 'username-and-token.Hibernate-CI.github.com', gitToolName: 'Default')
+ ]) {
+ sshagent( ['ed25519.Hibernate-CI.github.com'] ) {
+ dir( '.release/hibernate.org' ) {
+ checkout scmGit(
+ branches: [[name: '*/production']],
+ extensions: [],
+ userRemoteConfigs: [[credentialsId: 'ed25519.Hibernate-CI.github.com', url: 'https://github.com/hibernate/hibernate.org.git']]
+ )
+ sh "../scripts/website-release.sh ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION}"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ post {
+ always {
+ configFileProvider([configFile(fileId: 'job-configuration.yaml', variable: 'JOB_CONFIGURATION_FILE')]) {
+ notifyBuildResult maintainers: (String) readYaml(file: env.JOB_CONFIGURATION_FILE).notification?.email?.recipients
+ }
+ }
+ }
+}
diff --git a/ci/snapshot-publish.Jenkinsfile b/ci/snapshot-publish.Jenkinsfile
new file mode 100644
index 000000000000..0059d95d0a96
--- /dev/null
+++ b/ci/snapshot-publish.Jenkinsfile
@@ -0,0 +1,74 @@
+/*
+ * See https://github.com/hibernate/hibernate-jenkins-pipeline-helpers
+ */
+@Library('hibernate-jenkins-pipeline-helpers@1.5') _
+
+// Avoid running the pipeline on branch indexing
+if (currentBuild.getBuildCauses().toString().contains('BranchIndexingCause')) {
+ print "INFO: Build skipped due to trigger being Branch Indexing"
+ currentBuild.result = 'ABORTED'
+ return
+}
+
+def checkoutReleaseScripts() {
+ dir('.release/scripts') {
+ checkout scmGit(branches: [[name: '*/main']], extensions: [],
+ userRemoteConfigs: [[credentialsId: 'ed25519.Hibernate-CI.github.com',
+ url: 'https://github.com/hibernate/hibernate-release-scripts.git']])
+ }
+}
+
+pipeline {
+ agent {
+ label 'Release'
+ }
+ tools {
+ jdk 'OpenJDK 8 Latest'
+ }
+ options {
+ rateLimitBuilds(throttle: [count: 1, durationName: 'hour', userBoost: true])
+ buildDiscarder(logRotator(numToKeepStr: '3', artifactNumToKeepStr: '3'))
+ disableConcurrentBuilds(abortPrevious: true)
+ }
+ stages {
+ stage('Checkout') {
+ steps {
+ checkout scm
+ }
+ }
+ stage('Publish') {
+ steps {
+ script {
+ withCredentials([
+ // https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh
+ // https://docs.gradle.org/current/samples/sample_publishing_credentials.html#:~:text=via%20environment%20variables
+ usernamePassword(credentialsId: 'central.sonatype.com', passwordVariable: 'ORG_GRADLE_PROJECT_snapshotsPassword', usernameVariable: 'ORG_GRADLE_PROJECT_snapshotsUsername'),
+ string(credentialsId: 'Hibernate-CI.github.com', variable: 'JRELEASER_GITHUB_TOKEN'),
+ // https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html#account_setup
+ usernamePassword(credentialsId: 'gradle-plugin-portal-api-key', passwordVariable: 'GRADLE_PUBLISH_SECRET', usernameVariable: 'GRADLE_PUBLISH_KEY'),
+ gitUsernamePassword(credentialsId: 'username-and-token.Hibernate-CI.github.com', gitToolName: 'Default')
+ ]) {
+ withEnv([
+ "DISABLE_REMOTE_GRADLE_CACHE=true"
+ ]) {
+ checkoutReleaseScripts()
+ def version = sh(
+ script: ".release/scripts/determine-current-version.sh orm",
+ returnStdout: true
+ ).trim()
+ echo "Current version: '${version}'"
+ sh "bash -xe .release/scripts/snapshot-deploy.sh orm ${version}"
+ }
+ }
+ }
+ }
+ }
+ }
+ post {
+ always {
+ configFileProvider([configFile(fileId: 'job-configuration.yaml', variable: 'JOB_CONFIGURATION_FILE')]) {
+ notifyBuildResult maintainers: (String) readYaml(file: env.JOB_CONFIGURATION_FILE).notification?.email?.recipients
+ }
+ }
+ }
+}
diff --git a/databases/cockroachdb/matrix.gradle b/databases/cockroachdb/matrix.gradle
index c6ed30abc639..797dbd1d257c 100644
--- a/databases/cockroachdb/matrix.gradle
+++ b/databases/cockroachdb/matrix.gradle
@@ -11,4 +11,4 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
-jdbcDependency 'org.postgresql:postgresql:42.2.8'
\ No newline at end of file
+jdbcDependency 'org.postgresql:postgresql:42.5.0'
\ No newline at end of file
diff --git a/databases/pgsql/matrix.gradle b/databases/pgsql/matrix.gradle
index b8ac50d60726..21b9703e577c 100644
--- a/databases/pgsql/matrix.gradle
+++ b/databases/pgsql/matrix.gradle
@@ -4,4 +4,4 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
-jdbcDependency 'org.postgresql:postgresql:42.2.19'
+jdbcDependency 'org.postgresql:postgresql:42.5.0'
diff --git a/docker_db.sh b/docker_db.sh
index e88589cada38..6317b5c8755e 100755
--- a/docker_db.sh
+++ b/docker_db.sh
@@ -1,45 +1,123 @@
#! /bin/bash
+if command -v podman > /dev/null; then
+ CONTAINER_CLI=$(command -v podman)
+ HEALTCHECK_PATH="{{.State.Healthcheck.Status}}"
+ # Only use sudo for podman
+ if command -v sudo > /dev/null; then
+ PRIVILEGED_CLI="sudo"
+ else
+ PRIVILEGED_CLI=""
+ fi
+else
+ CONTAINER_CLI=$(command -v docker)
+ HEALTCHECK_PATH="{{.State.Health.Status}}"
+ PRIVILEGED_CLI=""
+fi
+
mysql_5_7() {
- docker rm -f mysql || true
- docker run --name mysql -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -p3306:3306 -d mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
+ $CONTAINER_CLI rm -f mysql || true
+ $CONTAINER_CLI run --name mysql -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d docker.io/mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --skip-character-set-client-handshake --log-bin-trust-function-creators=1
+ # Give the container some time to start
+ OUTPUT=
+ n=0
+ until [ "$n" -ge 5 ]
+ do
+ # Need to access STDERR. Thanks for the snippet https://stackoverflow.com/a/56577569/412446
+ { OUTPUT="$( { $CONTAINER_CLI logs mysql; } 2>&1 1>&3 3>&- )"; } 3>&1;
+ if [[ $OUTPUT == *"ready for connections"* ]]; then
+ break;
+ fi
+ n=$((n+1))
+ echo "Waiting for MySQL to start..."
+ sleep 3
+ done
+ if [ "$n" -ge 5 ]; then
+ echo "MySQL failed to start and configure after 15 seconds"
+ else
+ echo "MySQL successfully started"
+ fi
}
mysql_8_0() {
- docker rm -f mysql || true
- docker run --name mysql -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -p3306:3306 -d mysql:8.0.21 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
+ $CONTAINER_CLI rm -f mysql || true
+ $CONTAINER_CLI run --name mysql -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d docker.io/mysql:8.0.21 --character-set-server=utf8mb4 --collation-server=utf8mb4_0900_as_cs --skip-character-set-client-handshake --log-bin-trust-function-creators=1
+ # Give the container some time to start
+ OUTPUT=
+ n=0
+ until [ "$n" -ge 5 ]
+ do
+ # Need to access STDERR. Thanks for the snippet https://stackoverflow.com/a/56577569/412446
+ { OUTPUT="$( { $CONTAINER_CLI logs mysql; } 2>&1 1>&3 3>&- )"; } 3>&1;
+ if [[ $OUTPUT == *"ready for connections"* ]]; then
+ break;
+ fi
+ n=$((n+1))
+ echo "Waiting for MySQL to start..."
+ sleep 3
+ done
+ if [ "$n" -ge 5 ]; then
+ echo "MySQL failed to start and configure after 15 seconds"
+ else
+ echo "MySQL successfully started"
+ fi
}
mariadb() {
- docker rm -f mariadb || true
- docker run --name mariadb -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d mariadb:10.5.8 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
+ $CONTAINER_CLI rm -f mariadb || true
+ $CONTAINER_CLI run --name mariadb -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=hibernate_orm_test -p3306:3306 -d docker.io/mariadb:10.5.8 --character-set-server=utf8mb4 --collation-server=utf8mb4_bin --skip-character-set-client-handshake
+ OUTPUT=
+ n=0
+ until [ "$n" -ge 5 ]
+ do
+ # Need to access STDERR. Thanks for the snippet https://stackoverflow.com/a/56577569/412446
+ { OUTPUT="$( { $CONTAINER_CLI logs mariadb; } 2>&1 1>&3 3>&- )"; } 3>&1;
+ if [[ $OUTPUT == *"ready for connections"* ]]; then
+ break;
+ fi
+ n=$((n+1))
+ echo "Waiting for MariaDB to start..."
+ sleep 3
+ done
+ if [ "$n" -ge 5 ]; then
+ echo "MariaDB failed to start and configure after 15 seconds"
+ else
+ echo "MariaDB successfully started"
+ fi
}
postgresql_9_5() {
- docker rm -f postgres || true
- docker run --name postgres -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p5432:5432 -d postgres:9.5
+ $CONTAINER_CLI rm -f postgres || true
+ $CONTAINER_CLI run --name postgres -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p5432:5432 -d docker.io/postgis/postgis:9.5-2.5
}
-postgis(){
- docker rm -f postgis || true
- docker run --name postgis -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p5432:5432 -d postgis/postgis:11-2.5
+postgresql_13() {
+ $CONTAINER_CLI rm -f postgres || true
+ $CONTAINER_CLI run --name postgres -e POSTGRES_USER=hibernate_orm_test -e POSTGRES_PASSWORD=hibernate_orm_test -e POSTGRES_DB=hibernate_orm_test -p5432:5432 -d docker.io/postgis/postgis:13-3.1
+}
+
+edb() {
+ #$CONTAINER_CLI login containers.enterprisedb.com
+ $CONTAINER_CLI rm -f edb || true
+ $CONTAINER_CLI run --name edb -e ACCEPT_EULA=Yes -e DATABASE_USER=hibernate_orm_test -e DATABASE_USER_PASSWORD=hibernate_orm_test -e ENTERPRISEDB_PASSWORD=hibernate_orm_test -e DATABASE_NAME=hibernate_orm_test -e PGPORT=5433 -p 5433:5433 --mount type=tmpfs,destination=/edbvolume -d containers.enterprisedb.com/edb/edb-as-lite:v11
}
db2() {
- docker rm -f db2 || true
- docker run --name db2 --privileged -e DB2INSTANCE=orm_test -e DB2INST1_PASSWORD=orm_test -e DBNAME=orm_test -e LICENSE=accept -e AUTOCONFIG=false -e ARCHIVE_LOGS=false -e TO_CREATE_SAMPLEDB=false -e REPODB=false -p 50000:50000 -d ibmcom/db2:11.5.5.0
+ echo $CONTAINER_CLI
+ $PRIVILEGED_CLI $CONTAINER_CLI rm -f db2 || true
+ $PRIVILEGED_CLI $CONTAINER_CLI run --name db2 --privileged -e DB2INSTANCE=orm_test -e DB2INST1_PASSWORD=orm_test -e DBNAME=orm_test -e LICENSE=accept -e AUTOCONFIG=false -e ARCHIVE_LOGS=false -e TO_CREATE_SAMPLEDB=false -e REPODB=false -p 50000:50000 -d docker.io/ibmcom/db2:11.5.7.0
# Give the container some time to start
OUTPUT=
while [[ $OUTPUT != *"INSTANCE"* ]]; do
echo "Waiting for DB2 to start..."
sleep 10
- OUTPUT=$(docker logs db2)
+ OUTPUT=$($PRIVILEGED_CLI $CONTAINER_CLI logs db2)
done
- docker exec -t db2 su - orm_test bash -c ". /database/config/orm_test/sqllib/db2profile && /database/config/orm_test/sqllib/bin/db2 'connect to orm_test' && /database/config/orm_test/sqllib/bin/db2 'CREATE USER TEMPORARY TABLESPACE usr_tbsp MANAGED BY AUTOMATIC STORAGE'"
+ $PRIVILEGED_CLI $CONTAINER_CLI exec -t db2 su - orm_test bash -c ". /database/config/orm_test/sqllib/db2profile && /database/config/orm_test/sqllib/bin/db2 'connect to orm_test' && /database/config/orm_test/sqllib/bin/db2 'CREATE USER TEMPORARY TABLESPACE usr_tbsp MANAGED BY AUTOMATIC STORAGE'"
}
db2_spatial() {
- docker rm -f db2spatial || true
+ $PRIVILEGED_CLI $CONTAINER_CLI rm -f db2spatial || true
temp_dir=$(mktemp -d)
cat <${temp_dir}/ewkt.sql
create or replace function db2gse.asewkt(geometry db2gse.st_geometry)
@@ -78,35 +156,35 @@ CREATE TRANSFORM FOR db2gse.ST_Geometry DB2_PROGRAM (
TO SQL WITH FUNCTION db2gse.geomfromewkt(varchar(32000)) )
;
EOF
- docker run --name db2spatial --privileged -e DB2INSTANCE=orm_test -e DB2INST1_PASSWORD=orm_test -e DBNAME=orm_test -e LICENSE=accept -e AUTOCONFIG=false -e ARCHIVE_LOGS=false -e TO_CREATE_SAMPLEDB=false -e REPODB=false \
+ $PRIVILEGED_CLI $CONTAINER_CLI run --name db2spatial --privileged -e DB2INSTANCE=orm_test -e DB2INST1_PASSWORD=orm_test -e DBNAME=orm_test -e LICENSE=accept -e AUTOCONFIG=false -e ARCHIVE_LOGS=false -e TO_CREATE_SAMPLEDB=false -e REPODB=false \
-v ${temp_dir}:/conf \
- -p 50000:50000 -d ibmcom/db2:11.5.5.0
+ -p 50000:50000 -d docker.io/ibmcom/db2:11.5.5.0
# Give the container some time to start
OUTPUT=
while [[ $OUTPUT != *"Setup has completed."* ]]; do
echo "Waiting for DB2 to start..."
sleep 10
- OUTPUT=$(docker logs db2spatial)
+ OUTPUT=$($PRIVILEGED_CLI $CONTAINER_CLI logs db2spatial)
done
sleep 10
echo "Enabling spatial extender"
- docker exec -t db2spatial su - orm_test bash -c "/database/config/orm_test/sqllib/db2profile && /database/config/orm_test/sqllib/bin/db2se enable_db orm_test"
+ $PRIVILEGED_CLI $CONTAINER_CLI exec -t db2spatial su - orm_test bash -c "/database/config/orm_test/sqllib/db2profile && /database/config/orm_test/sqllib/bin/db2se enable_db orm_test"
echo "Installing required transform group"
- docker exec -t db2spatial su - orm_test bash -c "/database/config/orm_test/sqllib/db2profile && /database/config/orm_test/sqllib/bin/db2 'connect to orm_test' && /database/config/orm_test/sqllib/bin/db2 -tvf /conf/ewkt.sql"
+ $PRIVILEGED_CLI $CONTAINER_CLI exec -t db2spatial su - orm_test bash -c "/database/config/orm_test/sqllib/db2profile && /database/config/orm_test/sqllib/bin/db2 'connect to orm_test' && /database/config/orm_test/sqllib/bin/db2 -tvf /conf/ewkt.sql"
}
mssql() {
- docker rm -f mssql || true
- docker run --name mssql -d -p 1433:1433 -e "SA_PASSWORD=Hibernate_orm_test" -e ACCEPT_EULA=Y mcr.microsoft.com/mssql/server:2017-CU13
+ $CONTAINER_CLI rm -f mssql || true
+ $CONTAINER_CLI run --name mssql -d -p 1433:1433 -e "SA_PASSWORD=Hibernate_orm_test" -e ACCEPT_EULA=Y mcr.microsoft.com/mssql/server:2017-CU13
sleep 5
n=0
until [ "$n" -ge 5 ]
do
# We need a database that uses a non-lock based MVCC approach
# https://github.com/microsoft/homebrew-mssql-release/issues/2#issuecomment-682285561
- docker exec mssql bash -c 'echo "create database hibernate_orm_test collate SQL_Latin1_General_CP1_CI_AS; alter database hibernate_orm_test set READ_COMMITTED_SNAPSHOT ON" | /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Hibernate_orm_test -i /dev/stdin' && break
+ $CONTAINER_CLI exec mssql bash -c 'echo "create database hibernate_orm_test collate SQL_Latin1_General_CP1_CS_AS; alter database hibernate_orm_test set READ_COMMITTED_SNAPSHOT ON" | /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Hibernate_orm_test -i /dev/stdin' && break
echo "Waiting for SQL Server to start..."
n=$((n+1))
sleep 5
@@ -118,19 +196,146 @@ mssql() {
fi
}
-oracle() {
- docker rm -f oracle || true
- # We need to use the defaults
- # SYSTEM/Oracle18
- docker run --shm-size=1536m --name oracle -d -p 1521:1521 --ulimit nofile=1048576:1048576 quillbuilduser/oracle-18-xe
- until [ "`docker inspect -f {{.State.Health.Status}} oracle`" == "healthy" ];
+sybase() {
+ $CONTAINER_CLI rm -f sybase || true
+ # Yup, that sucks, but on ubuntu we need to use -T11889 as per: https://github.com/DataGrip/docker-env/issues/12
+ $CONTAINER_CLI run -d -p 5000:5000 -p 5001:5001 --name sybase --entrypoint /bin/bash docker.io/nguoianphu/docker-sybase -c "source /opt/sybase/SYBASE.sh
+/opt/sybase/ASE-16_0/bin/dataserver \
+-d/opt/sybase/data/master.dat \
+-e/opt/sybase/ASE-16_0/install/MYSYBASE.log \
+-c/opt/sybase/ASE-16_0/MYSYBASE.cfg \
+-M/opt/sybase/ASE-16_0 \
+-N/opt/sybase/ASE-16_0/sysam/MYSYBASE.properties \
+-i/opt/sybase \
+-sMYSYBASE \
+-T11889
+RET=\$?
+exit 0
+"
+
+ sybase_check() {
+ $CONTAINER_CLI exec sybase bash -c "source /opt/sybase/SYBASE.sh;
+/opt/sybase/OCS-16_0/bin/isql -Usa -P myPassword -S MYSYBASE < 0
+go
+quit
+EOF
+"
+}
+ START_STATUS=0
+ j=1
+ while (( $j < 30 )); do
+ echo "Waiting for Sybase to start..."
+ sleep 1
+ j=$((j+1))
+ START_STATUS=$(sybase_check | grep '(0 rows affected)' | wc -c)
+ if (( $START_STATUS > 0 )); then
+ break
+ fi
+ done
+ if (( $j == 30 )); then
+ echo "Failed starting Sybase"
+ $CONTAINER_CLI ps -a
+ $CONTAINER_CLI logs sybase
+ sybase_check
+ exit 1
+ fi
+
+ export SYBASE_DB=hibernate_orm_test
+ export SYBASE_USER=hibernate_orm_test
+ export SYBASE_PASSWORD=hibernate_orm_test
+ $CONTAINER_CLI exec sybase bash -c "source /opt/sybase/SYBASE.sh;
+cat <<-EOSQL > init1.sql
+use master
+go
+disk resize name='master', size='256m'
+go
+create database $SYBASE_DB on master = '96m'
+go
+sp_dboption $SYBASE_DB, \"single user\", true
+go
+alter database $SYBASE_DB log on master = '50m'
+go
+use $SYBASE_DB
+go
+exec sp_extendsegment logsegment, $SYBASE_DB, master
+go
+use master
+go
+sp_dboption $SYBASE_DB, \"single user\", false
+go
+use $SYBASE_DB
+go
+checkpoint
+go
+use master
+go
+create login $SYBASE_USER with password $SYBASE_PASSWORD
+go
+exec sp_dboption $SYBASE_DB, 'abort tran on log full', true
+go
+exec sp_dboption $SYBASE_DB, 'allow nulls by default', true
+go
+exec sp_dboption $SYBASE_DB, 'ddl in tran', true
+go
+exec sp_dboption $SYBASE_DB, 'trunc log on chkpt', true
+go
+exec sp_dboption $SYBASE_DB, 'full logging for select into', true
+go
+exec sp_dboption $SYBASE_DB, 'full logging for alter table', true
+go
+sp_dboption $SYBASE_DB, \"select into\", true
+go
+sp_dboption tempdb, 'ddl in tran', true
+go
+EOSQL
+
+/opt/sybase/OCS-16_0/bin/isql -Usa -P myPassword -S MYSYBASE -i ./init1.sql
+
+echo =============== CREATING DB ==========================
+cat <<-EOSQL > init2.sql
+use $SYBASE_DB
+go
+sp_adduser '$SYBASE_USER', '$SYBASE_USER', null
+go
+grant create default to $SYBASE_USER
+go
+grant create table to $SYBASE_USER
+go
+grant create view to $SYBASE_USER
+go
+grant create rule to $SYBASE_USER
+go
+grant create function to $SYBASE_USER
+go
+grant create procedure to $SYBASE_USER
+go
+commit
+go
+EOSQL
+
+/opt/sybase/OCS-16_0/bin/isql -Usa -P myPassword -S MYSYBASE -i ./init2.sql"
+ echo "Sybase successfully started"
+}
+
+oracle_setup() {
+ HEALTHSTATUS=
+ until [ "$HEALTHSTATUS" == "healthy" ];
do
echo "Waiting for Oracle to start..."
- sleep 10;
+ sleep 5;
+ # On WSL, health-checks intervals don't work for Podman, so run them manually
+ if command -v podman > /dev/null; then
+ $CONTAINER_CLI healthcheck run oracle > /dev/null
+ fi
+ HEALTHSTATUS="`$CONTAINER_CLI inspect -f $HEALTCHECK_PATH oracle`"
+ HEALTHSTATUS=${HEALTHSTATUS##+( )} #Remove longest matching series of spaces from the front
+ HEALTHSTATUS=${HEALTHSTATUS%%+( )} #Remove longest matching series of spaces from the back
done
+ sleep 2;
echo "Oracle successfully started"
# We increase file sizes to avoid online resizes as that requires lots of CPU which is restricted in XE
- docker exec oracle bash -c "source /home/oracle/.bashrc; bash -c \"
+ $CONTAINER_CLI exec oracle bash -c "source /home/oracle/.bashrc; bash -c \"
cat <$temp_dir/password.json
chmod 777 -R $temp_dir
- docker rm -f hana || true
- docker run -d --name hana -p 39013:39013 -p 39017:39017 -p 39041-39045:39041-39045 -p 1128-1129:1128-1129 -p 59013-59014:59013-59014 \
+ $CONTAINER_CLI rm -f hana || true
+ $CONTAINER_CLI run -d --name hana -p 39013:39013 -p 39017:39017 -p 39041-39045:39041-39045 -p 1128-1129:1128-1129 -p 59013-59014:59013-59014 \
--memory=8g \
--ulimit nofile=1048576:1048576 \
--sysctl kernel.shmmax=1073741824 \
@@ -204,7 +461,7 @@ hana() {
--sysctl kernel.shmmni=4096 \
--sysctl kernel.shmall=8388608 \
-v $temp_dir:/config \
- store/saplabs/hanaexpress:2.00.045.00.20200121.1 \
+ docker.io/store/saplabs/hanaexpress:2.00.045.00.20200121.1 \
--passwords-url file:///config/password.json \
--agree-to-sap-license
# Give the container some time to start
@@ -212,22 +469,22 @@ hana() {
while [[ $OUTPUT != *"Startup finished"* ]]; do
echo "Waiting for HANA to start..."
sleep 10
- OUTPUT=$(docker logs hana)
+ OUTPUT=$($CONTAINER_CLI logs hana)
done
echo "HANA successfully started"
}
cockroachdb() {
- docker rm -f cockroach || true
- docker run -d --name=cockroach -p 26257:26257 -p 8080:8080 cockroachdb/cockroach:v20.2.4 start-single-node --insecure
+ $CONTAINER_CLI rm -f cockroach || true
+ $CONTAINER_CLI run -d --name=cockroach -p 26257:26257 -p 8080:8080 docker.io/cockroachdb/cockroach:v20.2.4 start-single-node --insecure
OUTPUT=
while [[ $OUTPUT != *"CockroachDB node starting"* ]]; do
echo "Waiting for CockroachDB to start..."
sleep 10
- OUTPUT=$(docker logs cockroach)
+ OUTPUT=$($CONTAINER_CLI logs cockroach)
done
echo "Enabling experimental box2d operators"
- docker exec -it cockroach bash -c "cat <
apply from: rootProject.file( 'gradle/java-module.gradle' )
-apply plugin: 'org.asciidoctor.convert'
+apply plugin: 'org.asciidoctor.jvm.convert'
apply plugin: 'hibernate-matrix-testing'
@@ -180,8 +184,6 @@ task renderTopicalGuides(type: AsciidoctorTask, group: 'Documentation') {
description = 'Renders the Topical Guides in HTML format using Asciidoctor.'
sourceDir = file( 'src/main/asciidoc/topical' )
outputDir = new File("$buildDir/asciidoc/topical/html_single")
- backends "html5"
- separateOutputDirs false
options logDocuments: true
attributes icons: 'font',
experimental: true,
@@ -205,8 +207,6 @@ task renderGettingStartedGuides(type: AsciidoctorTask, group: 'Documentation') {
include 'index.adoc'
}
outputDir = new File("$buildDir/asciidoc/quickstart/html_single")
- backends "html5"
- separateOutputDirs false
options logDocuments: true
attributes icons: 'font', experimental: true, 'source-highlighter': 'prettify'
}
@@ -216,10 +216,10 @@ task buildTutorialZip(type: Zip) {
from 'src/main/asciidoc/quickstart/tutorials'
destinationDir = tasks.renderGettingStartedGuides.outputDir
archiveName = 'hibernate-tutorials.zip'
- expand(
+ expand(
version: project.version,
slf4j: "1.7.5",
- junit: project.junitVersion,
+ junit: project.junit4Version,
h2: project.h2Version
)
}
@@ -235,8 +235,6 @@ task renderUserGuide(type: AsciidoctorTask, group: 'Documentation') {
include 'Hibernate_User_Guide.adoc'
}
outputDir = new File("$buildDir/asciidoc/userguide/html_single")
- backends "html5"
- separateOutputDirs false
options logDocuments: true
attributes icons: 'font', experimental: true,
'source-highlighter': 'prettify',
@@ -272,8 +270,6 @@ task renderIntegrationGuide(type: AsciidoctorTask, group: 'Documentation') {
include 'Hibernate_Integration_Guide.adoc'
}
outputDir = new File("$buildDir/asciidoc/integrationguide/html_single")
- backends "html5"
- separateOutputDirs false
options logDocuments: true
attributes icons: 'font',
experimental: true,
@@ -320,3 +316,10 @@ buildDocsForPublishing.dependsOn renderIntegrationGuide
checkstyleMain.exclude '**/org/hibernate/userguide/model/*'
+tasks.withType(AsciidoctorTask).configureEach {
+ baseDirFollowsSourceDir()
+ outputOptions {
+ separateOutputDirs = false
+ backends 'html5'
+ }
+}
diff --git a/documentation/src/main/asciidoc/quickstart/tutorials/annotations/src/test/resources/hibernate.cfg.xml b/documentation/src/main/asciidoc/quickstart/tutorials/annotations/src/test/resources/hibernate.cfg.xml
index a9590c18778a..8bcad6c099fd 100644
--- a/documentation/src/main/asciidoc/quickstart/tutorials/annotations/src/test/resources/hibernate.cfg.xml
+++ b/documentation/src/main/asciidoc/quickstart/tutorials/annotations/src/test/resources/hibernate.cfg.xml
@@ -15,7 +15,7 @@
org.h2.Driver
- jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE
+ jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1sa
diff --git a/documentation/src/main/asciidoc/quickstart/tutorials/basic/src/test/resources/hibernate.cfg.xml b/documentation/src/main/asciidoc/quickstart/tutorials/basic/src/test/resources/hibernate.cfg.xml
index 03d39373c3fa..9f7caa7269e6 100644
--- a/documentation/src/main/asciidoc/quickstart/tutorials/basic/src/test/resources/hibernate.cfg.xml
+++ b/documentation/src/main/asciidoc/quickstart/tutorials/basic/src/test/resources/hibernate.cfg.xml
@@ -15,7 +15,7 @@
org.h2.Driver
- jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE
+ jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1sa
diff --git a/documentation/src/main/asciidoc/quickstart/tutorials/entitymanager/src/test/resources/META-INF/persistence.xml b/documentation/src/main/asciidoc/quickstart/tutorials/entitymanager/src/test/resources/META-INF/persistence.xml
index 0fc952381381..57dcc7ccf471 100644
--- a/documentation/src/main/asciidoc/quickstart/tutorials/entitymanager/src/test/resources/META-INF/persistence.xml
+++ b/documentation/src/main/asciidoc/quickstart/tutorials/entitymanager/src/test/resources/META-INF/persistence.xml
@@ -18,7 +18,7 @@
-
+
diff --git a/documentation/src/main/asciidoc/quickstart/tutorials/envers/src/test/resources/META-INF/persistence.xml b/documentation/src/main/asciidoc/quickstart/tutorials/envers/src/test/resources/META-INF/persistence.xml
index 45a7dafe73cf..51ccd776ffe6 100644
--- a/documentation/src/main/asciidoc/quickstart/tutorials/envers/src/test/resources/META-INF/persistence.xml
+++ b/documentation/src/main/asciidoc/quickstart/tutorials/envers/src/test/resources/META-INF/persistence.xml
@@ -18,7 +18,7 @@
-
+
diff --git a/documentation/src/main/asciidoc/quickstart/tutorials/osgi/datasource-h2.xml b/documentation/src/main/asciidoc/quickstart/tutorials/osgi/datasource-h2.xml
index 3bf738fd7a7b..b560a983d8b4 100644
--- a/documentation/src/main/asciidoc/quickstart/tutorials/osgi/datasource-h2.xml
+++ b/documentation/src/main/asciidoc/quickstart/tutorials/osgi/datasource-h2.xml
@@ -15,7 +15,7 @@ Then copy this file to the deploy folder
-
+
diff --git a/documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/META-INF/persistence.xml b/documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/META-INF/persistence.xml
index b0ebfe7053b1..7b03fca6f054 100644
--- a/documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/META-INF/persistence.xml
+++ b/documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-jpa/src/main/resources/META-INF/persistence.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/hibernate.cfg.xml b/documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/hibernate.cfg.xml
index 7f9f2380c902..1ed56551c9b1 100644
--- a/documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/hibernate.cfg.xml
+++ b/documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/hibernate.cfg.xml
@@ -12,7 +12,7 @@
org.h2.Driver
- jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE
+ jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1saorg.hibernate.dialect.H2Dialect
diff --git a/documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/pool-one.properties b/documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/pool-one.properties
index 7e1c4cf1bac6..d3b9463cc6d7 100644
--- a/documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/pool-one.properties
+++ b/documentation/src/main/asciidoc/quickstart/tutorials/osgi/unmanaged-native/src/main/resources/pool-one.properties
@@ -5,7 +5,7 @@
# See the lgpl.txt file in the root directory or .
#
jdbc-0.proxool.alias=pool-one
-jdbc-0.proxool.driver-url=jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE
+jdbc-0.proxool.driver-url=jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1
jdbc-0.proxool.driver-class=org.h2.Driver
jdbc-0.user=sa
jdbc-0.password=
diff --git a/documentation/src/main/asciidoc/userguide/Hibernate_User_Guide-docinfo.html b/documentation/src/main/asciidoc/userguide/Hibernate_User_Guide-docinfo.html
index 0fd842c784cc..8a452d99e232 100644
--- a/documentation/src/main/asciidoc/userguide/Hibernate_User_Guide-docinfo.html
+++ b/documentation/src/main/asciidoc/userguide/Hibernate_User_Guide-docinfo.html
@@ -1,5 +1,5 @@
-
-
+
+
diff --git a/documentation/src/main/asciidoc/userguide/chapters/bootstrap/extras/persistence-external.xml b/documentation/src/main/asciidoc/userguide/chapters/bootstrap/extras/persistence-external.xml
index e009087fede9..215821ea26c9 100644
--- a/documentation/src/main/asciidoc/userguide/chapters/bootstrap/extras/persistence-external.xml
+++ b/documentation/src/main/asciidoc/userguide/chapters/bootstrap/extras/persistence-external.xml
@@ -18,7 +18,7 @@
value="org.h2.Driver" />
+ value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1" />
diff --git a/documentation/src/main/asciidoc/userguide/chapters/bootstrap/extras/persistence.xml b/documentation/src/main/asciidoc/userguide/chapters/bootstrap/extras/persistence.xml
index 67e79c3dc042..b61f3160c33f 100644
--- a/documentation/src/main/asciidoc/userguide/chapters/bootstrap/extras/persistence.xml
+++ b/documentation/src/main/asciidoc/userguide/chapters/bootstrap/extras/persistence.xml
@@ -18,7 +18,7 @@
value="org.h2.Driver" />
+ value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1" />
diff --git a/documentation/src/main/asciidoc/userguide/chapters/domain/associations.adoc b/documentation/src/main/asciidoc/userguide/chapters/domain/associations.adoc
index 1e12b48ce4dd..a70735be9f3f 100644
--- a/documentation/src/main/asciidoc/userguide/chapters/domain/associations.adoc
+++ b/documentation/src/main/asciidoc/userguide/chapters/domain/associations.adoc
@@ -417,21 +417,39 @@ include::{extrasdir}/associations-many-to-many-bidirectional-with-link-entity-li
There is only one delete statement executed because, this time, the association is controlled by the `@ManyToOne` side which only has to monitor the state of the underlying foreign key relationship to trigger the right DML statement.
[[associations-not-found]]
-==== `@NotFound` association mapping
+==== `@NotFound`
-When dealing with associations which are not enforced by a Foreign Key,
-it's possible to bump into inconsistencies if the child record cannot reference a parent entity.
+When dealing with associations which are not enforced by a physical foreign-key, it is possible
+for a non-null foreign-key value to point to a non-existent value on the associated entity's table.
-By default, Hibernate will complain whenever a child association references a non-existing parent record.
-However, you can configure this behavior so that Hibernate can ignore such an Exception and simply assign `null` as a parent object referenced.
+[WARNING]
+====
+Not enforcing physical foreign-keys at the database level is highly discouraged.
+====
-To ignore non-existing parent entity references, even though not really recommended, it's possible to use the annotation `org.hibernate.annotation.NotFound` annotation with a value of `org.hibernate.annotations.NotFoundAction.IGNORE`.
+Hibernate provides support for such models using the `@NotFound` annotation, which accepts a
+`NotFoundAction` value which indicates how Hibernate should behave when such broken foreign-keys
+are encountered -
-[NOTE]
+EXCEPTION:: (default) Hibernate will throw an exception (`FetchNotFoundException`)
+IGNORE:: the association will be treated as `null`
+
+Both `@NotFound(IGNORE)` and `@NotFound(EXCEPTION)` cause Hibernate to assume that there is
+no physical foreign-key.
+
+`@ManyToOne` and `@OneToOne` associations annotated with `@NotFound` are always fetched eagerly even
+if the `fetch` strategy is set to `FetchType.LAZY`.
+
+
+[TIP]
====
-The `@ManyToOne` and `@OneToOne` associations that are annotated with `@NotFound(action = NotFoundAction.IGNORE)` are always fetched eagerly even if the `fetch` strategy is set to `FetchType.LAZY`.
+If the application itself manages the referential integrity and can guarantee that there are no
+broken foreign-keys, `jakarta.persistence.ForeignKey(NO_CONSTRAINT)` can be used instead.
+This will force Hibernate to not export physical foreign-keys, but still behave as if there is
+in terms of avoiding the downsides to `@NotFound`.
====
+
Considering the following `City` and `Person` entity mappings:
[[associations-not-found-domain-model-example]]
@@ -457,29 +475,29 @@ include::{sourcedir}/NotFoundTest.java[tags=associations-not-found-persist-examp
When loading the `Person` entity, Hibernate is able to locate the associated `City` parent entity:
[[associations-not-found-find-example]]
-.`@NotFound` find existing entity example
+.`@NotFound` - find existing entity example
====
[source,java]
----
-include::{sourcedir}/NotFoundTest.java[tags=associations-not-found-find-example,indent=0]
+include::{sourcedir}/NotFoundTest.java[tags=associations-not-found-find-baseline,indent=0]
----
====
-However, if we change the `cityName` attribute to a non-existing city's name:
+However, if we break the foreign-key:
[[associations-not-found-non-existing-persist-example]]
-.`@NotFound` change to non-existing City example
+.Break the foreign-key
====
[source,java]
----
-include::{sourcedir}/NotFoundTest.java[tags=associations-not-found-non-existing-persist-example,indent=0]
+include::{sourcedir}/NotFoundTest.java[tags=associations-not-found-break-fk,indent=0]
----
====
Hibernate is not going to throw any exception, and it will assign a value of `null` for the non-existing `City` entity reference:
[[associations-not-found-non-existing-find-example]]
-.`@NotFound` find non-existing City example
+.`@NotFound` - find non-existing City example
====
[source,java]
----
@@ -487,6 +505,62 @@ include::{sourcedir}/NotFoundTest.java[tags=associations-not-found-non-existing-
----
====
+`@NotFound` also affects how the association is treated as "implicit joins" in HQL and Criteria.
+When there is a physical foreign-key, Hibernate can safely assume that the value in the foreign-key's
+key-column(s) will match the value in the target-column(s) because the database makes sure that
+is the case. However, `@NotFound` forces Hibernate to perform a physical join for implicit joins
+when it might not be needed otherwise.
+
+Using the `Person` / `City` model, consider the query `from Person p where p.city.id is null`.
+
+Normally Hibernate would not need the join between the `Person` table and the `City` table because
+a physical foreign-key would ensure that any non-null value in the `Person.cityName` column
+has a matching non-null value in the `City.name` column.
+
+However, with `@NotFound` mappings it is possible to have a broken association because there is no
+physical foreign-key enforcing the relation. As seen in <>,
+the `Person.cityName` column for John Doe has been changed from "New York" to "Atlantis" even though
+there is no `City` in the database named "Atlantis". Hibernate is not able to trust the referring
+foreign-key value ("Atlantis") has a matching target value, so it must join to the `City` table to
+resolve the `city.id` value.
+
+
+[[associations-not-found-implicit-join-example]]
+.Implicit join example
+====
+[source,java]
+----
+include::{sourcedir}/NotFoundTest.java[tags=associations-not-found-implicit-join-example,indent=0]
+----
+====
+
+Neither result includes a match for "John Doe" because the inner-join filters out that row.
+
+Hibernate does support a means to refer specifically to the key column (`Person.cityName`) in a query
+using the special `fk(..)` function. E.g.
+
+[[associations-not-found-implicit-join-example]]
+.Implicit join example
+====
+[source,java]
+----
+include::{sourcedir}/NotFoundTest.java[tags=associations-not-found-fk-function-example,indent=0]
+----
+====
+
+With Hibernate Criteria it is possible to use `Projections.fk(...)` to select the foreign key value of an association
+and `Restrictions.fkEq(...)`, `Restrictions.fkNe(...)`, `Restrictions.fkIsNotNull(...)` and ``Restrictions.fkIsNull(...)`, E.g.
+
+[[associations-not-found-implicit-join-example]]
+.Implicit join example
+====
+[source,java]
+----
+include::{sourcedir}/NotFoundTest.java[tags=associations-not-found-fk-criteria-example,indent=0]
+----
+====
+
+
[[associations-any]]
==== `@Any` mapping
diff --git a/documentation/src/main/asciidoc/userguide/chapters/osgi/extras/datasource-h2.xml b/documentation/src/main/asciidoc/userguide/chapters/osgi/extras/datasource-h2.xml
index 25898baabf6f..35dec2dcb4c3 100644
--- a/documentation/src/main/asciidoc/userguide/chapters/osgi/extras/datasource-h2.xml
+++ b/documentation/src/main/asciidoc/userguide/chapters/osgi/extras/datasource-h2.xml
@@ -8,7 +8,7 @@ Then copy this file to the deploy folder
-
+
diff --git a/documentation/src/test/java/org/hibernate/userguide/associations/NotFoundTest.java b/documentation/src/test/java/org/hibernate/userguide/associations/NotFoundTest.java
index 004c51a5c63b..e903fa2b100d 100644
--- a/documentation/src/test/java/org/hibernate/userguide/associations/NotFoundTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/associations/NotFoundTest.java
@@ -1,21 +1,35 @@
package org.hibernate.userguide.associations;
import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
+import org.hibernate.Criteria;
+import org.hibernate.Session;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
+import org.hibernate.criterion.ForeignKeyExpression;
+import org.hibernate.criterion.ForeingKeyProjection;
+import org.hibernate.criterion.ProjectionList;
+import org.hibernate.criterion.Projections;
+import org.hibernate.criterion.PropertyProjection;
+import org.hibernate.criterion.Restrictions;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
+import org.hibernate.testing.jdbc.SQLStatementInterceptor;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
+import static org.hibernate.testing.transaction.TransactionUtil2.inTransaction;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -24,6 +38,13 @@
*/
public class NotFoundTest extends BaseEntityManagerFunctionalTestCase {
+ private SQLStatementInterceptor sqlStatementInterceptor;
+
+ @Override
+ protected void addConfigOptions(Map options) {
+ sqlStatementInterceptor = new SQLStatementInterceptor( options );
+ }
+
@Override
protected Class>[] getAnnotatedClasses() {
return new Class>[] {
@@ -32,75 +53,182 @@ protected Class>[] getAnnotatedClasses() {
};
}
- @Test
- public void test() {
- doInJPA( this::entityManagerFactory, entityManager -> {
+ @Before
+ public void createTestData() {
+ inTransaction( entityManagerFactory(), (entityManager) -> {
//tag::associations-not-found-persist-example[]
- City _NewYork = new City();
- _NewYork.setName( "New York" );
- entityManager.persist( _NewYork );
-
- Person person = new Person();
- person.setId( 1L );
- person.setName( "John Doe" );
- person.setCityName( "New York" );
+ City newYork = new City( 1, "New York" );
+ entityManager.persist( newYork );
+
+ Person person = new Person( 1, "John Doe", newYork );
entityManager.persist( person );
//end::associations-not-found-persist-example[]
} );
+ }
- doInJPA( this::entityManagerFactory, entityManager -> {
- //tag::associations-not-found-find-example[]
- Person person = entityManager.find( Person.class, 1L );
- assertEquals( "New York", person.getCity().getName() );
- //end::associations-not-found-find-example[]
+ @After
+ public void dropTestData() {
+ inTransaction( entityManagerFactory(), (em) -> {
+ em.createQuery( "delete Person" ).executeUpdate();
+ em.createQuery( "delete City" ).executeUpdate();
+ } );
+ }
- //tag::associations-not-found-non-existing-persist-example[]
- person.setCityName( "Atlantis" );
- //end::associations-not-found-non-existing-persist-example[]
+ @Test
+ public void test() {
+ doInJPA(this::entityManagerFactory, entityManager -> {
+ //tag::associations-not-found-find-baseline[]
+ Person person = entityManager.find(Person.class, 1);
+ assertEquals("New York", person.getCity().getName());
+ //end::associations-not-found-find-baseline[]
+ });
- } );
+ breakForeignKey();
- doInJPA( this::entityManagerFactory, entityManager -> {
+ doInJPA(this::entityManagerFactory, entityManager -> {
//tag::associations-not-found-non-existing-find-example[]
- Person person = entityManager.find( Person.class, 1L );
+ Person person = entityManager.find(Person.class, 1);
- assertEquals( "Atlantis", person.getCityName() );
- assertNull( null, person.getCity() );
+ assertNull(null, person.getCity());
//end::associations-not-found-non-existing-find-example[]
+ });
+ }
+
+ private void breakForeignKey() {
+ inTransaction( entityManagerFactory(), (em) -> {
+ //tag::associations-not-found-break-fk[]
+ // the database allows this because there is no physical foreign-key
+ em.createQuery( "delete City" ).executeUpdate();
+ //end::associations-not-found-break-fk[]
+ } );
+ }
+
+ @Test
+ public void queryTest() {
+ breakForeignKey();
+
+ inTransaction( entityManagerFactory(), (entityManager) -> {
+ //tag::associations-not-found-implicit-join-example[]
+ final List nullResults = entityManager
+ .createQuery( "from Person p where p.city.id is null", Person.class )
+ .list();
+ assertThat( nullResults ).isEmpty();
+
+ final List nonNullResults = entityManager
+ .createQuery( "from Person p where p.city.id is not null", Person.class )
+ .list();
+ assertThat( nonNullResults ).isEmpty();
+ //end::associations-not-found-implicit-join-example[]
+ } );
+ }
+
+ @Test
+ public void queryTestFk() {
+ breakForeignKey();
+
+ inTransaction( entityManagerFactory(), (entityManager) -> {
+ sqlStatementInterceptor.clear();
+ //tag::associations-not-found-fk-function-example[]
+ final List nullResults = entityManager
+ .createQuery( "select p.name from Person p where fk( p.city ) is null", String.class )
+ .list();
+
+ assertThat( nullResults ).isEmpty();
+
+ final List nonNullResults = entityManager
+ .createQuery( "select p.name from Person p where fk( p.city ) is not null", String.class )
+ .list();
+ assertThat( nonNullResults ).hasSize( 1 );
+ assertThat( nonNullResults.get( 0 ) ).isEqualTo( "John Doe" );
+ //end::associations-not-found-fk-function-example[]
+
+ // In addition, make sure that the two executed queries do not create a join
+ assertThat( sqlStatementInterceptor.getQueryCount() ).isEqualTo( 2 );
+ assertThat( sqlStatementInterceptor.getSqlQueries().get( 0 ) ).doesNotContain( " join " );
+ assertThat( sqlStatementInterceptor.getSqlQueries().get( 1 ) ).doesNotContain( " join " );
+ } );
+ }
+
+ @Test
+ public void cirteriaTestFk() {
+ breakForeignKey();
+
+ inTransaction( entityManagerFactory(), (entityManager) -> {
+ sqlStatementInterceptor.clear();
+ Session session = entityManager.unwrap( Session.class );
+ //tag::associations-not-found-fk-criteria-example[]
+ Criteria criteria = session.createCriteria( Person.class );
+ ProjectionList projList = Projections.projectionList();
+ projList.add( Projections.property( "name" ) );
+ criteria.setProjection( projList );
+ criteria.add( Restrictions.fkIsNull( "city" ) );
+ final List nullResults = criteria.list();
+
+ assertThat( nullResults ).isEmpty();
+
+ criteria = session.createCriteria( Person.class );
+ projList = Projections.projectionList();
+ projList.add( Projections.property( "name" ) );
+ criteria.setProjection( projList );
+ criteria.add( Restrictions.fkIsNotNull( "city" ) );
+ final List nonNullResults = criteria.list();
+
+ assertThat( nonNullResults ).hasSize( 1 );
+ assertThat( nonNullResults.get( 0 ) ).isEqualTo( "John Doe" );
+
+ // selecting Person -> city Foreign key
+ criteria = session.createCriteria( Person.class );
+ projList = Projections.projectionList();
+ projList.add( Projections.fk( "city" ) );
+ criteria.setProjection( projList );
+ criteria.add( Restrictions.fkIsNotNull( "city" ) );
+
+ final List foreigKeyResults = criteria.list();
+ assertThat( foreigKeyResults ).hasSize( 1 );
+ assertThat( foreigKeyResults.get( 0 ) ).isEqualTo( 1 );
+ //end::associations-not-found-fk-criteria-example[]
+
+ // In addition, make sure that the two executed queries do not create a join
+ assertThat( sqlStatementInterceptor.getQueryCount() ).isEqualTo( 3 );
+ assertThat( sqlStatementInterceptor.getSqlQueries().get( 0 ) ).doesNotContain( " join " );
+ assertThat( sqlStatementInterceptor.getSqlQueries().get( 1 ) ).doesNotContain( " join " );
+ assertThat( sqlStatementInterceptor.getSqlQueries().get( 2 ) ).doesNotContain( " join " );
} );
}
//tag::associations-not-found-domain-model-example[]
- @Entity
- @Table( name = "Person" )
+ @Entity(name = "Person")
+ @Table(name = "Person")
public static class Person {
@Id
- private Long id;
-
+ private Integer id;
private String name;
- private String cityName;
-
@ManyToOne
- @NotFound ( action = NotFoundAction.IGNORE )
- @JoinColumn(
- name = "cityName",
- referencedColumnName = "name",
- insertable = false,
- updatable = false
- )
+ @NotFound(action = NotFoundAction.IGNORE)
+ @JoinColumn(name = "city_fk", referencedColumnName = "id")
private City city;
//Getters and setters are omitted for brevity
- //end::associations-not-found-domain-model-example[]
+ //end::associations-not-found-domain-model-example[]
+
- public Long getId() {
+ public Person() {
+ }
+
+ public Person(Integer id, String name, City city) {
+ this.id = id;
+ this.name = name;
+ this.city = city;
+ }
+
+ public Integer getId() {
return id;
}
- public void setId(Long id) {
+ public void setId(Integer id) {
this.id = id;
}
@@ -112,40 +240,39 @@ public void setName(String name) {
this.name = name;
}
- public String getCityName() {
- return cityName;
- }
-
- public void setCityName(String cityName) {
- this.cityName = cityName;
- this.city = null;
- }
-
public City getCity() {
return city;
}
- //tag::associations-not-found-domain-model-example[]
+ //tag::associations-not-found-domain-model-example[]
}
- @Entity
- @Table( name = "City" )
+ @Entity(name = "City")
+ @Table(name = "City")
public static class City implements Serializable {
@Id
- @GeneratedValue
- private Long id;
+ private Integer id;
private String name;
//Getters and setters are omitted for brevity
- //end::associations-not-found-domain-model-example[]
+ //end::associations-not-found-domain-model-example[]
+
- public Long getId() {
+ public City() {
+ }
+
+ public City(Integer id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public Integer getId() {
return id;
}
- public void setId(Long id) {
+ public void setId(Integer id) {
this.id = id;
}
@@ -156,7 +283,7 @@ public String getName() {
public void setName(String name) {
this.name = name;
}
- //tag::associations-not-found-domain-model-example[]
+ //tag::associations-not-found-domain-model-example[]
}
//end::associations-not-found-domain-model-example[]
-}
\ No newline at end of file
+}
diff --git a/documentation/src/test/java/org/hibernate/userguide/caching/SecondLevelCacheTest.java b/documentation/src/test/java/org/hibernate/userguide/caching/SecondLevelCacheTest.java
index a5602e7ff903..f545f0d7ebe6 100644
--- a/documentation/src/test/java/org/hibernate/userguide/caching/SecondLevelCacheTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/caching/SecondLevelCacheTest.java
@@ -22,23 +22,27 @@
import org.hibernate.Session;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.NaturalId;
+import org.hibernate.annotations.NaturalIdCache;
import org.hibernate.cfg.AvailableSettings;
+import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.jpa.QueryHints;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.stat.CacheRegionStatistics;
import org.hibernate.stat.Statistics;
+import org.hibernate.testing.TestForIssue;
import org.junit.Ignore;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Vlad Mihalcea
*/
-@Ignore
+
//@FailureExpected( jiraKey = "HHH-12146", message = "No idea why those changes cause this to fail, especially in the way it does" )
public class SecondLevelCacheTest extends BaseEntityManagerFunctionalTestCase {
@@ -251,10 +255,89 @@ public void testCache() {
});
}
+ @Test
+ @TestForIssue( jiraKey = "HHH-14944") // issue is also reproduceable in Hibernate 5.4
+ public void testCacheVerifyHits() {
+ doInJPA( this::entityManagerFactory, entityManager -> {
+ entityManager.persist( new Person() );
+ Person aPerson= new Person();
+ aPerson.setName( "John Doe" );
+ aPerson.setCode( "unique-code" );
+ entityManager.persist( aPerson );
+ Session session = entityManager.unwrap(Session.class);
+ SessionFactoryImpl sfi = (SessionFactoryImpl) session.getSessionFactory();
+ sfi.getStatistics().clear();
+ return aPerson;
+ });
+
+ doInJPA(this::entityManagerFactory, entityManager -> {
+ log.info("Native load by natural-id, generate first hit");
+
+ Session session = entityManager.unwrap(Session.class);
+ SessionFactoryImpl sfi = (SessionFactoryImpl) session.getSessionFactory();
+ //tag::caching-entity-natural-id-example[]
+ Person person = session
+ .byNaturalId(Person.class)
+ .using("code", "unique-code")
+ .load();
+
+ assertNotNull(person);
+ log.info("NaturalIdCacheHitCount: " + sfi.getStatistics().getNaturalIdCacheHitCount());
+ log.info("SecondLevelCacheHitCount: " + sfi.getStatistics().getSecondLevelCacheHitCount());
+ assertEquals(1, sfi.getStatistics().getNaturalIdCacheHitCount());
+ assertEquals(1, sfi.getStatistics().getSecondLevelCacheHitCount());
+ //end::caching-entity-natural-id-example[]
+ });
+
+ doInJPA(this::entityManagerFactory, entityManager -> {
+ log.info("Native load by natural-id, generate second hit");
+
+ Session session = entityManager.unwrap(Session.class);
+ SessionFactoryImpl sfi = (SessionFactoryImpl) session.getSessionFactory();
+ //tag::caching-entity-natural-id-example[]
+ Person person = session.bySimpleNaturalId(Person.class).load("unique-code");
+ assertNotNull(person);
+
+ // resolve in persistence context (first level cache)
+ session.bySimpleNaturalId(Person.class).load("unique-code");
+ log.info("NaturalIdCacheHitCount: " + sfi.getStatistics().getNaturalIdCacheHitCount());
+ log.info("SecondLevelCacheHitCount: " + sfi.getStatistics().getSecondLevelCacheHitCount());
+ assertEquals(2, sfi.getStatistics().getNaturalIdCacheHitCount());
+ assertEquals(2, sfi.getStatistics().getSecondLevelCacheHitCount());
+
+ session.clear();
+ // persistence context (first level cache) empty, should resolve from second level cache
+ log.info("Native load by natural-id, generate third hit");
+ person = session.bySimpleNaturalId(Person.class).load("unique-code");
+ log.info("NaturalIdCacheHitCount: " + sfi.getStatistics().getNaturalIdCacheHitCount());
+ log.info("SecondLevelCacheHitCount: " + sfi.getStatistics().getSecondLevelCacheHitCount());
+ assertNotNull(person);
+ assertEquals(3, sfi.getStatistics().getNaturalIdCacheHitCount());
+ assertEquals(3, sfi.getStatistics().getSecondLevelCacheHitCount());
+
+ //Remove the entity from the persistence context
+ Long id = person.getId();
+
+ entityManager.detach(person); // still it should resolve from second level cache after this
+
+ log.info("Native load by natural-id, generate 4. hit");
+ person = session.bySimpleNaturalId(Person.class).load("unique-code");
+ log.info("NaturalIdCacheHitCount: " + sfi.getStatistics().getNaturalIdCacheHitCount());
+ assertEquals("we expected now 4 hits" , 4, sfi.getStatistics().getNaturalIdCacheHitCount());
+ assertNotNull(person);
+ session.delete(person); // evicts natural-id from first & second level cache
+ person = session.bySimpleNaturalId(Person.class).load("unique-code");
+ assertEquals(4, sfi.getStatistics().getNaturalIdCacheHitCount()); // thus hits should not increment
+
+ //end::caching-entity-natural-id-example[]
+ });
+ }
+
//tag::caching-entity-natural-id-mapping-example[]
@Entity(name = "Person")
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+ @NaturalIdCache
public static class Person {
@Id
diff --git a/documentation/src/test/java/org/hibernate/userguide/envers/EntityTypeChangeAuditTest.java b/documentation/src/test/java/org/hibernate/userguide/envers/EntityTypeChangeAuditTest.java
index b6fc6d3bdc5e..65d7480b6fb7 100644
--- a/documentation/src/test/java/org/hibernate/userguide/envers/EntityTypeChangeAuditTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/envers/EntityTypeChangeAuditTest.java
@@ -9,7 +9,6 @@
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Column;
diff --git a/documentation/src/test/java/org/hibernate/userguide/fetching/FetchingTest.java b/documentation/src/test/java/org/hibernate/userguide/fetching/FetchingTest.java
index 2dbec1f5b64d..d3121d620cec 100644
--- a/documentation/src/test/java/org/hibernate/userguide/fetching/FetchingTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/fetching/FetchingTest.java
@@ -28,7 +28,9 @@
import org.hibernate.dialect.H2Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
+import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialect;
+import org.hibernate.testing.RequiresDialectFeature;
import org.junit.Test;
import org.jboss.logging.Logger;
@@ -41,6 +43,7 @@
* @author Vlad Mihalcea
*/
@RequiresDialect(H2Dialect.class)
+@RequiresDialectFeature(value = DialectChecks.NotH2Version2.class, comment = "See https://github.com/h2database/h2database/issues/3338")
public class FetchingTest extends BaseEntityManagerFunctionalTestCase {
@Override
@@ -181,6 +184,12 @@ public static class Employee {
read = "decrypt( 'AES', '00', pswd )",
write = "encrypt('AES', '00', ?)"
)
+// For H2 2.0.202+ one must use the varbinary DDL type
+// @Column(name = "pswd", columnDefinition = "varbinary")
+// @ColumnTransformer(
+// read = "trim(trailing u&'\\0000' from cast(decrypt('AES', '00', pswd ) as character varying))",
+// write = "encrypt('AES', '00', ?)"
+// )
private String password;
private int accessLevel;
diff --git a/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java b/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java
index 98070dc5f67a..95027b62eb96 100644
--- a/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java
@@ -31,6 +31,7 @@
import org.hibernate.dialect.Oracle8iDialect;
import org.hibernate.dialect.PostgreSQL81Dialect;
import org.hibernate.dialect.SQLServerDialect;
+import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.type.StringType;
import org.hibernate.userguide.model.AddressType;
@@ -1314,6 +1315,7 @@ public void test_hql_sqrt_function_example() {
@Test
@SkipForDialect(SQLServerDialect.class)
+ @SkipForDialect(value = SybaseASE15Dialect.class, comment = "current_date requires parenthesis which we don't render")
@SkipForDialect(value = DerbyDialect.class, comment = "Comparisons between 'DATE' and 'TIMESTAMP' are not supported")
public void test_hql_current_date_function_example() {
doInJPA( this::entityManagerFactory, entityManager -> {
@@ -1356,6 +1358,7 @@ public void test_hql_current_time_function_example() {
}
@Test
+ @SkipForDialect(value = SybaseASE15Dialect.class, comment = "current_timestamp requires parenthesis which we don't render")
public void test_hql_current_timestamp_function_example() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::hql-current-timestamp-function-example[]
@@ -1428,6 +1431,7 @@ public void test_hql_year_function_example() {
@Test
@SkipForDialect(SQLServerDialect.class)
+ @SkipForDialect(value = SybaseASE15Dialect.class, comment = "No proper implementation for the STR function available")
public void test_hql_str_function_example() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::hql-str-function-example[]
diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BitSetUserTypeTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BitSetUserTypeTest.java
index 6a7a7ffb52f7..91c7d25e3597 100644
--- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BitSetUserTypeTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BitSetUserTypeTest.java
@@ -7,6 +7,7 @@
package org.hibernate.userguide.mapping.basic;
import java.util.BitSet;
+import javax.persistence.Column;
import javax.persistence.ColumnResult;
import javax.persistence.ConstructorResult;
import javax.persistence.Entity;
@@ -94,7 +95,7 @@ protected boolean isCleanupTestDataRequired() {
query =
"SELECT " +
" pr.id AS \"pr.id\", " +
- " pr.bitset AS \"pr.bitset\" " +
+ " pr.bitset_col AS \"pr.bitset\" " +
"FROM Product pr " +
"WHERE pr.id = :id",
resultSetMapping = "Person"
@@ -117,6 +118,7 @@ public static class Product {
private Integer id;
@Type( type = "bitset" )
+ @Column(name = "bitset_col")
private BitSet bitSet;
//Constructors, getters, and setters are omitted for brevity
diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ClobCharArrayTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ClobCharArrayTest.java
index feb6a1591ceb..c56769569d2b 100644
--- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ClobCharArrayTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ClobCharArrayTest.java
@@ -10,8 +10,10 @@
import javax.persistence.Id;
import javax.persistence.Lob;
+import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
+import org.hibernate.testing.SkipForDialect;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
@@ -20,6 +22,7 @@
/**
* @author Vlad Mihalcea
*/
+@SkipForDialect(value = SybaseASE15Dialect.class, comment = "jTDS driver doesn't implement character stream handling")
public class ClobCharArrayTest extends BaseEntityManagerFunctionalTestCase {
@Override
diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ClobStringTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ClobStringTest.java
index 5df1221983db..0a216a292ce9 100644
--- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ClobStringTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ClobStringTest.java
@@ -10,8 +10,10 @@
import javax.persistence.Id;
import javax.persistence.Lob;
+import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
+import org.hibernate.testing.SkipForDialect;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
@@ -20,6 +22,7 @@
/**
* @author Vlad Mihalcea
*/
+@SkipForDialect(value = SybaseASE15Dialect.class, comment = "jTDS driver doesn't implement character stream handling")
public class ClobStringTest extends BaseEntityManagerFunctionalTestCase {
@Override
diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ClobTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ClobTest.java
index 503413c63659..f7edd1f10998 100644
--- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ClobTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ClobTest.java
@@ -16,9 +16,11 @@
import javax.persistence.Lob;
import org.hibernate.Session;
+import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.engine.jdbc.ClobProxy;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
+import org.hibernate.testing.SkipForDialect;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
@@ -28,6 +30,7 @@
/**
* @author Vlad Mihalcea
*/
+@SkipForDialect(value = SybaseASE15Dialect.class, comment = "jTDS driver doesn't implement character stream handling")
public class ClobTest extends BaseEntityManagerFunctionalTestCase {
@Override
diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobCharArrayTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobCharArrayTest.java
index 50fa6a64d2d1..f2caf437df51 100644
--- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobCharArrayTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobCharArrayTest.java
@@ -14,6 +14,7 @@
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.MySQL5Dialect;
import org.hibernate.dialect.PostgreSQL81Dialect;
+import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.SkipForDialect;
@@ -33,6 +34,7 @@
},
comment = "@see https://hibernate.atlassian.net/browse/HHH-10693 and https://hibernate.atlassian.net/browse/HHH-10695 and https://hibernate.atlassian.net/browse/HHH-10473"
)
+@SkipForDialect(value = SybaseASE15Dialect.class, comment = "jTDS driver doesn't implement nationalized handling")
public class NClobCharArrayTest extends BaseEntityManagerFunctionalTestCase {
@Override
diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobStringTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobStringTest.java
index 03d20f9234bb..15c7b5e63120 100644
--- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobStringTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobStringTest.java
@@ -14,6 +14,7 @@
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.MySQL5Dialect;
import org.hibernate.dialect.PostgreSQL81Dialect;
+import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.SkipForDialect;
@@ -33,6 +34,7 @@
},
comment = "@see https://hibernate.atlassian.net/browse/HHH-10693 and https://hibernate.atlassian.net/browse/HHH-10695 and https://hibernate.atlassian.net/browse/HHH-10473"
)
+@SkipForDialect(value = SybaseASE15Dialect.class, comment = "jTDS driver doesn't implement nationalized handling")
public class NClobStringTest extends BaseEntityManagerFunctionalTestCase {
@Override
diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobTest.java
index 8061589e4253..01eaae8b2035 100644
--- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobTest.java
@@ -22,6 +22,7 @@
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.MySQL5Dialect;
import org.hibernate.dialect.PostgreSQL81Dialect;
+import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.engine.jdbc.NClobProxy;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
@@ -45,6 +46,7 @@
},
comment = "@see https://hibernate.atlassian.net/browse/HHH-10693 and https://hibernate.atlassian.net/browse/HHH-10695 and https://hibernate.atlassian.net/browse/HHH-10473"
)
+@SkipForDialect(value = SybaseASE15Dialect.class, comment = "jTDS driver doesn't implement nationalized handling")
public class NClobTest extends BaseEntityManagerFunctionalTestCase {
@Override
diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NationalizedTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NationalizedTest.java
index 22ba662aa263..c3257a988c1a 100644
--- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NationalizedTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NationalizedTest.java
@@ -12,6 +12,7 @@
import org.hibernate.annotations.Nationalized;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.PostgreSQL81Dialect;
+import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.SkipForDialect;
@@ -30,6 +31,7 @@
},
comment = "@see https://hibernate.atlassian.net/browse/HHH-10693 and Derby doesn't support nationalized type"
)
+@SkipForDialect(value = SybaseASE15Dialect.class, comment = "jTDS driver doesn't implement nationalized handling")
public class NationalizedTest extends BaseEntityManagerFunctionalTestCase {
@Override
diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/SubselectTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/SubselectTest.java
index d64f14813fe6..438f72f7b954 100644
--- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/SubselectTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/SubselectTest.java
@@ -16,6 +16,7 @@
import org.hibernate.annotations.Subselect;
import org.hibernate.annotations.Synchronize;
import org.hibernate.dialect.DerbyDialect;
+import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.SkipForDialect;
@@ -28,6 +29,7 @@
* @author Vlad Mihalcea
*/
@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't support a CONCAT function")
+@SkipForDialect(value = SybaseASE15Dialect.class, comment = "Sybase doesn't support a CONCAT function")
public class SubselectTest extends BaseEntityManagerFunctionalTestCase {
@Override
diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/generated/DatabaseValueGenerationTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/generated/DatabaseValueGenerationTest.java
index 870997a9b4d3..fec3260c73f6 100644
--- a/documentation/src/test/java/org/hibernate/userguide/mapping/generated/DatabaseValueGenerationTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/mapping/generated/DatabaseValueGenerationTest.java
@@ -15,11 +15,13 @@
import javax.persistence.Id;
import org.hibernate.annotations.ValueGenerationType;
+import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.tuple.AnnotationValueGeneration;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGenerator;
+import org.hibernate.testing.SkipForDialect;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
@@ -27,6 +29,7 @@
/**
* @author Vlad Mihalcea
*/
+@SkipForDialect(value = SybaseASE15Dialect.class, comment = "current_timestamp requires parenthesis which we don't render")
public class DatabaseValueGenerationTest extends BaseEntityManagerFunctionalTestCase {
@Override
diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/identifier/composite/EmbeddedIdDatabaseGeneratedValueTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/identifier/composite/EmbeddedIdDatabaseGeneratedValueTest.java
index 1bd0ab711c35..f2162253f897 100644
--- a/documentation/src/test/java/org/hibernate/userguide/mapping/identifier/composite/EmbeddedIdDatabaseGeneratedValueTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/mapping/identifier/composite/EmbeddedIdDatabaseGeneratedValueTest.java
@@ -11,7 +11,9 @@
import org.hibernate.dialect.H2Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
+import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialect;
+import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
@@ -22,6 +24,7 @@
* @author Vlad Mihalcea
*/
@RequiresDialect(H2Dialect.class)
+@RequiresDialectFeature(value = DialectChecks.NotH2Version2.class, comment = "CURRENT_TIMESTAMP now returns a TIMESTAMP_WITH_TIME_ZONE")
public class EmbeddedIdDatabaseGeneratedValueTest extends BaseEntityManagerFunctionalTestCase {
@Override
diff --git a/documentation/src/test/java/org/hibernate/userguide/pc/CascadeOnDeleteTest.java b/documentation/src/test/java/org/hibernate/userguide/pc/CascadeOnDeleteTest.java
index be78241ad8ed..5afa0bd0b976 100644
--- a/documentation/src/test/java/org/hibernate/userguide/pc/CascadeOnDeleteTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/pc/CascadeOnDeleteTest.java
@@ -10,6 +10,8 @@
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
+import org.hibernate.testing.DialectChecks;
+import org.hibernate.testing.RequiresDialectFeature;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
@@ -17,6 +19,7 @@
/**
* @author Vlad Mihalcea
*/
+@RequiresDialectFeature(DialectChecks.SupportsCascadeDeleteCheck.class)
public class CascadeOnDeleteTest extends BaseEntityManagerFunctionalTestCase {
@Override
diff --git a/documentation/src/test/java/org/hibernate/userguide/schema/UniqueConstraintTest.java b/documentation/src/test/java/org/hibernate/userguide/schema/UniqueConstraintTest.java
index ba6f042ff885..6651ccd70f30 100644
--- a/documentation/src/test/java/org/hibernate/userguide/schema/UniqueConstraintTest.java
+++ b/documentation/src/test/java/org/hibernate/userguide/schema/UniqueConstraintTest.java
@@ -19,9 +19,11 @@
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.DynamicInsert;
+import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
+import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.util.ExceptionUtil;
import org.junit.Test;
@@ -44,6 +46,7 @@ protected Class>[] getAnnotatedClasses() {
}
@Test
+ @SkipForDialect(value = SybaseASE15Dialect.class, comment = "Missing constraint violation extractor")
public void test() {
//tag::schema-generation-columns-unique-constraint-persist-example[]
Author _author = doInJPA( this::entityManagerFactory, entityManager -> {
diff --git a/documentation/src/test/resources/hibernate.properties b/documentation/src/test/resources/hibernate.properties
index 8b93710f2628..968847a8f3f7 100644
--- a/documentation/src/test/resources/hibernate.properties
+++ b/documentation/src/test/resources/hibernate.properties
@@ -10,6 +10,7 @@ hibernate.connection.driver_class @jdbc.driver@
hibernate.connection.url @jdbc.url@
hibernate.connection.username @jdbc.user@
hibernate.connection.password @jdbc.pass@
+hibernate.connection.init_sql @connection.init_sql@
hibernate.connection.pool_size 5
diff --git a/gradle/databases.gradle b/gradle/databases.gradle
index cf54fda5dd4d..bd2b65b16043 100644
--- a/gradle/databases.gradle
+++ b/gradle/databases.gradle
@@ -16,20 +16,23 @@ ext {
'jdbc.user' : 'sa',
'jdbc.pass' : '',
'jdbc.url' : 'jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000',
+ 'connection.init_sql' : ''
],
hsqldb : [
'db.dialect' : 'org.hibernate.dialect.HSQLDialect',
'jdbc.driver': 'org.hsqldb.jdbc.JDBCDriver',
'jdbc.user' : 'sa',
'jdbc.pass' : '',
- 'jdbc.url' : 'jdbc:hsqldb:mem:test'
+ 'jdbc.url' : 'jdbc:hsqldb:mem:test',
+ 'connection.init_sql' : ''
],
derby : [
'db.dialect' : 'org.hibernate.dialect.DerbyTenSevenDialect',
'jdbc.driver': 'org.apache.derby.jdbc.EmbeddedDriver',
'jdbc.user' : 'hibernate_orm_test',
'jdbc.pass' : 'hibernate_orm_test',
- 'jdbc.url' : 'jdbc:derby:target/tmp/derby/hibernate_orm_test;databaseName=hibernate_orm_test;create=true'
+ 'jdbc.url' : 'jdbc:derby:target/tmp/derby/hibernate_orm_test;databaseName=hibernate_orm_test;create=true',
+ 'connection.init_sql' : ''
],
pgsql : [
'db.dialect' : 'org.hibernate.dialect.PostgreSQL95Dialect',
@@ -37,7 +40,8 @@ ext {
'jdbc.user' : 'hibernate_orm_test',
'jdbc.pass' : 'hibernate_orm_test',
// Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com
- 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0'
+ 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0',
+ 'connection.init_sql' : ''
],
pgsql_docker : [
'db.dialect' : 'org.hibernate.dialect.PostgreSQL10Dialect',
@@ -45,7 +49,8 @@ ext {
'jdbc.user' : 'hibernate_orm_test',
'jdbc.pass' : 'hibernate_orm_test',
// Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com
- 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0'
+ 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0',
+ 'connection.init_sql' : ''
],
pgsql_ci : [
'db.dialect' : 'org.hibernate.dialect.PostgreSQL95Dialect',
@@ -53,21 +58,41 @@ ext {
'jdbc.user' : 'hibernate_orm_test',
'jdbc.pass' : 'hibernate_orm_test',
// Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com
- 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0'
+ 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0',
+ 'connection.init_sql' : ''
+ ],
+ sybase_ci : [
+ 'db.dialect' : 'org.hibernate.dialect.SybaseASE157Dialect',
+ 'jdbc.driver': 'net.sourceforge.jtds.jdbc.Driver',
+ 'jdbc.user' : 'hibernate_orm_test',
+ 'jdbc.pass' : 'hibernate_orm_test',
+ // Disable prepared statement caching to avoid issues with changing schemas
+ 'jdbc.url' : 'jdbc:jtds:sybase://' + dbHost + ':5000/hibernate_orm_test;maxStatements=0;cacheMetaData=false',
+ 'connection.init_sql' : 'set ansinull on'
],
mysql : [
'db.dialect' : 'org.hibernate.dialect.MySQL57Dialect',
'jdbc.driver': 'com.mysql.jdbc.Driver',
'jdbc.user' : 'hibernateormtest',
'jdbc.pass' : 'hibernateormtest',
- 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test'
+ 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test',
+ 'connection.init_sql' : ''
],
mysql_docker : [
'db.dialect' : 'org.hibernate.dialect.MySQL57Dialect',
'jdbc.driver': 'com.mysql.jdbc.Driver',
'jdbc.user' : 'hibernate_orm_test',
'jdbc.pass' : 'hibernate_orm_test',
- 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test?useSSL=false'
+ 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test?useSSL=false',
+ 'connection.init_sql' : ''
+ ],
+ mysql_ci : [
+ 'db.dialect' : 'org.hibernate.dialect.MySQL8Dialect',
+ 'jdbc.driver': 'com.mysql.jdbc.Driver',
+ 'jdbc.user' : 'hibernate_orm_test',
+ 'jdbc.pass' : 'hibernate_orm_test',
+ 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test?allowPublicKeyRetrieval=true',
+ 'connection.init_sql' : ''
],
// uses docker mysql_8_0
mysql8_spatial_ci: [
@@ -75,28 +100,32 @@ ext {
'jdbc.driver': 'com.mysql.cj.jdbc.Driver',
'jdbc.user' : 'hibernate_orm_test',
'jdbc.pass' : 'hibernate_orm_test',
- 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test?allowPublicKeyRetrieval=true&useSSL=false'
+ 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test?allowPublicKeyRetrieval=true&useSSL=false',
+ 'connection.init_sql' : ''
],
mariadb : [
'db.dialect' : 'org.hibernate.dialect.MariaDB103Dialect',
'jdbc.driver': 'org.mariadb.jdbc.Driver',
'jdbc.user' : 'hibernate_orm_test',
'jdbc.pass' : 'hibernate_orm_test',
- 'jdbc.url' : 'jdbc:mariadb://' + dbHost + '/hibernate_orm_test'
+ 'jdbc.url' : 'jdbc:mariadb://' + dbHost + '/hibernate_orm_test',
+ 'connection.init_sql' : ''
],
mariadb_ci : [
'db.dialect' : 'org.hibernate.dialect.MariaDB103Dialect',
'jdbc.driver': 'org.mariadb.jdbc.Driver',
'jdbc.user' : 'root',
'jdbc.pass' : 'hibernate_orm_test',
- 'jdbc.url' : 'jdbc:mariadb://' + dbHost + '/hibernate_orm_test'
+ 'jdbc.url' : 'jdbc:mariadb://' + dbHost + '/hibernate_orm_test',
+ 'connection.init_sql' : ''
],
mariadb_spatial_ci : [
'db.dialect' : 'org.hibernate.spatial.dialect.mariadb.MariaDB103SpatialDialect',
'jdbc.driver': 'org.mariadb.jdbc.Driver',
'jdbc.user' : 'root',
'jdbc.pass' : 'hibernate_orm_test',
- 'jdbc.url' : 'jdbc:mariadb://' + dbHost + '/hibernate_orm_test'
+ 'jdbc.url' : 'jdbc:mariadb://' + dbHost + '/hibernate_orm_test',
+ 'connection.init_sql' : ''
],
postgis : [
'db.dialect' : 'org.hibernate.spatial.dialect.postgis.PostgisPG95Dialect',
@@ -104,14 +133,24 @@ ext {
'jdbc.user' : 'hibernate_orm_test',
'jdbc.pass' : 'hibernate_orm_test',
// Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com
- 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0'
+ 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0',
+ 'connection.init_sql' : ''
],
oracle : [
'db.dialect' : 'org.hibernate.dialect.Oracle10gDialect',
'jdbc.driver': 'oracle.jdbc.OracleDriver',
'jdbc.user' : 'hibernate_orm_test',
'jdbc.pass' : 'hibernate_orm_test',
- 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521/xe'
+ 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521/xe',
+ 'connection.init_sql' : ''
+ ],
+ oracle_jenkins : [
+ 'db.dialect' : 'org.hibernate.dialect.Oracle12cDialect',
+ 'jdbc.driver': 'oracle.jdbc.OracleDriver',
+ 'jdbc.user' : 'hibernate_orm_test',
+ 'jdbc.pass' : 'hibernate_orm_test',
+ 'jdbc.url' : 'jdbc:oracle:thin:@hibernate-testing-oracle-se.ccuzkqo3zqzq.us-east-1.rds.amazonaws.com:1521:ORCL',
+ 'connection.init_sql' : ''
],
// Use ./docker_db.sh oracle_ee to start the database
oracle_docker : [
@@ -119,70 +158,80 @@ ext {
'jdbc.driver': 'oracle.jdbc.OracleDriver',
'jdbc.user' : 'c##hibernate_orm_test',
'jdbc.pass' : 'hibernate_orm_test',
- 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521/ORCLPDB1.localdomain'
+ 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521/ORCLPDB1.localdomain',
+ 'connection.init_sql' : ''
],
oracle_ci : [
'db.dialect' : 'org.hibernate.dialect.Oracle12cDialect',
'jdbc.driver': 'oracle.jdbc.OracleDriver',
'jdbc.user' : 'SYSTEM',
'jdbc.pass' : 'Oracle18',
- 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521:XE'
+ 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521:XE',
+ 'connection.init_sql' : ''
],
oracle_spatial_ci : [
'db.dialect' : 'org.hibernate.spatial.dialect.oracle.OracleSpatial10gDialect',
'jdbc.driver': 'oracle.jdbc.OracleDriver',
'jdbc.user' : 'SYSTEM',
'jdbc.pass' : 'Oracle18',
- 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521:XE'
+ 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521:XE',
+ 'connection.init_sql' : ''
],
mssql : [
'db.dialect' : 'org.hibernate.dialect.SQLServer2012Dialect',
'jdbc.driver': 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
'jdbc.user' : 'hibernate_orm_test',
'jdbc.pass' : 'hibernate_orm_test',
- 'jdbc.url' : 'jdbc:sqlserver://' + dbHost + ';instance=SQLEXPRESS;databaseName=hibernate_orm_test'
+ 'jdbc.url' : 'jdbc:sqlserver://' + dbHost + ';instance=SQLEXPRESS;databaseName=hibernate_orm_test',
+ 'connection.init_sql' : ''
],
mssql_ci : [
'db.dialect' : 'org.hibernate.dialect.SQLServer2012Dialect',
'jdbc.driver': 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
'jdbc.user' : 'sa',
'jdbc.pass' : 'Hibernate_orm_test',
- 'jdbc.url' : 'jdbc:sqlserver://' + dbHost + ';databaseName=hibernate_orm_test'
+ 'jdbc.url' : 'jdbc:sqlserver://' + dbHost + ';databaseName=hibernate_orm_test;sendTimeAsDatetime=false',
+ 'connection.init_sql' : ''
],
mssql_spatial_ci : [
'db.dialect' : 'org.hibernate.spatial.dialect.sqlserver.SqlServer2012SpatialDialect',
'jdbc.driver': 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
'jdbc.user' : 'sa',
'jdbc.pass' : 'Hibernate_orm_test',
- 'jdbc.url' : 'jdbc:sqlserver://' + dbHost + ';databaseName=hibernate_orm_test'
+ 'jdbc.url' : 'jdbc:sqlserver://' + dbHost + ';databaseName=hibernate_orm_test',
+ 'connection.init_sql' : ''
],
informix : [
'db.dialect' : 'org.hibernate.dialect.InformixDialect',
'jdbc.driver': 'com.informix.jdbc.IfxDriver',
'jdbc.user' : 'informix',
'jdbc.pass' : 'in4mix',
- 'jdbc.url' : 'jdbc:informix-sqli://' + dbHost + ':9088/sysuser:INFORMIXSERVER=dev;user=informix;password=in4mix'
+ 'jdbc.url' : 'jdbc:informix-sqli://' + dbHost + ':9088/sysuser:INFORMIXSERVER=dev;user=informix;password=in4mix',
+ 'connection.init_sql' : ''
],
db2 : [
'db.dialect' : 'org.hibernate.dialect.DB2Dialect',
'jdbc.driver': 'com.ibm.db2.jcc.DB2Driver',
'jdbc.user' : 'db2inst1',
'jdbc.pass' : 'db2inst1-pwd',
- 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/hibern8'
+ 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/hibern8',
+ 'connection.init_sql' : ''
],
db2_ci : [
'db.dialect' : 'org.hibernate.dialect.DB2Dialect',
'jdbc.driver': 'com.ibm.db2.jcc.DB2Driver',
'jdbc.user' : 'orm_test',
'jdbc.pass' : 'orm_test',
- 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/orm_test'
+ 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/orm_test',
+ 'connection.init_sql' : ''
],
db2_spatial_ci : [
'db.dialect' : 'org.hibernate.spatial.dialect.db2.DB2SpatialDialect',
'jdbc.driver': 'com.ibm.db2.jcc.DB2Driver',
'jdbc.user' : 'orm_test',
'jdbc.pass' : 'orm_test',
- 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/orm_test'
+ 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/orm_test',
+ 'connection.init_sql' : ''
],
hana : [
'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect',
@@ -190,7 +239,8 @@ ext {
'jdbc.user' : 'HIBERNATE_TEST',
'jdbc.pass' : 'H1bernate_test',
// Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html
- 'jdbc.url' : 'jdbc:sap://' + dbHost + ':30015/?statementCacheSize=0'
+ 'jdbc.url' : 'jdbc:sap://' + dbHost + ':30015/?statementCacheSize=0',
+ 'connection.init_sql' : ''
],
hana_cloud : [
'db.dialect' : 'org.hibernate.dialect.HANACloudColumnStoreDialect',
@@ -198,7 +248,17 @@ ext {
'jdbc.user' : 'HIBERNATE_TEST',
'jdbc.pass' : 'H1bernate_test',
// Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html
- 'jdbc.url' : 'jdbc:sap://' + dbHost + ':443/?encrypt=true&validateCertificate=false&statementCacheSize=0'
+ 'jdbc.url' : 'jdbc:sap://' + dbHost + ':443/?encrypt=true&validateCertificate=false&statementCacheSize=0',
+ 'connection.init_sql' : ''
+ ],
+ hana_jenkins : [
+ 'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect',
+ 'jdbc.driver': 'com.sap.db.jdbc.Driver',
+ 'jdbc.user' : 'HIBERNATE_TEST',
+ 'jdbc.pass' : 'H1bernate_test',
+ // Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html
+ 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39015/?statementCacheSize=0',
+ 'connection.init_sql' : ''
],
hana_vlad : [
'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect',
@@ -206,7 +266,8 @@ ext {
'jdbc.user' : 'VLAD',
'jdbc.pass' : 'V1ad_test',
// Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html
- 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39015/?statementCacheSize=0'
+ 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39015/?statementCacheSize=0',
+ 'connection.init_sql' : ''
],
hana_docker : [
'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect',
@@ -214,7 +275,8 @@ ext {
'jdbc.user' : 'SYSTEM',
'jdbc.pass' : 'H1bernate_test',
// Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html
- 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39017/?statementCacheSize=0'
+ 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39017/?statementCacheSize=0',
+ 'connection.init_sql' : ''
],
hana_ci : [
'db.dialect' : 'org.hibernate.dialect.HANAColumnStoreDialect',
@@ -222,7 +284,8 @@ ext {
'jdbc.user' : 'SYSTEM',
'jdbc.pass' : 'H1bernate_test',
// Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html
- 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39017/?statementCacheSize=0'
+ 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39017/?statementCacheSize=0',
+ 'connection.init_sql' : ''
],
hana_spatial_ci : [
'db.dialect' : 'org.hibernate.spatial.dialect.hana.HANASpatialDialect',
@@ -230,7 +293,8 @@ ext {
'jdbc.user' : 'SYSTEM',
'jdbc.pass' : 'H1bernate_test',
// Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html
- 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39017/?statementCacheSize=0'
+ 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39017/?statementCacheSize=0',
+ 'connection.init_sql' : ''
],
cockroachdb : [
'db.dialect' : 'org.hibernate.dialect.CockroachDB192Dialect',
@@ -239,7 +303,8 @@ ext {
'jdbc.user' : 'root',
'jdbc.pass' : '',
// Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com
- 'jdbc.url' : 'jdbc:postgresql://' + dbHost + ':26257/defaultdb?sslmode=disable&preparedStatementCacheQueries=0'
+ 'jdbc.url' : 'jdbc:postgresql://' + dbHost + ':26257/defaultdb?sslmode=disable&preparedStatementCacheQueries=0',
+ 'connection.init_sql' : ''
],
cockroachdb_spatial : [
'db.dialect' : 'org.hibernate.spatial.dialect.cockroachdb.CockroachDB202SpatialDialect',
@@ -248,7 +313,8 @@ ext {
'jdbc.user' : 'root',
'jdbc.pass' : '',
// Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com
- 'jdbc.url' : 'jdbc:postgresql://' + dbHost + ':26257/defaultdb?sslmode=disable&preparedStatementCacheQueries=0'
+ 'jdbc.url' : 'jdbc:postgresql://' + dbHost + ':26257/defaultdb?sslmode=disable&preparedStatementCacheQueries=0',
+ 'connection.init_sql' : ''
]
]
}
diff --git a/gradle/java-module.gradle b/gradle/java-module.gradle
index f4bb0dbfce63..ca8f48168fe1 100644
--- a/gradle/java-module.gradle
+++ b/gradle/java-module.gradle
@@ -79,17 +79,14 @@ dependencies {
testRuntime( libraries.derby )
testRuntime( libraries.hsqldb )
testRuntime( libraries.postgresql )
- testRuntime( libraries.mysql )
- testRuntime( libraries.mariadb )
testRuntime( libraries.mssql )
testRuntime( libraries.informix )
- testRuntime( libraries.hana )
testRuntime( libraries.cockroachdb )
+ testRuntime( libraries.oracle )
+ testRuntime( libraries.sybase )
asciidoclet 'org.asciidoctor:asciidoclet:1.+'
- testRuntime( libraries.oracle )
-
// Since both the DB2 driver and HANA have a package "net.jpountz" we have to add dependencies conditionally
// This is due to the "no split-packages" requirement of Java 9+
@@ -99,6 +96,12 @@ dependencies {
else if ( db.startsWith( 'hana' ) ) {
testRuntime( libraries.hana )
}
+ else if ( db.startsWith( 'mysql' ) ) {
+ testRuntimeOnly libraries.mysql
+ }
+ else if ( db.startsWith( 'mariadb' ) ) {
+ testRuntimeOnly libraries.mariadb
+ }
// Mac-specific
project.ext.toolsJar = file("${System.getProperty('java.home')}/../lib/tools.jar")
diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle
index 7ddc2ff28f03..4c2b17a1b83c 100644
--- a/gradle/libraries.gradle
+++ b/gradle/libraries.gradle
@@ -8,10 +8,12 @@
// build a map of the dependency artifacts to use. Allows centralized definition of the version of artifacts to
// use. In that respect it serves a role similar to in Maven
ext {
+ junit5Version = '5.8.2'
+ junitVintageVersion = junit5Version
+ junit4Version = '4.13.2'
- junitVersion = '4.13.2'
h2Version = '1.4.197'
- bytemanVersion = '4.0.16' //Compatible with JDK 17
+ bytemanVersion = '4.0.20' //Compatible with JDK 20
jnpVersion = '5.0.6.CR1'
hibernateCommonsVersion = '5.1.2.Final'
@@ -24,7 +26,7 @@ ext {
weldVersion = '3.1.5.Final'
jakartaWeldVersion = '4.0.1.SP1'
- byteBuddyVersion = '1.11.20'
+ byteBuddyVersion = '1.12.18'
agroalVersion = '1.9'
@@ -44,9 +46,9 @@ ext {
jakartaJaxbRuntimeVersion = '3.0.0'
//GraalVM
- graalvmVersion = '21.2.0'
+ graalvmVersion = '21.3.0'
- micrometerVersion = '1.6.1'
+ micrometerVersion = '1.9.3'
libraries = [
// Ant
@@ -57,7 +59,7 @@ ext {
// Annotations
commons_annotations: "org.hibernate.common:hibernate-commons-annotations:${hibernateCommonsVersion}",
- jandex: 'org.jboss:jandex:2.2.3.Final',
+ jandex: 'org.jboss:jandex:2.4.2.Final',
classmate: 'com.fasterxml:classmate:1.5.1',
// Dom4J
@@ -88,9 +90,9 @@ ext {
jakarta_cdi: 'jakarta.enterprise:jakarta.enterprise.cdi-api:3.0.0',
// logging
- logging: 'org.jboss.logging:jboss-logging:3.4.2.Final',
- logging_annotations: 'org.jboss.logging:jboss-logging-annotations:2.1.0.Final',
- logging_processor: 'org.jboss.logging:jboss-logging-processor:2.1.0.Final',
+ logging: 'org.jboss.logging:jboss-logging:3.4.3.Final',
+ logging_annotations: 'org.jboss.logging:jboss-logging-annotations:2.2.1.Final',
+ logging_processor: 'org.jboss.logging:jboss-logging-processor:2.2.1.Final',
// jaxb task
jaxb_api: "javax.xml.bind:jaxb-api:${jaxbApiVersion}",
@@ -116,23 +118,30 @@ ext {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~ testing
- log4j2: "org.apache.logging.log4j:log4j-core:2.14.1",
- junit: "junit:junit:${junitVersion}",
+ junit5_api: "org.junit.jupiter:junit-jupiter-api:${junit5Version}",
+ junit5_jupiter: "org.junit.jupiter:junit-jupiter-engine:${junit5Version}",
+ junit5_params : "org.junit.jupiter:junit-jupiter-params:${junit5Version}",
+ junit: "junit:junit:${junit4Version}",
+ junit5_vintage: "org.junit.vintage:junit-vintage-engine:${junitVintageVersion}",
+
+ log4j2: "org.apache.logging.log4j:log4j-core:2.17.1",
+
byteman: "org.jboss.byteman:byteman:${bytemanVersion}",
byteman_install: "org.jboss.byteman:byteman-install:${bytemanVersion}",
byteman_bmunit: "org.jboss.byteman:byteman-bmunit:${bytemanVersion}",
h2: "com.h2database:h2:${h2Version}",
hsqldb: "org.hsqldb:hsqldb:2.3.2",
derby: "org.apache.derby:derby:10.14.2.0",
- postgresql: 'org.postgresql:postgresql:42.2.16',
+ postgresql: 'org.postgresql:postgresql:42.5.0',
mysql: 'mysql:mysql-connector-java:8.0.27',
mariadb: 'org.mariadb.jdbc:mariadb-java-client:2.2.3',
- cockroachdb: 'org.postgresql:postgresql:42.2.8',
+ cockroachdb: 'org.postgresql:postgresql:42.5.0',
oracle: 'com.oracle.database.jdbc:ojdbc8:21.3.0.0',
mssql: 'com.microsoft.sqlserver:mssql-jdbc:7.2.1.jre8',
db2: 'com.ibm.db2:jcc:11.5.4.0',
hana: 'com.sap.cloud.db.jdbc:ngdbc:2.4.59',
+ sybase: 'net.sourceforge.jtds:jtds:1.3.1',
jodaTime: "joda-time:joda-time:${jodaTimeVersion}",
@@ -162,7 +171,7 @@ ext {
vibur: "org.vibur:vibur-dbcp:25.0",
agroal_api: "io.agroal:agroal-api:${agroalVersion}",
agroal_pool: "io.agroal:agroal-pool:${agroalVersion}",
- micrometer: "io.micrometer:micrometer-core:1.6.1",
+ micrometer: "io.micrometer:micrometer-core:${micrometerVersion}",
atomikos: "com.atomikos:transactions:4.0.6",
atomikos_jta: "com.atomikos:transactions-jta:4.0.6",
diff --git a/gradle/published-java-module.gradle b/gradle/published-java-module.gradle
index df0fd448b2c5..58201f9037bc 100644
--- a/gradle/published-java-module.gradle
+++ b/gradle/published-java-module.gradle
@@ -165,6 +165,8 @@ publishing {
task ciBuild( dependsOn: [test, publish] )
-task release( dependsOn: [test, publishToSonatype] )
-publishToSonatype.mustRunAfter test
+task releasePrepare( dependsOn: [publishAllPublicationsToStagingRepository] ) {
+ group = "Release"
+ description = "Performs a release: the hibernate version is set and the changelog.txt file updated, the changes are pushed to github, then the release is performed, tagged and the hibernate version is set to the development one."
+}
diff --git a/gradle/publishing-repos.gradle b/gradle/publishing-repos.gradle
index b417e1eba3ca..bc3c34a5857a 100644
--- a/gradle/publishing-repos.gradle
+++ b/gradle/publishing-repos.gradle
@@ -6,19 +6,22 @@
*/
apply plugin: 'maven-publish'
-if ( rootProject.ormVersion.isSnapshot ) {
- apply plugin: 'org.hibernate.build.maven-repo-auth'
-}
publishing {
publications {
publishedArtifacts( MavenPublication )
}
-
repositories {
maven {
- name 'jboss-snapshots-repository'
- url 'https://repository.jboss.org/nexus/content/repositories/snapshots'
+ name = "staging"
+ url = rootProject.layout.buildDirectory.dir("staging-deploy${File.separator}maven")
+ }
+ maven {
+ name = 'snapshots'
+ url = "https://central.sonatype.com/repository/maven-snapshots/"
+ // So that Gradle uses the `ORG_GRADLE_PROJECT_snapshotsPassword` / `ORG_GRADLE_PROJECT_snapshotsUsername`
+ // env variables to read the username/password for the `snapshots` repository publishing:
+ credentials(PasswordCredentials)
}
}
}
diff --git a/gradle/version.properties b/gradle/version.properties
index 5c72c9b3ea00..0b454938b8b3 100644
--- a/gradle/version.properties
+++ b/gradle/version.properties
@@ -1 +1 @@
-hibernateVersion=5.6.2-SNAPSHOT
\ No newline at end of file
+hibernateVersion=5.6.16-SNAPSHOT
\ No newline at end of file
diff --git a/hibernate-agroal/src/test/resources/hibernate.properties b/hibernate-agroal/src/test/resources/hibernate.properties
index 6b80862911be..da8399b8675f 100644
--- a/hibernate-agroal/src/test/resources/hibernate.properties
+++ b/hibernate-agroal/src/test/resources/hibernate.properties
@@ -9,6 +9,7 @@ hibernate.connection.driver_class @jdbc.driver@
hibernate.connection.url @jdbc.url@
hibernate.connection.username @jdbc.user@
hibernate.connection.password @jdbc.pass@
+hibernate.connection.init_sql @connection.init_sql@
hibernate.jdbc.batch_size 10
hibernate.connection.provider_class AgroalConnectionProvider
diff --git a/hibernate-c3p0/src/test/resources/hibernate.properties b/hibernate-c3p0/src/test/resources/hibernate.properties
index 0d39da782e64..715af2a80a52 100644
--- a/hibernate-c3p0/src/test/resources/hibernate.properties
+++ b/hibernate-c3p0/src/test/resources/hibernate.properties
@@ -9,6 +9,7 @@ hibernate.connection.driver_class @jdbc.driver@
hibernate.connection.url @jdbc.url@
hibernate.connection.username @jdbc.user@
hibernate.connection.password @jdbc.pass@
+hibernate.connection.init_sql @connection.init_sql@
hibernate.connection.pool_size 5
hibernate.c3p0.min_size 50
diff --git a/hibernate-core-jakarta/hibernate-core-jakarta.gradle b/hibernate-core-jakarta/hibernate-core-jakarta.gradle
index 09e3e1bd5dc8..802fea183165 100644
--- a/hibernate-core-jakarta/hibernate-core-jakarta.gradle
+++ b/hibernate-core-jakarta/hibernate-core-jakarta.gradle
@@ -94,6 +94,8 @@ processResources.enabled false
compileTestJava.enabled false
processTestResources.enabled false
jar.enabled false
+javadocJar.enabled false
+sourcesJar.enabled false
ext {
transformedJarName = project(':hibernate-core').tasks.jar.archiveFileName.get().replaceAll( 'hibernate-core', 'hibernate-core-jakarta' )
@@ -123,6 +125,26 @@ task transformJar(type: JakartaJarTransformation) {
targetJar tasks.jar.archiveFile.get().asFile
}
+task transformSourcesJar(type: JakartaJarTransformation) {
+ description 'Transforms the hibernate-core sources jar using the JakartaTransformer tool'
+
+ dependsOn project(':hibernate-core').tasks.sourcesJar
+ mustRunAfter project(':hibernate-core').tasks.sourcesJar
+
+ sourceJar project(':hibernate-core').tasks.sourcesJar.archiveFile
+ targetJar tasks.sourcesJar.archiveFile.get().asFile
+}
+
+task transformJavadocJar(type: JakartaJarTransformation) {
+ description 'Transforms the hibernate-core javadoc jar using the JakartaTransformer tool'
+
+ dependsOn project(':hibernate-core').tasks.javadocJar
+ mustRunAfter project(':hibernate-core').tasks.javadocJar
+
+ sourceJar project(':hibernate-core').tasks.javadocJar.archiveFile
+ targetJar tasks.javadocJar.archiveFile.get().asFile
+}
+
configurations {
[apiElements, runtimeElements].each {
it.outgoing.artifacts.removeIf {
@@ -131,6 +153,12 @@ configurations {
it.outgoing.artifact(tasks.transformJar.targetJar) {
builtBy tasks.transformJar
}
+ it.outgoing.artifact(tasks.transformSourcesJar.targetJar) {
+ builtBy tasks.transformSourcesJar
+ }
+ it.outgoing.artifact(tasks.transformJavadocJar.targetJar) {
+ builtBy tasks.transformJavadocJar
+ }
}
}
diff --git a/hibernate-core/src/main/antlr/hql-sql.g b/hibernate-core/src/main/antlr/hql-sql.g
index 142348258022..c1ab78645d32 100644
--- a/hibernate-core/src/main/antlr/hql-sql.g
+++ b/hibernate-core/src/main/antlr/hql-sql.g
@@ -261,6 +261,15 @@ tokens
return dot;
}
+ protected AST lookupFkRefSource(AST path) throws SemanticException {
+ if ( path.getType() == DOT ) {
+ return lookupProperty( path, true, isInSelect() );
+ }
+ else {
+ return lookupNonQualifiedProperty( path );
+ }
+ }
+
protected boolean isNonQualifiedPropertyRef(AST ident) { return false; }
protected AST lookupNonQualifiedProperty(AST property) throws SemanticException { return property; }
@@ -746,12 +755,15 @@ identifier
;
addrExpr! [ boolean root ]
- : #(d:DOT lhs:addrExprLhs rhs:propertyName ) {
+ : #(d:DOT lhs:addrExprLhs rhs:propertyName ) {
// This gives lookupProperty() a chance to transform the tree
// to process collection properties (.elements, etc).
#addrExpr = #(#d, #lhs, #rhs);
#addrExpr = lookupProperty(#addrExpr,root,false);
}
+ | fk_ref:fkRef {
+ #addrExpr = #fk_ref;
+ }
| #(i:INDEX_OP lhs2:addrExprLhs rhs2:expr [ null ]) {
#addrExpr = #(#i, #lhs2, #rhs2);
processIndex(#addrExpr);
@@ -776,6 +788,12 @@ addrExpr! [ boolean root ]
}
;
+fkRef
+ : #( r:FK_REF p:propertyRef ) {
+ #p = lookupProperty( #p, false, isInSelect() );
+ }
+ ;
+
addrExprLhs
: addrExpr [ false ]
;
@@ -797,8 +815,7 @@ propertyRef!
#propertyRef = #(#d, #lhs, #rhs);
#propertyRef = lookupProperty(#propertyRef,false,true);
}
- |
- p:identifier {
+ | p:identifier {
// In many cases, things other than property-refs are recognized
// by this propertyRef rule. Some of those I have seen:
// 1) select-clause from-aliases
diff --git a/hibernate-core/src/main/antlr/hql.g b/hibernate-core/src/main/antlr/hql.g
index 3f2782263107..a8b32453c6e4 100644
--- a/hibernate-core/src/main/antlr/hql.g
+++ b/hibernate-core/src/main/antlr/hql.g
@@ -46,6 +46,7 @@ tokens
EXISTS="exists";
FALSE="false";
FETCH="fetch";
+ FK_REF;
FROM="from";
FULL="full";
GROUP="group";
@@ -722,7 +723,8 @@ atom
// level 0 - the basic element of an expression
primaryExpression
- : { validateSoftKeyword("function") && LA(2) == OPEN && LA(3) == QUOTED_STRING }? jpaFunctionSyntax
+ : { validateSoftKeyword("fk") && LA(2) == OPEN }? fkRefPath
+ | { validateSoftKeyword("function") && LA(2) == OPEN && LA(3) == QUOTED_STRING }? jpaFunctionSyntax
| { validateSoftKeyword("cast") && LA(2) == OPEN }? castFunction
| { validateSoftKeyword("size") && LA(2) == OPEN }? collectionSizeFunction
| identPrimary ( options {greedy=true;} : DOT^ "class" )?
@@ -731,6 +733,12 @@ primaryExpression
| OPEN! (expressionOrVector | subQuery) CLOSE!
;
+fkRefPath!
+ : "fk" OPEN p:identPrimary CLOSE {
+ #fkRefPath = #( [FK_REF], #p );
+ }
+ ;
+
jpaFunctionSyntax!
: i:IDENT OPEN n:QUOTED_STRING (COMMA a:exprList)? CLOSE {
final String functionName = unquote( #n.getText() );
@@ -824,6 +832,7 @@ identPrimary
#identPrimary = #( [ENTRY], path );
}
}
+ | (DOT^ FK_REF)
)?
// Also allow special 'aggregate functions' such as count(), avg(), etc.
| aggregate
diff --git a/hibernate-core/src/main/antlr/sql-gen.g b/hibernate-core/src/main/antlr/sql-gen.g
index b59667c79658..a5cfb13ee592 100644
--- a/hibernate-core/src/main/antlr/sql-gen.g
+++ b/hibernate-core/src/main/antlr/sql-gen.g
@@ -509,6 +509,7 @@ parameter
addrExpr
: #(r:DOT . .) { out(r); }
+ | #(fk:FK_REF .) { out(fk); }
| i:ALIAS_REF { out(i); }
| j:INDEX_OP { out(j); }
| v:RESULT_VARIABLE_REF { out(v); }
diff --git a/hibernate-core/src/main/java/org/hibernate/FetchNotFoundException.java b/hibernate-core/src/main/java/org/hibernate/FetchNotFoundException.java
new file mode 100644
index 000000000000..42f729c1f2ce
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/FetchNotFoundException.java
@@ -0,0 +1,43 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
+ * See the lgpl.txt file in the root directory or .
+ */
+package org.hibernate;
+
+import java.util.Locale;
+import javax.persistence.EntityNotFoundException;
+
+/**
+ * Exception for {@link org.hibernate.annotations.NotFoundAction#EXCEPTION}
+ *
+ * @see org.hibernate.annotations.NotFound
+ *
+ * @author Steve Ebersole
+ */
+public class FetchNotFoundException extends EntityNotFoundException {
+ private final String entityName;
+ private final Object identifier;
+
+ public FetchNotFoundException(String entityName, Object identifier) {
+ super(
+ String.format(
+ Locale.ROOT,
+ "Entity `%s` with identifier value `%s` does not exist",
+ entityName,
+ identifier
+ )
+ );
+ this.entityName = entityName;
+ this.identifier = identifier;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public Object getIdentifier() {
+ return identifier;
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/Hibernate.java b/hibernate-core/src/main/java/org/hibernate/Hibernate.java
index 9f4fea0184d5..22cd1e646f89 100644
--- a/hibernate-core/src/main/java/org/hibernate/Hibernate.java
+++ b/hibernate-core/src/main/java/org/hibernate/Hibernate.java
@@ -10,16 +10,18 @@
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
-import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.HibernateIterator;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.spi.JdbcServices;
-import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
+import org.hibernate.collection.spi.LazyInitializable;
+
+import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
+import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable;
/**
*
@@ -61,12 +63,11 @@ public static void initialize(Object proxy) throws HibernateException {
if ( proxy instanceof HibernateProxy ) {
( (HibernateProxy) proxy ).getHibernateLazyInitializer().initialize();
}
- else if ( proxy instanceof PersistentCollection ) {
- ( (PersistentCollection) proxy ).forceInitialization();
+ else if ( proxy instanceof LazyInitializable ) {
+ ( (LazyInitializable) proxy ).forceInitialization();
}
- else if ( proxy instanceof PersistentAttributeInterceptable ) {
- final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) proxy;
- final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
+ else if ( isPersistentAttributeInterceptable( proxy ) ) {
+ final PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable( proxy ).$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
( (EnhancementAsProxyLazinessInterceptor) interceptor ).forceInitialize( proxy, null );
}
@@ -84,15 +85,15 @@ public static boolean isInitialized(Object proxy) {
if ( proxy instanceof HibernateProxy ) {
return !( (HibernateProxy) proxy ).getHibernateLazyInitializer().isUninitialized();
}
- else if ( proxy instanceof PersistentAttributeInterceptable ) {
- final PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) proxy ).$$_hibernate_getInterceptor();
+ else if ( isPersistentAttributeInterceptable( proxy ) ) {
+ final PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable( proxy ).$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
return false;
}
return true;
}
- else if ( proxy instanceof PersistentCollection ) {
- return ( (PersistentCollection) proxy ).wasInitialized();
+ else if ( proxy instanceof LazyInitializable ) {
+ return ( (LazyInitializable) proxy ).wasInitialized();
}
else {
return true;
@@ -200,8 +201,9 @@ public static boolean isPropertyInitialized(Object proxy, String propertyName) {
entity = proxy;
}
- if ( entity instanceof PersistentAttributeInterceptable ) {
- PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) entity ).$$_hibernate_getInterceptor();
+
+ if ( isPersistentAttributeInterceptable( entity ) ) {
+ PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor();
if ( interceptor instanceof BytecodeLazyAttributeInterceptor ) {
return ( (BytecodeLazyAttributeInterceptor) interceptor ).isAttributeLoaded( propertyName );
}
diff --git a/hibernate-core/src/main/java/org/hibernate/Session.java b/hibernate-core/src/main/java/org/hibernate/Session.java
index c673743e22b2..9bb66e1c8cac 100644
--- a/hibernate-core/src/main/java/org/hibernate/Session.java
+++ b/hibernate-core/src/main/java/org/hibernate/Session.java
@@ -793,7 +793,22 @@ public interface Session extends SharedSessionContract, EntityManager, Hibernate
* @return the entity name
*/
String getEntityName(Object object);
-
+
+ /**
+ * Return a reference to the persistent instance with the same identity as the given
+ * instance, which might be detached, making the assumption that the instance is still
+ * persistent in the database. This method never results in access to the underlying
+ * data store, and thus might return a proxy that is initialized on-demand, when a
+ * non-identifier method is accessed.
+ *
+ * @param object a detached persistent instance
+ *
+ * @return the persistent instance or proxy
+ */
+ default T getReference(T object) {
+ throw new IllegalStateException( "getReference(Object) is not implemented in " + getClass() );
+ }
+
/**
* Create an {@link IdentifierLoadAccess} instance to retrieve the specified entity type by
* primary key.
@@ -1156,6 +1171,16 @@ interface LockRequest {
org.hibernate.query.Query createNamedQuery(String name, Class resultType);
+ /**
+ * Create a {@link NativeQuery} instance for the given SQL query string.
+ *
+ * @param queryString The SQL query
+ *
+ * @return The query instance for manipulation and execution
+ *
+ * @deprecated (since 5.2) use {@link #createNativeQuery(String)} instead
+ */
+ @Deprecated
@Override
NativeQuery createSQLQuery(String queryString);
}
diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java
index 41bc9f9e653b..7dbed1ba0d37 100644
--- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java
+++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java
@@ -9,6 +9,7 @@
import java.io.Serializable;
import org.hibernate.AssertionFailure;
+import org.hibernate.CacheMode;
import org.hibernate.HibernateException;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.access.EntityDataAccess;
@@ -32,6 +33,7 @@
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.stat.internal.StatsHelper;
import org.hibernate.stat.spi.StatisticsImplementor;
+import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.type.TypeHelper;
/**
@@ -239,9 +241,21 @@ public void execute() throws HibernateException {
entry.postUpdate( instance, state, nextVersion );
}
+ if ( entry.getStatus() == Status.DELETED ) {
+ final EntityMetamodel entityMetamodel = persister.getEntityMetamodel();
+ final boolean isImpliedOptimisticLocking = !entityMetamodel.isVersioned()
+ && entityMetamodel.getOptimisticLockStyle().isAllOrDirty();
+ if ( isImpliedOptimisticLocking && entry.getLoadedState() != null ) {
+ // The entity will be deleted and because we are going to create a delete statement that uses
+ // all the state values in the where clause, the entry state needs to be updated otherwise the statement execution will
+ // not delete any row (see HHH-15218).
+ entry.postUpdate( instance, state, nextVersion );
+ }
+ }
+
final StatisticsImplementor statistics = factory.getStatistics();
if ( persister.canWriteToCache() ) {
- if ( persister.isCacheInvalidationRequired() || entry.getStatus() != Status.MANAGED ) {
+ if ( isCacheInvalidationRequired( persister, session ) || entry.getStatus() != Status.MANAGED ) {
persister.getCacheAccessStrategy().remove( session, ck );
}
else if ( session.getCacheMode().isPutEnabled() ) {
@@ -275,6 +289,13 @@ else if ( session.getCacheMode().isPutEnabled() ) {
}
+ private static boolean isCacheInvalidationRequired(
+ EntityPersister persister,
+ SharedSessionContractImplementor session) {
+ // the cache has to be invalidated when CacheMode is equal to GET or IGNORE
+ return persister.isCacheInvalidationRequired() || session.getCacheMode() == CacheMode.GET || session.getCacheMode() == CacheMode.IGNORE;
+ }
+
protected boolean cacheUpdate(EntityPersister persister, Object previousVersion, Object ck) {
final SharedSessionContractImplementor session = getSession();
try {
diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/FetchMode.java b/hibernate-core/src/main/java/org/hibernate/annotations/FetchMode.java
index fa1e03a7645a..b9ca4abc83c2 100644
--- a/hibernate-core/src/main/java/org/hibernate/annotations/FetchMode.java
+++ b/hibernate-core/src/main/java/org/hibernate/annotations/FetchMode.java
@@ -7,8 +7,8 @@
package org.hibernate.annotations;
/**
- * Fetch options on associations. Defines more of the "how" of fetching, whereas JPA {@link javax.persistence.FetchType}
- * focuses on the "when".
+ * Defines how the association should be fetched, compared to
+ * {@link javax.persistence.FetchType} which defines when it should be fetched
*
* @author Emmanuel Bernard
*/
@@ -16,13 +16,27 @@ public enum FetchMode {
/**
* Use a secondary select for each individual entity, collection, or join load.
*/
- SELECT,
+ SELECT( org.hibernate.FetchMode.SELECT ),
/**
* Use an outer join to load the related entities, collections or joins.
*/
- JOIN,
+ JOIN( org.hibernate.FetchMode.JOIN ),
/**
- * Available for collections only.  When accessing a non-initialized collection, this fetch mode will trigger loading all elements of all collections of the same role for all owners associated with the persistence context using a single secondary select.
+ * Available for collections only.
+ *
+ * When accessing a non-initialized collection, this fetch mode will trigger
+ * loading all elements of all collections of the same role for all owners
+ * associated with the persistence context using a single secondary select.
*/
- SUBSELECT
+ SUBSELECT( org.hibernate.FetchMode.SELECT );
+
+ private final org.hibernate.FetchMode hibernateFetchMode;
+
+ FetchMode(org.hibernate.FetchMode hibernateFetchMode) {
+ this.hibernateFetchMode = hibernateFetchMode;
+ }
+
+ public org.hibernate.FetchMode getHibernateFetchMode() {
+ return hibernateFetchMode;
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/NotFoundAction.java b/hibernate-core/src/main/java/org/hibernate/annotations/NotFoundAction.java
index c6e4e9622d29..906d65024096 100644
--- a/hibernate-core/src/main/java/org/hibernate/annotations/NotFoundAction.java
+++ b/hibernate-core/src/main/java/org/hibernate/annotations/NotFoundAction.java
@@ -6,20 +6,36 @@
*/
package org.hibernate.annotations;
+import org.hibernate.FetchNotFoundException;
/**
- * Possible actions when an associated entity is not found in the database. Often seen with "legacy" foreign-key
- * schemes which do not use {@code NULL} to indicate a missing reference, instead using a "magic value".
+ * Possible actions when the database contains a non-null fk with no
+ * matching target. This also implies that there are no physical
+ * foreign-key constraints on the database.
*
+ * As an example, consider a typical Customer/Order model. These actions apply
+ * when a non-null `orders.customer_fk` value does not have a corresponding value
+ * in `customers.id`.
+ *
+ * Generally this will occur in 2 scenarios:
+ *
the associated data has been deleted
+ *
the model uses special "magic" values to indicate null
+ *
+ *
+ * @author Steve Ebersole
* @author Emmanuel Bernard
*/
public enum NotFoundAction {
/**
- * Raise an exception when an element is not found (default and recommended).
+ * Throw an exception when the association is not found (default and recommended).
+ *
+ * @see FetchNotFoundException
*/
EXCEPTION,
+
/**
- * Ignore the element when not found in database.
+ * Ignore the association when not found in database. Effectively treats the
+ * association as null, despite the non-null foreign-key value.
*/
IGNORE
}
diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/Type.java b/hibernate-core/src/main/java/org/hibernate/annotations/Type.java
index 3290021b77c8..8b8e40ea172b 100644
--- a/hibernate-core/src/main/java/org/hibernate/annotations/Type.java
+++ b/hibernate-core/src/main/java/org/hibernate/annotations/Type.java
@@ -24,12 +24,9 @@
*
* @author Emmanuel Bernard
* @author Steve Ebersole
- *
- * @deprecated 6.0 will introduce a new type-safe {@code CustomType} annotation
*/
@Target({FIELD, METHOD})
@Retention(RUNTIME)
-@Deprecated
public @interface Type {
/**
* The Hibernate type name. Usually the fully qualified name of an implementation class for
diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/TypeDef.java b/hibernate-core/src/main/java/org/hibernate/annotations/TypeDef.java
index 51f7c8abe7d4..4810824de0e6 100644
--- a/hibernate-core/src/main/java/org/hibernate/annotations/TypeDef.java
+++ b/hibernate-core/src/main/java/org/hibernate/annotations/TypeDef.java
@@ -28,13 +28,10 @@
*
* @author Emmanuel Bernard
* @author Steve Ebersole
- *
- * @deprecated 6.0 will introduce a new series of type-safe annotations to serve the same purpose
*/
@Target({TYPE, PACKAGE})
@Retention(RUNTIME)
@Repeatable(TypeDefs.class)
-@Deprecated
public @interface TypeDef {
/**
* The type name. This is the name that would be used in other locations.
diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/TypeDefs.java b/hibernate-core/src/main/java/org/hibernate/annotations/TypeDefs.java
index cdc175b0e282..6f05951eafc4 100644
--- a/hibernate-core/src/main/java/org/hibernate/annotations/TypeDefs.java
+++ b/hibernate-core/src/main/java/org/hibernate/annotations/TypeDefs.java
@@ -18,12 +18,9 @@
*
* @author Emmanuel Bernard
* @author Steve Ebersole
- *
- * @deprecated 6.0 will introduce a new series of type-safe annotations to serve the same purpose
*/
@Target({TYPE, PACKAGE})
@Retention(RUNTIME)
-@Deprecated
public @interface TypeDefs {
/**
* The grouping of type definitions.
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/archive/scan/spi/ClassFileArchiveEntryHandler.java b/hibernate-core/src/main/java/org/hibernate/boot/archive/scan/spi/ClassFileArchiveEntryHandler.java
index 8679accff6c2..a4866f1a3ce0 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/archive/scan/spi/ClassFileArchiveEntryHandler.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/archive/scan/spi/ClassFileArchiveEntryHandler.java
@@ -61,8 +61,9 @@ public void handleEntry(ArchiveEntry entry, ArchiveContext context) {
private ClassDescriptor toClassDescriptor(ArchiveEntry entry) {
try (InputStream inputStream = entry.getStreamAccess().accessInputStream()) {
Indexer indexer = new Indexer();
- ClassInfo classInfo = indexer.index( inputStream );
+ indexer.index( inputStream );
Index index = indexer.complete();
+ ClassInfo classInfo = index.getKnownClasses().iterator().next();
return toClassDescriptor( classInfo, index, entry );
}
catch (IOException e) {
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java
index 568ef9649817..4f0c817e8c15 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java
@@ -45,7 +45,6 @@
import org.hibernate.boot.model.naming.ImplicitUniqueKeyNameSource;
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
import org.hibernate.boot.model.relational.Database;
-import org.hibernate.boot.model.relational.ExportableProducer;
import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.boot.model.relational.QualifiedTableName;
import org.hibernate.boot.model.source.internal.ImplicitColumnNamingSecondPass;
@@ -2198,8 +2197,6 @@ private void processExportableProducers() {
// for now we only handle id generators as ExportableProducers
final Dialect dialect = getDatabase().getJdbcEnvironment().getDialect();
- final String defaultCatalog = extractName( getDatabase().getDefaultNamespace().getName().getCatalog(), dialect );
- final String defaultSchema = extractName( getDatabase().getDefaultNamespace().getName().getSchema(), dialect );
for ( PersistentClass entityBinding : entityBindingMap.values() ) {
if ( entityBinding.isInherited() ) {
@@ -2209,8 +2206,6 @@ private void processExportableProducers() {
handleIdentifierValueBinding(
entityBinding.getIdentifier(),
dialect,
- defaultCatalog,
- defaultSchema,
(RootClass) entityBinding
);
}
@@ -2223,8 +2218,6 @@ private void processExportableProducers() {
handleIdentifierValueBinding(
( (IdentifierCollection) collection ).getIdentifier(),
dialect,
- defaultCatalog,
- defaultSchema,
null
);
}
@@ -2233,8 +2226,6 @@ private void processExportableProducers() {
private void handleIdentifierValueBinding(
KeyValue identifierValueBinding,
Dialect dialect,
- String defaultCatalog,
- String defaultSchema,
RootClass entityBinding) {
// todo : store this result (back into the entity or into the KeyValue, maybe?)
// This process of instantiating the id-generator is called multiple times.
@@ -2244,14 +2235,10 @@ private void handleIdentifierValueBinding(
final IdentifierGenerator ig = identifierValueBinding.createIdentifierGenerator(
getIdentifierGeneratorFactory(),
dialect,
- defaultCatalog,
- defaultSchema,
entityBinding
);
- if ( ig instanceof ExportableProducer ) {
- ( (ExportableProducer) ig ).registerExportables( getDatabase() );
- }
+ ig.registerExportables( getDatabase() );
}
catch (MappingException e) {
// ignore this for now. The reasoning being "non-reflective" binding as needed
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java
index 61f98f680e9a..6f44c9c7d2e7 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java
@@ -499,17 +499,12 @@ public static class MappingDefaultsImpl implements MappingDefaults {
public MappingDefaultsImpl(StandardServiceRegistry serviceRegistry) {
final ConfigurationService configService = serviceRegistry.getService( ConfigurationService.class );
- this.implicitSchemaName = configService.getSetting(
- AvailableSettings.DEFAULT_SCHEMA,
- StandardConverters.STRING,
- null
- );
-
- this.implicitCatalogName = configService.getSetting(
- AvailableSettings.DEFAULT_CATALOG,
- StandardConverters.STRING,
- null
- );
+ // AvailableSettings.DEFAULT_SCHEMA and AvailableSettings.DEFAULT_CATALOG
+ // are taken into account later, at runtime, when rendering table/sequence names.
+ // These fields are exclusively about mapping defaults,
+ // overridden in XML mappings or through setters in MetadataBuilder.
+ this.implicitSchemaName = null;
+ this.implicitCatalogName = null;
this.implicitlyQuoteIdentifiers = configService.getSetting(
AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS,
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
index 704437008fc9..dfff98061cdd 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
@@ -82,7 +82,9 @@
import static org.hibernate.cfg.AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE;
import static org.hibernate.cfg.AvailableSettings.CUSTOM_ENTITY_DIRTINESS_STRATEGY;
import static org.hibernate.cfg.AvailableSettings.DEFAULT_BATCH_FETCH_SIZE;
+import static org.hibernate.cfg.AvailableSettings.DEFAULT_CATALOG;
import static org.hibernate.cfg.AvailableSettings.DEFAULT_ENTITY_MODE;
+import static org.hibernate.cfg.AvailableSettings.DEFAULT_SCHEMA;
import static org.hibernate.cfg.AvailableSettings.DELAY_ENTITY_LOADER_CREATIONS;
import static org.hibernate.cfg.AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS;
import static org.hibernate.cfg.AvailableSettings.FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH;
@@ -242,6 +244,12 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
private boolean queryParametersValidationEnabled;
private LiteralHandlingMode criteriaLiteralHandlingMode;
private ImmutableEntityUpdateQueryHandlingMode immutableEntityUpdateQueryHandlingMode;
+ // These two settings cannot be modified from the builder,
+ // in order to maintain consistency.
+ // Indeed, other components (the schema tools) also make use of these settings,
+ // and THOSE do not have access to session factory options.
+ private final String defaultCatalog;
+ private final String defaultSchema;
private Map sqlFunctions;
@@ -531,6 +539,9 @@ else if ( jdbcTimeZoneValue != null ) {
configurationSettings.get( IMMUTABLE_ENTITY_UPDATE_QUERY_HANDLING_MODE )
);
+ this.defaultCatalog = ConfigurationHelper.getString( DEFAULT_CATALOG, configurationSettings );
+ this.defaultSchema = ConfigurationHelper.getString( DEFAULT_SCHEMA, configurationSettings );
+
this.inClauseParameterPaddingEnabled = ConfigurationHelper.getBoolean(
IN_CLAUSE_PARAMETER_PADDING,
configurationSettings,
@@ -1047,6 +1058,16 @@ public ImmutableEntityUpdateQueryHandlingMode getImmutableEntityUpdateQueryHandl
return immutableEntityUpdateQueryHandlingMode;
}
+ @Override
+ public String getDefaultCatalog() {
+ return defaultCatalog;
+ }
+
+ @Override
+ public String getDefaultSchema() {
+ return defaultSchema;
+ }
+
@Override
public boolean jdbcStyleParamsZeroBased() {
return this.jdbcStyleParamsZeroBased;
@@ -1092,7 +1113,6 @@ public boolean isOmitJoinOfSuperclassTablesEnabled() {
return omitJoinOfSuperclassTablesEnabled;
}
-
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// In-flight mutation access
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/stax/LocalXmlResourceResolver.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/stax/LocalXmlResourceResolver.java
index 89a749a2d154..a8b2f122a0ef 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/stax/LocalXmlResourceResolver.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/stax/LocalXmlResourceResolver.java
@@ -67,19 +67,15 @@ else if ( CFG_XSD_MAPPING.matches( namespace ) ) {
);
return openUrlStream( HBM_DTD_MAPPING.getMappedLocalUrl() );
}
- else if ( LEGACY_HBM_DTD_MAPPING.matches( publicID, systemID ) ) {
- DeprecationLogger.DEPRECATION_LOGGER.recognizedObsoleteHibernateNamespace(
- LEGACY_HBM_DTD_MAPPING.getIdentifierBase(),
- HBM_DTD_MAPPING.getIdentifierBase()
- );
+ else if ( ALTERNATE_MAPPING_DTD.matches( publicID, systemID ) ) {
log.debug(
- "Recognized legacy hibernate-mapping identifier; attempting to resolve on classpath under org/hibernate/"
+ "Recognized alternate hibernate-mapping identifier; attempting to resolve on classpath under org/hibernate/"
);
- return openUrlStream( HBM_DTD_MAPPING.getMappedLocalUrl() );
+ return openUrlStream( ALTERNATE_MAPPING_DTD.getMappedLocalUrl() );
}
- else if ( LEGACY2_HBM_DTD_MAPPING.matches( publicID, systemID ) ) {
+ else if ( LEGACY_HBM_DTD_MAPPING.matches( publicID, systemID ) ) {
DeprecationLogger.DEPRECATION_LOGGER.recognizedObsoleteHibernateNamespace(
- LEGACY2_HBM_DTD_MAPPING.getIdentifierBase(),
+ LEGACY_HBM_DTD_MAPPING.getIdentifierBase(),
HBM_DTD_MAPPING.getIdentifierBase()
);
log.debug(
@@ -93,6 +89,12 @@ else if ( CFG_DTD_MAPPING.matches( publicID, systemID ) ) {
);
return openUrlStream( CFG_DTD_MAPPING.getMappedLocalUrl() );
}
+ else if ( ALTERNATE_CFG_DTD.matches( publicID, systemID ) ) {
+ log.debug(
+ "Recognized alternate hibernate-configuration identifier; attempting to resolve on classpath under org/hibernate/"
+ );
+ return openUrlStream( ALTERNATE_CFG_DTD.getMappedLocalUrl() );
+ }
else if ( LEGACY_CFG_DTD_MAPPING.matches( publicID, systemID ) ) {
DeprecationLogger.DEPRECATION_LOGGER.recognizedObsoleteHibernateNamespace(
LEGACY_CFG_DTD_MAPPING.getIdentifierBase(),
@@ -158,7 +160,7 @@ private InputStream resolveInLocalNamespace(String path) {
"http://xmlns.jcp.org/xml/ns/persistence/orm",
"org/hibernate/jpa/orm_2_1.xsd"
);
-
+
/**
* Maps the namespace for the orm.xml xsd for Jakarta Persistence 2.2
*/
@@ -174,7 +176,7 @@ private InputStream resolveInLocalNamespace(String path) {
"https://jakarta.ee/xml/ns/persistence/orm",
"org/hibernate/jpa/orm_3_0.xsd"
);
-
+
public static final NamespaceSchemaMapping HBM_XSD_MAPPING = new NamespaceSchemaMapping(
"http://www.hibernate.org/xsd/orm/hbm",
"org/hibernate/xsd/mapping/legacy-mapping-4.0.xsd"
@@ -191,27 +193,32 @@ private InputStream resolveInLocalNamespace(String path) {
);
public static final DtdMapping HBM_DTD_MAPPING = new DtdMapping(
- "http://www.hibernate.org/dtd/hibernate-mapping",
+ "www.hibernate.org/dtd/hibernate-mapping",
"org/hibernate/hibernate-mapping-3.0.dtd"
);
- public static final DtdMapping LEGACY_HBM_DTD_MAPPING = new DtdMapping(
- "http://www.hibernate.org/dtd/hibernate-mapping",
+ public static final DtdMapping ALTERNATE_MAPPING_DTD = new DtdMapping(
+ "hibernate.org/dtd/hibernate-mapping",
"org/hibernate/hibernate-mapping-3.0.dtd"
);
- public static final DtdMapping LEGACY2_HBM_DTD_MAPPING = new DtdMapping(
- "http://hibernate.sourceforge.net/hibernate-mapping",
+ public static final DtdMapping LEGACY_HBM_DTD_MAPPING = new DtdMapping(
+ "hibernate.sourceforge.net/hibernate-mapping",
"org/hibernate/hibernate-mapping-3.0.dtd"
);
public static final DtdMapping CFG_DTD_MAPPING = new DtdMapping(
- "http://www.hibernate.org/dtd/hibernate-configuration",
+ "www.hibernate.org/dtd/hibernate-configuration",
+ "org/hibernate/hibernate-configuration-3.0.dtd"
+ );
+
+ public static final DtdMapping ALTERNATE_CFG_DTD = new DtdMapping(
+ "hibernate.org/dtd/hibernate-configuration",
"org/hibernate/hibernate-configuration-3.0.dtd"
);
public static final DtdMapping LEGACY_CFG_DTD_MAPPING = new DtdMapping(
- "http://hibernate.sourceforge.net/hibernate-configuration",
+ "hibernate.sourceforge.net/hibernate-configuration",
"org/hibernate/hibernate-configuration-3.0.dtd"
);
@@ -235,27 +242,31 @@ public URL getMappedLocalUrl() {
}
public static class DtdMapping {
- private final String identifierBase;
+ private final String httpBase;
+ private final String httpsBase;
private final URL localSchemaUrl;
public DtdMapping(String identifierBase, String resourceName) {
- this.identifierBase = identifierBase;
+ this.httpBase = "http://" + identifierBase;
+ this.httpsBase = "https://" + identifierBase;
this.localSchemaUrl = LocalSchemaLocator.resolveLocalSchemaUrl( resourceName );
}
public String getIdentifierBase() {
- return identifierBase;
+ return httpBase;
}
public boolean matches(String publicId, String systemId) {
if ( publicId != null ) {
- if ( publicId.startsWith( identifierBase ) ) {
+ if ( publicId.startsWith( httpBase )
+ || publicId.startsWith( httpsBase ) ) {
return true;
}
}
if ( systemId != null ) {
- if ( systemId.startsWith( identifierBase ) ) {
+ if ( systemId.startsWith( httpBase )
+ || systemId.startsWith( httpsBase ) ) {
return true;
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/naming/Identifier.java b/hibernate-core/src/main/java/org/hibernate/boot/model/naming/Identifier.java
index be697f9be446..667c189bf577 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/model/naming/Identifier.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/model/naming/Identifier.java
@@ -75,6 +75,64 @@ public static Identifier toIdentifier(String text, boolean quote) {
}
}
+ /**
+ * Means to generate an {@link Identifier} instance from its simple text form.
+ *
+ * If passed text is {@code null}, {@code null} is returned.
+ *
+ * If passed text is surrounded in quote markers, the generated Identifier
+ * is considered quoted. Quote markers include back-ticks (`),
+ * double-quotes (") and brackets ([ and ]).
+ *
+ * @param text The text form
+ * @param quote Whether to quote unquoted text forms
+ * @param quoteOnNonIdentifierChar Controls whether to treat the result as quoted if text contains characters that are invalid for identifiers
+ *
+ * @return The identifier form, or {@code null} if text was {@code null}
+ */
+ public static Identifier toIdentifier(String text, boolean quote, boolean quoteOnNonIdentifierChar) {
+ if ( StringHelper.isEmpty( text ) ) {
+ return null;
+ }
+ int start = 0;
+ int end = text.length();
+ while ( start < end ) {
+ if ( !Character.isWhitespace( text.charAt( start ) ) ) {
+ break;
+ }
+ start++;
+ }
+ while ( start < end ) {
+ if ( !Character.isWhitespace( text.charAt( end - 1 ) ) ) {
+ break;
+ }
+ end--;
+ }
+ if ( isQuoted( text, start, end ) ) {
+ start++;
+ end--;
+ quote = true;
+ }
+ else if ( quoteOnNonIdentifierChar && !quote ) {
+ // Check the letters to determine if we must quote the text
+ char c = text.charAt( start );
+ if ( !Character.isLetter( c ) && c != '_' ) {
+ // SQL identifiers must begin with a letter or underscore
+ quote = true;
+ }
+ else {
+ for ( int i = start + 1; i < end; i++ ) {
+ c = text.charAt( i );
+ if ( !Character.isLetterOrDigit( c ) && c != '_' ) {
+ quote = true;
+ break;
+ }
+ }
+ }
+ }
+ return new Identifier( text.substring( start, end ), quote );
+ }
+
/**
* Is the given identifier text considered quoted. The following patterns are
* recognized as quoted:
@@ -96,6 +154,20 @@ public static boolean isQuoted(String name) {
|| ( name.startsWith( "\"" ) && name.endsWith( "\"" ) );
}
+ public static boolean isQuoted(String name, int start, int end) {
+ if ( start + 2 < end ) {
+ switch ( name.charAt( start ) ) {
+ case '`':
+ return name.charAt( end - 1 ) == '`';
+ case '[':
+ return name.charAt( end - 1 ) == ']';
+ case '"':
+ return name.charAt( end - 1 ) == '"';
+ }
+ }
+ return false;
+ }
+
/**
* Constructs an identifier instance.
*
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/AbstractAuxiliaryDatabaseObject.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/AbstractAuxiliaryDatabaseObject.java
index 95230353ed09..cf3eb2bb32b9 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/AbstractAuxiliaryDatabaseObject.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/AbstractAuxiliaryDatabaseObject.java
@@ -13,7 +13,7 @@
import org.hibernate.dialect.Dialect;
/**
- * Convenience base class for {@link org.hibernate.mapping.AuxiliaryDatabaseObject}s.
+ * Convenience base class for {@link AuxiliaryDatabaseObject}s.
*
* This implementation performs dialect scoping checks strictly based on
* dialect name comparisons. Custom implementations might want to do
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/AuxiliaryDatabaseObject.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/AuxiliaryDatabaseObject.java
index 7434c0772afd..fe91498b11c0 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/AuxiliaryDatabaseObject.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/AuxiliaryDatabaseObject.java
@@ -39,14 +39,42 @@ public interface AuxiliaryDatabaseObject extends Exportable, Serializable {
*/
public boolean beforeTablesOnCreation();
+ /**
+ * Gets the SQL strings for creating the database object.
+ *
+ * @param context A context to help generate the SQL creation strings
+ *
+ * @return the SQL strings for creating the database object.
+ */
+ default String[] sqlCreateStrings(SqlStringGenerationContext context) {
+ return sqlCreateStrings( context.getDialect() );
+ }
+
/**
* Gets the SQL strings for creating the database object.
*
* @param dialect The dialect for which to generate the SQL creation strings
*
* @return the SQL strings for creating the database object.
+ * @deprecated Hibernate ORM may never call this method,
+ * and implementations cannot properly handle default catalogs/schemas.
+ * Call/implement {@link #sqlCreateStrings(SqlStringGenerationContext)} instead.
+ */
+ @Deprecated
+ default String[] sqlCreateStrings(Dialect dialect) {
+ throw new IllegalStateException( this + " does not implement sqlCreateStrings(...)" );
+ }
+
+ /**
+ * Gets the SQL strings for dropping the database object.
+ *
+ * @param context A context to help generate the SQL drop strings
+ *
+ * @return the SQL strings for dropping the database object.
*/
- public String[] sqlCreateStrings(Dialect dialect);
+ default String[] sqlDropStrings(SqlStringGenerationContext context) {
+ return sqlDropStrings( context.getDialect() );
+ }
/**
* Gets the SQL strings for dropping the database object.
@@ -54,8 +82,14 @@ public interface AuxiliaryDatabaseObject extends Exportable, Serializable {
* @param dialect The dialect for which to generate the SQL drop strings
*
* @return the SQL strings for dropping the database object.
+ * @deprecated Hibernate ORM may never call this method,
+ * and implementations cannot properly handle default catalogs/schemas.
+ * Call/implement {@link #sqlDropStrings(SqlStringGenerationContext)} instead.
*/
- public String[] sqlDropStrings(Dialect dialect);
+ @Deprecated
+ default String[] sqlDropStrings(Dialect dialect) {
+ throw new IllegalStateException( this + " does not implement sqlDropStrings(...)" );
+ }
/**
* Additional, optional interface for AuxiliaryDatabaseObject that want to allow
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Database.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Database.java
index ef77cb757248..5cf503dad07e 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Database.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Database.java
@@ -35,7 +35,7 @@ public class Database {
private final ServiceRegistry serviceRegistry;
private final PhysicalNamingStrategy physicalNamingStrategy;
- private Namespace implicitNamespace;
+ private Namespace.Name physicalImplicitNamespaceName;
private List initCommands;
public Database(MetadataBuildingOptions buildingOptions) {
@@ -48,11 +48,16 @@ public Database(MetadataBuildingOptions buildingOptions, JdbcEnvironment jdbcEnv
this.physicalNamingStrategy = buildingOptions.getPhysicalNamingStrategy();
this.dialect = determineDialect( buildingOptions );
- this.implicitNamespace = makeNamespace(
- new Namespace.Name(
- toIdentifier( buildingOptions.getMappingDefaults().getImplicitCatalogName() ),
- toIdentifier( buildingOptions.getMappingDefaults().getImplicitSchemaName() )
- )
+ setImplicitNamespaceName(
+ toIdentifier( buildingOptions.getMappingDefaults().getImplicitCatalogName() ),
+ toIdentifier( buildingOptions.getMappingDefaults().getImplicitSchemaName() )
+ );
+ }
+
+ private void setImplicitNamespaceName(Identifier catalogName, Identifier schemaName) {
+ this.physicalImplicitNamespaceName = new Namespace.Name(
+ physicalNamingStrategy.toPhysicalCatalogName( catalogName, jdbcEnvironment ),
+ physicalNamingStrategy.toPhysicalSchemaName( schemaName, jdbcEnvironment )
);
}
@@ -108,15 +113,25 @@ public Iterable getNamespaces() {
return namespaceMap.values();
}
+ /**
+ * @return The default namespace, with a {@code null} catalog and schema
+ * which will have to be interpreted with defaults at runtime.
+ * @see SqlStringGenerationContext
+ */
public Namespace getDefaultNamespace() {
- return implicitNamespace;
+ return locateNamespace( null, null );
}
- public Namespace locateNamespace(Identifier catalogName, Identifier schemaName) {
- if ( catalogName == null && schemaName == null ) {
- return getDefaultNamespace();
- }
+ /**
+ * @return The implicit name of the default namespace, with a {@code null} catalog and schema
+ * which will have to be interpreted with defaults at runtime.
+ * @see SqlStringGenerationContext
+ */
+ public Namespace.Name getPhysicalImplicitNamespaceName() {
+ return physicalImplicitNamespaceName;
+ }
+ public Namespace locateNamespace(Identifier catalogName, Identifier schemaName) {
final Namespace.Name name = new Namespace.Name( catalogName, schemaName );
Namespace namespace = namespaceMap.get( name );
if ( namespace == null ) {
@@ -126,17 +141,8 @@ public Namespace locateNamespace(Identifier catalogName, Identifier schemaName)
}
public Namespace adjustDefaultNamespace(Identifier catalogName, Identifier schemaName) {
- final Namespace.Name name = new Namespace.Name( catalogName, schemaName );
- if ( implicitNamespace.getName().equals( name ) ) {
- return implicitNamespace;
- }
-
- Namespace namespace = namespaceMap.get( name );
- if ( namespace == null ) {
- namespace = makeNamespace( name );
- }
- implicitNamespace = namespace;
- return implicitNamespace;
+ setImplicitNamespaceName( catalogName, schemaName );
+ return locateNamespace( catalogName, schemaName );
}
public Namespace adjustDefaultNamespace(String implicitCatalogName, String implicitSchemaName) {
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedName.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedName.java
index 92139e89c224..b7af552a2b2d 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedName.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedName.java
@@ -31,8 +31,8 @@ public interface QualifiedName {
* Returns a String-form of the qualified name.
*
* Depending on intention, may not be appropriate. May want
- * {@link org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter#format}
- * instead. See {@link org.hibernate.engine.jdbc.env.spi.JdbcEnvironment#getQualifiedObjectNameFormatter}
+ * {@link SqlStringGenerationContext#format}
+ * instead.
*
* @return The string form
*/
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/SimpleAuxiliaryDatabaseObject.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/SimpleAuxiliaryDatabaseObject.java
index df59ef7466f7..618931f1d04b 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/SimpleAuxiliaryDatabaseObject.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/SimpleAuxiliaryDatabaseObject.java
@@ -9,6 +9,7 @@
import java.util.Set;
import org.hibernate.boot.model.naming.Identifier;
+import org.hibernate.boot.model.relational.internal.SqlStringGenerationContextImpl;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.util.StringHelper;
@@ -76,19 +77,37 @@ public SimpleAuxiliaryDatabaseObject(
}
@Override
+ @Deprecated
public String[] sqlCreateStrings(Dialect dialect) {
+ // Implemented exclusively for backwards compatibility for callers other than Hibernate ORM.
+ // This is not called by Hibernate ORM and will not take into account
+ // default catalog/schema set through configuration properties.
+ return sqlCreateStrings( SqlStringGenerationContextImpl.forBackwardsCompatibility( dialect, null, null ) );
+ }
+
+ @Override
+ public String[] sqlCreateStrings(SqlStringGenerationContext context) {
final String[] copy = new String[createStrings.length];
for ( int i = 0, max =createStrings.length; i
+ * Note that the Identifiers returned from this helper already account for auto-quoting.
+ *
+ * @deprecated Use {@link #toIdentifier(String)} instead.
+ */
+ @Deprecated
+ IdentifierHelper getIdentifierHelper();
+
+ /**
+ * Generate an Identifier instance from its simple name as obtained from mapping
+ * information.
+ *
+ * Note that Identifiers returned from here may be implicitly quoted based on
+ * 'globally quoted identifiers' or based on reserved words.
+ *
+ * @param text The text form of a name as obtained from mapping information.
+ *
+ * @return The identifier form of the name.
+ */
+ Identifier toIdentifier(String text);
+
+ /**
+ * @return The default catalog, used for table/sequence names that do not explicitly mention a catalog.
+ * May be {@code null}.
+ * This default is generally applied automatically by the {@link #format(QualifiedName) format methods},
+ * but in some cases it can be useful to access it directly.
+ */
+ Identifier getDefaultCatalog();
+
+ /**
+ * @param explicitCatalogOrNull An explicitly configured catalog, or {@code null}.
+ * @return The given identifier if non-{@code null}, or the default catalog otherwise.
+ */
+ Identifier catalogWithDefault(Identifier explicitCatalogOrNull);
+
+ /**
+ * @return The default schema, used for table/sequence names that do not explicitly mention a schema.
+ * May be {@code null}.
+ * This default is generally applied automatically by the {@link #format(QualifiedName) format methods},
+ * but in some cases it can be useful to access it directly.
+ */
+ Identifier getDefaultSchema();
+
+ /**
+ * @param explicitSchemaOrNull An explicitly configured schema, or {@code null}.
+ * @return The given identifier if non-{@code null}, or the default schema otherwise.
+ */
+ Identifier schemaWithDefault(Identifier explicitSchemaOrNull);
+
+ /**
+ * Render a formatted a table name
+ *
+ * @param qualifiedName The table name
+ *
+ * @return The formatted name,
+ */
+ String format(QualifiedTableName qualifiedName);
+
+ /**
+ * Render a formatted a table name, ignoring the default catalog/schema.
+ *
+ * @param qualifiedName The table name
+ *
+ * @return The formatted name
+ */
+ String formatWithoutDefaults(QualifiedTableName qualifiedName);
+
+ /**
+ * Render a formatted sequence name
+ *
+ * @param qualifiedName The sequence name
+ *
+ * @return The formatted name
+ */
+ String format(QualifiedSequenceName qualifiedName);
+
+ /**
+ * Render a formatted non-table and non-sequence qualified name
+ *
+ * @param qualifiedName The name
+ *
+ * @return The formatted name
+ */
+ String format(QualifiedName qualifiedName);
+
+ /**
+ * Render a formatted sequence name, without the catalog (even the default one).
+ *
+ * @param qualifiedName The sequence name
+ *
+ * @return The formatted name
+ */
+ String formatWithoutCatalog(QualifiedSequenceName qualifiedName);
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/internal/SqlStringGenerationContextImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/internal/SqlStringGenerationContextImpl.java
new file mode 100644
index 000000000000..e61331500201
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/internal/SqlStringGenerationContextImpl.java
@@ -0,0 +1,237 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
+ */
+package org.hibernate.boot.model.relational.internal;
+
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.hibernate.boot.model.naming.Identifier;
+import org.hibernate.boot.model.relational.Database;
+import org.hibernate.boot.model.relational.Namespace;
+import org.hibernate.boot.model.relational.QualifiedName;
+import org.hibernate.boot.model.relational.QualifiedSequenceName;
+import org.hibernate.boot.model.relational.QualifiedTableName;
+import org.hibernate.boot.model.relational.SqlStringGenerationContext;
+import org.hibernate.cfg.AvailableSettings;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.jdbc.env.internal.QualifiedObjectNameFormatterStandardImpl;
+import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
+import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
+import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
+import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
+import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter;
+
+import org.jboss.logging.Logger;
+
+public class SqlStringGenerationContextImpl
+ implements SqlStringGenerationContext {
+ private static final Logger log = Logger.getLogger( SqlStringGenerationContextImpl.class );
+
+ /**
+ * @param jdbcEnvironment The JDBC environment, to extract the dialect, identifier helper, etc.
+ * @param database The database metadata, to retrieve the implicit namespace name configured through XML mapping.
+ * @param configurationMap The configuration map, holding settings such as {@link AvailableSettings#DEFAULT_SCHEMA}.
+ * @return An {@link SqlStringGenerationContext}.
+ */
+ public static SqlStringGenerationContext fromConfigurationMap(JdbcEnvironment jdbcEnvironment,
+ Database database, Map configurationMap) {
+ String defaultCatalog = (String) configurationMap.get( AvailableSettings.DEFAULT_CATALOG );
+ String defaultSchema = (String) configurationMap.get( AvailableSettings.DEFAULT_SCHEMA );
+ return fromExplicit( jdbcEnvironment, database, defaultCatalog, defaultSchema );
+ }
+
+ /**
+ * @param jdbcEnvironment The JDBC environment, to extract the dialect, identifier helper, etc.
+ * @param database The database metadata, to retrieve the implicit namespace name configured through XML mapping.
+ * @param defaultCatalog The default catalog to use; if {@code null}, will use the implicit catalog that was configured through XML mapping.
+ * @param defaultSchema The default schema to use; if {@code null}, will use the implicit schema that was configured through XML mapping.
+ * @return An {@link SqlStringGenerationContext}.
+ */
+ public static SqlStringGenerationContext fromExplicit(JdbcEnvironment jdbcEnvironment,
+ Database database, String defaultCatalog, String defaultSchema) {
+ Namespace.Name implicitNamespaceName = database.getPhysicalImplicitNamespaceName();
+ IdentifierHelper identifierHelper = jdbcEnvironment.getIdentifierHelper();
+ NameQualifierSupport nameQualifierSupport = jdbcEnvironment.getNameQualifierSupport();
+ Identifier actualDefaultCatalog = null;
+ if ( nameQualifierSupport.supportsCatalogs() ) {
+ actualDefaultCatalog = identifierHelper.toIdentifier( defaultCatalog );
+ if ( actualDefaultCatalog == null ) {
+ actualDefaultCatalog = implicitNamespaceName.getCatalog();
+ }
+ }
+ Identifier actualDefaultSchema = null;
+ if ( nameQualifierSupport.supportsSchemas() ) {
+ actualDefaultSchema = identifierHelper.toIdentifier( defaultSchema );
+ if ( defaultSchema == null ) {
+ actualDefaultSchema = implicitNamespaceName.getSchema();
+ }
+ }
+ return new SqlStringGenerationContextImpl( jdbcEnvironment, actualDefaultCatalog, actualDefaultSchema );
+ }
+
+ /**
+ * @param dialect The dialect to use.
+ * @param defaultCatalog The default catalog to use.
+ * @param defaultSchema The default schema to use.
+ * @return An {@link SqlStringGenerationContext}.
+ * @deprecated Only use for backwards compatibility in deprecated methods.
+ * New methods should take the {@link SqlStringGenerationContext} as an argument,
+ * and should not need to create their own context.
+ */
+ @Deprecated
+ public static SqlStringGenerationContext forBackwardsCompatibility(Dialect dialect, String defaultCatalog, String defaultSchema) {
+ NameQualifierSupport nameQualifierSupport = dialect.getNameQualifierSupport();
+ if ( nameQualifierSupport == null ) {
+ // assume both catalogs and schemas are supported
+ nameQualifierSupport = NameQualifierSupport.BOTH;
+ }
+ QualifiedObjectNameFormatter qualifiedObjectNameFormatter =
+ new QualifiedObjectNameFormatterStandardImpl( nameQualifierSupport );
+
+ Identifier actualDefaultCatalog = null;
+ if ( nameQualifierSupport.supportsCatalogs() ) {
+ actualDefaultCatalog = Identifier.toIdentifier( defaultCatalog );
+ }
+ Identifier actualDefaultSchema = null;
+ if ( nameQualifierSupport.supportsSchemas() ) {
+ actualDefaultSchema = Identifier.toIdentifier( defaultSchema );
+ }
+ return new SqlStringGenerationContextImpl( dialect, null, qualifiedObjectNameFormatter,
+ actualDefaultCatalog, actualDefaultSchema );
+ }
+
+ public static SqlStringGenerationContext forTests(JdbcEnvironment jdbcEnvironment) {
+ return forTests( jdbcEnvironment, null, null );
+ }
+
+ public static SqlStringGenerationContext forTests(JdbcEnvironment jdbcEnvironment,
+ String defaultCatalog, String defaultSchema) {
+ IdentifierHelper identifierHelper = jdbcEnvironment.getIdentifierHelper();
+ return new SqlStringGenerationContextImpl( jdbcEnvironment,
+ identifierHelper.toIdentifier( defaultCatalog ), identifierHelper.toIdentifier( defaultSchema ) );
+ }
+
+ private final Dialect dialect;
+ private final IdentifierHelper identifierHelper;
+ private final QualifiedObjectNameFormatter qualifiedObjectNameFormatter;
+ private final Identifier defaultCatalog;
+ private final Identifier defaultSchema;
+
+ @SuppressWarnings("deprecation")
+ private SqlStringGenerationContextImpl(JdbcEnvironment jdbcEnvironment,
+ Identifier defaultCatalog, Identifier defaultSchema) {
+ this( jdbcEnvironment.getDialect(), jdbcEnvironment.getIdentifierHelper(),
+ jdbcEnvironment.getQualifiedObjectNameFormatter(),
+ defaultCatalog, defaultSchema );
+ }
+
+ private SqlStringGenerationContextImpl(Dialect dialect, IdentifierHelper identifierHelper,
+ QualifiedObjectNameFormatter qualifiedObjectNameFormatter,
+ Identifier defaultCatalog, Identifier defaultSchema) {
+ this.dialect = dialect;
+ this.identifierHelper = identifierHelper;
+ this.qualifiedObjectNameFormatter = qualifiedObjectNameFormatter;
+ this.defaultCatalog = defaultCatalog;
+ this.defaultSchema = defaultSchema;
+ }
+
+ @Override
+ public Dialect getDialect() {
+ return dialect;
+ }
+
+ @Override
+ public IdentifierHelper getIdentifierHelper() {
+ return identifierHelper;
+ }
+
+ @Override
+ public Identifier toIdentifier(String text) {
+ return identifierHelper != null ? identifierHelper.toIdentifier( text ) : Identifier.toIdentifier( text );
+ }
+
+ @Override
+ public Identifier getDefaultCatalog() {
+ return defaultCatalog;
+ }
+
+ @Override
+ public Identifier catalogWithDefault(Identifier explicitCatalogOrNull) {
+ return explicitCatalogOrNull != null ? explicitCatalogOrNull : defaultCatalog;
+ }
+
+ @Override
+ public Identifier getDefaultSchema() {
+ return defaultSchema;
+ }
+
+ @Override
+ public Identifier schemaWithDefault(Identifier explicitSchemaOrNull) {
+ return explicitSchemaOrNull != null ? explicitSchemaOrNull : defaultSchema;
+ }
+
+ private QualifiedTableName withDefaults(QualifiedTableName name) {
+ if ( name.getCatalogName() == null && defaultCatalog != null
+ || name.getSchemaName() == null && defaultSchema != null ) {
+ return new QualifiedTableName( catalogWithDefault( name.getCatalogName() ),
+ schemaWithDefault( name.getSchemaName() ), name.getTableName() );
+ }
+ return name;
+ }
+
+ private QualifiedSequenceName withDefaults(QualifiedSequenceName name) {
+ if ( name.getCatalogName() == null && defaultCatalog != null
+ || name.getSchemaName() == null && defaultSchema != null ) {
+ return new QualifiedSequenceName( catalogWithDefault( name.getCatalogName() ),
+ schemaWithDefault( name.getSchemaName() ), name.getSequenceName() );
+ }
+ return name;
+ }
+
+ private QualifiedName withDefaults(QualifiedName name) {
+ if ( name.getCatalogName() == null && defaultCatalog != null
+ || name.getSchemaName() == null && defaultSchema != null ) {
+ return new QualifiedSequenceName( catalogWithDefault( name.getCatalogName() ),
+ schemaWithDefault( name.getSchemaName() ), name.getObjectName() );
+ }
+ return name;
+ }
+
+ @Override
+ public String format(QualifiedTableName qualifiedName) {
+ return qualifiedObjectNameFormatter.format( withDefaults( qualifiedName ), dialect );
+ }
+
+ @Override
+ public String formatWithoutDefaults(QualifiedTableName qualifiedName) {
+ return qualifiedObjectNameFormatter.format( qualifiedName, dialect );
+ }
+
+ @Override
+ public String format(QualifiedSequenceName qualifiedName) {
+ return qualifiedObjectNameFormatter.format( withDefaults( qualifiedName ), dialect );
+ }
+
+ @Override
+ public String format(QualifiedName qualifiedName) {
+ return qualifiedObjectNameFormatter.format( withDefaults( qualifiedName ), dialect );
+ }
+
+ @Override
+ public String formatWithoutCatalog(QualifiedSequenceName qualifiedName) {
+ QualifiedSequenceName nameToFormat;
+ if ( qualifiedName.getCatalogName() != null
+ || qualifiedName.getSchemaName() == null && defaultSchema != null ) {
+ nameToFormat = new QualifiedSequenceName( null,
+ schemaWithDefault( qualifiedName.getSchemaName() ), qualifiedName.getSequenceName() );
+ }
+ else {
+ nameToFormat = qualifiedName;
+ }
+ return qualifiedObjectNameFormatter.format( nameToFormat, dialect );
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java
index 3efebf2d3f62..3052ee32c0c9 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java
@@ -804,19 +804,6 @@ private void makeIdentifier(
// YUCK! but cannot think of a clean way to do this given the string-config based scheme
params.put( PersistentIdentifierGenerator.IDENTIFIER_NORMALIZER, objectNameNormalizer);
- if ( database.getDefaultNamespace().getPhysicalName().getSchema() != null ) {
- params.setProperty(
- PersistentIdentifierGenerator.SCHEMA,
- database.getDefaultNamespace().getPhysicalName().getSchema().render( database.getDialect() )
- );
- }
- if ( database.getDefaultNamespace().getPhysicalName().getCatalog() != null ) {
- params.setProperty(
- PersistentIdentifierGenerator.CATALOG,
- database.getDefaultNamespace().getPhysicalName().getCatalog().render( database.getDialect() )
- );
- }
-
params.putAll( generator.getParameters() );
identifierValue.setIdentifierGeneratorProperties( params );
@@ -2961,7 +2948,7 @@ private Identifier determineCatalogName(TableSpecificationSource tableSpecSource
return database.toIdentifier( tableSpecSource.getExplicitCatalogName() );
}
else {
- return database.getDefaultNamespace().getName().getCatalog();
+ return null;
}
}
@@ -2970,7 +2957,7 @@ private Identifier determineSchemaName(TableSpecificationSource tableSpecSource)
return database.toIdentifier( tableSpecSource.getExplicitSchemaName() );
}
else {
- return database.getDefaultNamespace().getName().getSchema();
+ return null;
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/spi/MetadataSourceProcessor.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/spi/MetadataSourceProcessor.java
index 4a857b8a7848..d5e651d2ff06 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/spi/MetadataSourceProcessor.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/spi/MetadataSourceProcessor.java
@@ -47,7 +47,7 @@ public interface MetadataSourceProcessor {
void processNamedQueries();
/**
- * Process all {@link org.hibernate.mapping.AuxiliaryDatabaseObject} definitions.
+ * Process all {@link org.hibernate.boot.model.relational.AuxiliaryDatabaseObject} definitions.
*
* This step has no prerequisites.
*/
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java
index 1ac730e0e52c..84953eac45ab 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java
@@ -423,6 +423,16 @@ public ImmutableEntityUpdateQueryHandlingMode getImmutableEntityUpdateQueryHandl
return delegate.getImmutableEntityUpdateQueryHandlingMode();
}
+ @Override
+ public String getDefaultCatalog() {
+ return delegate.getDefaultCatalog();
+ }
+
+ @Override
+ public String getDefaultSchema() {
+ return delegate.getDefaultSchema();
+ }
+
@Override
public boolean inClauseParameterPaddingEnabled() {
return delegate.inClauseParameterPaddingEnabled();
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java
index 93cb7516128e..30b3a761c2c1 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java
@@ -293,6 +293,26 @@ default ImmutableEntityUpdateQueryHandlingMode getImmutableEntityUpdateQueryHand
return ImmutableEntityUpdateQueryHandlingMode.WARNING;
}
+ /**
+ * The default catalog to use in generated SQL when a catalog wasn't specified in the mapping,
+ * neither explicitly nor implicitly (see the concept of implicit catalog in XML mapping).
+ *
+ * @return The default catalog to use.
+ */
+ default String getDefaultCatalog() {
+ return null;
+ }
+
+ /**
+ * The default schema to use in generated SQL when a catalog wasn't specified in the mapping,
+ * neither explicitly nor implicitly (see the concept of implicit schema in XML mapping).
+ *
+ * @return The default schema to use.
+ */
+ default String getDefaultSchema() {
+ return null;
+ }
+
default boolean inClauseParameterPaddingEnabled() {
return false;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java
index d142264bd9c7..849ebc93fd8c 100644
--- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java
@@ -34,6 +34,7 @@
import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState;
import org.hibernate.engine.spi.CompositeOwner;
import org.hibernate.engine.spi.CompositeTracker;
+import org.hibernate.engine.spi.EnhancedEntity;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.ExtendedSelfDirtinessTracker;
import org.hibernate.engine.spi.Managed;
@@ -68,6 +69,7 @@
public class EnhancerImpl implements Enhancer {
private static final CoreMessageLogger log = CoreLogging.messageLogger( Enhancer.class );
+ private static final AnnotationDescription TRANSIENT_ANNOTATION = AnnotationDescription.Builder.ofType( Transient.class ).build();
protected final ByteBuddyEnhancementContext enhancementContext;
private final ByteBuddyState byteBuddyState;
@@ -154,12 +156,14 @@ private DynamicType.Builder> doEnhance(DynamicType.Builder> builder, TypeDes
log.debugf( "Skipping enhancement of [%s]: already enhanced", managedCtClass.getName() );
return null;
}
+ final EnhancementStatus es = new EnhancementStatus( managedCtClass.getName() );
if ( enhancementContext.isEntityClass( managedCtClass ) ) {
log.debugf( "Enhancing [%s] as Entity", managedCtClass.getName() );
builder = builder.implement( ManagedEntity.class )
.defineMethod( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME, Object.class, Visibility.PUBLIC )
.intercept( FixedValue.self() );
+ es.enabledInterfaceManagedEntity();
builder = addFieldWithGetterAndSetter(
builder,
@@ -183,7 +187,7 @@ private DynamicType.Builder> doEnhance(DynamicType.Builder> builder, TypeDes
EnhancerConstants.NEXT_SETTER_NAME
);
- builder = addInterceptorHandling( builder, managedCtClass );
+ builder = addInterceptorHandling( builder, managedCtClass, es );
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
List collectionFields = collectCollectionFields( managedCtClass );
@@ -191,7 +195,7 @@ private DynamicType.Builder> doEnhance(DynamicType.Builder> builder, TypeDes
if ( collectionFields.isEmpty() ) {
builder = builder.implement( SelfDirtinessTracker.class )
.defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, FieldPersistence.TRANSIENT, Visibility.PRIVATE )
- .annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() )
+ .annotateField( TRANSIENT_ANNOTATION )
.defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, void.class, Visibility.PUBLIC )
.withParameters( String.class )
.intercept( implementationTrackChange )
@@ -206,13 +210,15 @@ private DynamicType.Builder> doEnhance(DynamicType.Builder> builder, TypeDes
.intercept( implementationSuspendDirtyTracking )
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, CollectionTracker.class, Visibility.PUBLIC )
.intercept( implementationGetCollectionTrackerWithoutCollections );
+ es.enabledInterfaceSelfDirtinessTracker();
}
else {
+ //TODO es.enableInterfaceExtendedSelfDirtinessTracker ? Careful with consequences..
builder = builder.implement( ExtendedSelfDirtinessTracker.class )
.defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, FieldPersistence.TRANSIENT, Visibility.PRIVATE )
- .annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() )
+ .annotateField( TRANSIENT_ANNOTATION )
.defineField( EnhancerConstants.TRACKER_COLLECTION_NAME, CollectionTracker.class, FieldPersistence.TRANSIENT, Visibility.PRIVATE )
- .annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() )
+ .annotateField( TRANSIENT_ANNOTATION )
.defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, void.class, Visibility.PUBLIC )
.withParameters( String.class )
.intercept( implementationTrackChange )
@@ -302,13 +308,13 @@ private DynamicType.Builder> doEnhance(DynamicType.Builder> builder, TypeDes
}
}
- return createTransformer( managedCtClass ).applyTo( builder );
+ return createTransformer( managedCtClass ).applyTo( builder, es );
}
else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
log.debugf( "Enhancing [%s] as Composite", managedCtClass.getName() );
builder = builder.implement( ManagedComposite.class );
- builder = addInterceptorHandling( builder, managedCtClass );
+ builder = addInterceptorHandling( builder, managedCtClass, es );
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
builder = builder.implement( CompositeTracker.class )
@@ -318,7 +324,7 @@ else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
FieldPersistence.TRANSIENT,
Visibility.PRIVATE
)
- .annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() )
+ .annotateField( TRANSIENT_ANNOTATION )
.defineMethod(
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
void.class,
@@ -335,17 +341,17 @@ else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
.intercept( implementationClearOwner );
}
- return createTransformer( managedCtClass ).applyTo( builder );
+ return createTransformer( managedCtClass ).applyTo( builder, es );
}
else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
log.debugf( "Enhancing [%s] as MappedSuperclass", managedCtClass.getName() );
builder = builder.implement( ManagedMappedSuperclass.class );
- return createTransformer( managedCtClass ).applyTo( builder );
+ return createTransformer( managedCtClass ).applyTo( builder, es );
}
else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
log.debugf( "Extended enhancement of [%s]", managedCtClass.getName() );
- return createTransformer( managedCtClass ).applyExtended( builder );
+ return createTransformer( managedCtClass ).applyExtended( builder, es );
}
else {
log.debugf( "Skipping enhancement of [%s]: not entity or composite", managedCtClass.getName() );
@@ -367,12 +373,13 @@ private boolean alreadyEnhanced(TypeDescription managedCtClass) {
return false;
}
- private DynamicType.Builder> addInterceptorHandling(DynamicType.Builder> builder, TypeDescription managedCtClass) {
+ private DynamicType.Builder> addInterceptorHandling(DynamicType.Builder> builder, TypeDescription managedCtClass, EnhancementStatus es) {
// interceptor handling is only needed if class has lazy-loadable attributes
if ( enhancementContext.hasLazyLoadableAttributes( managedCtClass ) ) {
log.debugf( "Weaving in PersistentAttributeInterceptable implementation on [%s]", managedCtClass.getName() );
builder = builder.implement( PersistentAttributeInterceptable.class );
+ es.enabledInterfacePersistentAttributeInterceptable();
builder = addFieldWithGetterAndSetter(
builder,
@@ -394,7 +401,7 @@ private static DynamicType.Builder> addFieldWithGetterAndSetter(
String setterName) {
return builder
.defineField( fieldName, type, Visibility.PRIVATE, FieldPersistence.TRANSIENT )
- .annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() )
+ .annotateField( TRANSIENT_ANNOTATION )
.defineMethod( getterName, type, Visibility.PUBLIC )
.intercept( FieldAccessor.ofField( fieldName ) )
.defineMethod( setterName, void.class, Visibility.PUBLIC )
@@ -592,4 +599,53 @@ void setClassNameAndBytes(String className, byte[] bytes) {
this.resolution = new Resolution.Explicit( bytes);
}
}
+
+ /**
+ * Attempt to keep track of which interfaces are being applied,
+ * so to attempt dodging the performance implications of for https://bugs.openjdk.org/browse/JDK-8180450
+ * We're optimising for the case in which entities are fully enhanced.
+ */
+ final static class EnhancementStatus {
+
+ private final String typeName;
+ private boolean managedEntity = false;
+ private boolean selfDirtynessTracker = false;
+ private boolean persistentAttributeInterceptable = false;
+ private boolean applied = false;
+
+ public EnhancementStatus(String typeName) {
+ this.typeName = typeName;
+ }
+
+ public void enabledInterfaceManagedEntity() {
+ this.managedEntity = true;
+ }
+
+ public void enabledInterfaceSelfDirtinessTracker() {
+ this.selfDirtynessTracker = true;
+ }
+
+ public void enabledInterfacePersistentAttributeInterceptable() {
+ this.persistentAttributeInterceptable = true;
+ }
+
+ public DynamicType.Builder> applySuperInterfaceOptimisations(DynamicType.Builder> builder) {
+ if ( applied ) {
+ throw new IllegalStateException("Should not apply super-interface optimisations twice");
+ }
+ else {
+ applied = true;
+ if ( managedEntity && persistentAttributeInterceptable && selfDirtynessTracker ) {
+ log.debugf( "Applying Enhancer optimisations for type [%s]; adding EnhancedEntity as additional marker.", typeName );
+ return builder.implement( EnhancedEntity.class );
+ }
+ else {
+ log.debugf( "Applying Enhancer optimisations for type [%s]; NOT enabling EnhancedEntity as additional marker.", typeName );
+ }
+ //TODO consider applying a marker for other combinations of interfaces as well?
+ }
+ return builder;
+ }
+ }
+
}
diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckerEqualsHelper.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckerEqualsHelper.java
new file mode 100644
index 000000000000..098db0b8051a
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckerEqualsHelper.java
@@ -0,0 +1,132 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
+ * See the lgpl.txt file in the root directory or .
+ */
+package org.hibernate.bytecode.enhance.internal.bytebuddy;
+
+import java.util.Objects;
+
+import org.hibernate.engine.spi.PersistentAttributeInterceptable;
+import org.hibernate.engine.spi.PersistentAttributeInterceptor;
+
+public final class InlineDirtyCheckerEqualsHelper {
+
+ public static boolean areEquals(
+ PersistentAttributeInterceptable persistentAttributeInterceptable,
+ String fieldName,
+ Object a,
+ Object b) {
+ final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor();
+ if ( persistentAttributeInterceptor != null
+ && !persistentAttributeInterceptor.isAttributeLoaded( fieldName ) ) {
+ return false;
+ }
+ return Objects.deepEquals( a, b );
+ }
+
+ public static boolean areEquals(
+ PersistentAttributeInterceptable persistentAttributeInterceptable,
+ String fieldName,
+ boolean a,
+ boolean b) {
+ final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor();
+ if ( persistentAttributeInterceptor != null
+ && !persistentAttributeInterceptor.isAttributeLoaded( fieldName ) ) {
+ return false;
+ }
+ return a == b;
+ }
+
+ public static boolean areEquals(
+ PersistentAttributeInterceptable persistentAttributeInterceptable,
+ String fieldName,
+ byte a,
+ byte b) {
+ final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor();
+ if ( persistentAttributeInterceptor != null
+ && !persistentAttributeInterceptor.isAttributeLoaded( fieldName ) ) {
+ return false;
+ }
+ return a == b;
+ }
+
+ public static boolean areEquals(
+ PersistentAttributeInterceptable persistentAttributeInterceptable,
+ String fieldName,
+ short a,
+ short b) {
+ final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor();
+ if ( persistentAttributeInterceptor != null
+ && !persistentAttributeInterceptor.isAttributeLoaded( fieldName ) ) {
+ return false;
+ }
+ return a == b;
+ }
+
+ public static boolean areEquals(
+ PersistentAttributeInterceptable persistentAttributeInterceptable,
+ String fieldName,
+ char a,
+ char b) {
+ final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor();
+ if ( persistentAttributeInterceptor != null
+ && !persistentAttributeInterceptor.isAttributeLoaded( fieldName ) ) {
+ return false;
+ }
+ return a == b;
+ }
+
+ public static boolean areEquals(
+ PersistentAttributeInterceptable persistentAttributeInterceptable,
+ String fieldName,
+ int a,
+ int b) {
+ final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor();
+ if ( persistentAttributeInterceptor != null
+ && !persistentAttributeInterceptor.isAttributeLoaded( fieldName ) ) {
+ return false;
+ }
+ return a == b;
+ }
+
+ public static boolean areEquals(
+ PersistentAttributeInterceptable persistentAttributeInterceptable,
+ String fieldName,
+ long a,
+ long b) {
+ final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor();
+ if ( persistentAttributeInterceptor != null
+ && !persistentAttributeInterceptor.isAttributeLoaded( fieldName ) ) {
+ return false;
+ }
+ return a == b;
+ }
+
+ public static boolean areEquals(
+ PersistentAttributeInterceptable persistentAttributeInterceptable,
+ String fieldName,
+ float a,
+ float b) {
+ final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor();
+ if ( persistentAttributeInterceptor != null
+ && !persistentAttributeInterceptor.isAttributeLoaded( fieldName ) ) {
+ return false;
+ }
+ return a == b;
+ }
+
+ public static boolean areEquals(
+ PersistentAttributeInterceptable persistentAttributeInterceptable,
+ String fieldName,
+ double a,
+ double b) {
+ final PersistentAttributeInterceptor persistentAttributeInterceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor();
+ if ( persistentAttributeInterceptor != null
+ && !persistentAttributeInterceptor.isAttributeLoaded( fieldName ) ) {
+ return false;
+ }
+ return a == b;
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java
index 1c61641c2940..6306e52436bb 100644
--- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java
+++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java
@@ -15,6 +15,7 @@
import org.hibernate.bytecode.enhance.internal.bytebuddy.EnhancerImpl.AnnotatedFieldDescription;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
+import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.asm.Advice;
@@ -31,16 +32,27 @@
final class InlineDirtyCheckingHandler implements Implementation, ByteCodeAppender {
+ private static final String HELPER_TYPE_NAME = Type.getInternalName( InlineDirtyCheckerEqualsHelper.class );
+ private static final Type PE_INTERCEPTABLE_TYPE = Type.getType( PersistentAttributeInterceptable.class );
+ private static final Type OBJECT_TYPE = Type.getType( Object.class );
+ private static final Type STRING_TYPE = Type.getType( String.class );
+
private final Implementation delegate;
private final TypeDescription managedCtClass;
private final FieldDescription.InDefinedShape persistentField;
+ private final boolean applyLazyCheck;
- private InlineDirtyCheckingHandler(Implementation delegate, TypeDescription managedCtClass, FieldDescription.InDefinedShape persistentField) {
+ private InlineDirtyCheckingHandler(
+ Implementation delegate,
+ TypeDescription managedCtClass,
+ FieldDescription.InDefinedShape persistentField,
+ boolean applyLazyCheck) {
this.delegate = delegate;
this.managedCtClass = managedCtClass;
this.persistentField = persistentField;
+ this.applyLazyCheck = applyLazyCheck;
}
static Implementation wrap(
@@ -57,8 +69,12 @@ else if ( !persistentField.hasAnnotation( Id.class )
&& !persistentField.hasAnnotation( EmbeddedId.class )
&& !( persistentField.getType().asErasure().isAssignableTo( Collection.class )
&& enhancementContext.isMappedCollection( persistentField ) ) ) {
- implementation = new InlineDirtyCheckingHandler( implementation, managedCtClass,
- persistentField.asDefined() );
+ implementation = new InlineDirtyCheckingHandler(
+ implementation,
+ managedCtClass,
+ persistentField.asDefined(),
+ enhancementContext.hasLazyLoadableAttributes( managedCtClass )
+ );
}
if ( enhancementContext.isCompositeClass( persistentField.getType().asErasure() )
@@ -97,6 +113,11 @@ public Size apply(
Context implementationContext,
MethodDescription instrumentedMethod) {
// if (arg != field) {
+
+ if ( applyLazyCheck ) {
+ methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
+ methodVisitor.visitLdcInsn( persistentField.getName() );
+ }
methodVisitor.visitVarInsn( Type.getType( persistentField.getType().asErasure().getDescriptor() ).getOpcode( Opcodes.ILOAD ), 1 );
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
if ( persistentField.getDeclaringType().asErasure().equals( managedCtClass ) ) {
@@ -117,30 +138,70 @@ public Size apply(
);
}
int branchCode;
- if ( persistentField.getType().isPrimitive() ) {
- if ( persistentField.getType().represents( long.class ) ) {
- methodVisitor.visitInsn( Opcodes.LCMP );
- }
- else if ( persistentField.getType().represents( float.class ) ) {
- methodVisitor.visitInsn( Opcodes.FCMPL );
- }
- else if ( persistentField.getType().represents( double.class ) ) {
- methodVisitor.visitInsn( Opcodes.DCMPL );
+ if ( applyLazyCheck ) {
+ if ( persistentField.getType().isPrimitive() ) {
+ final Type fieldType = Type.getType( persistentField.getDescriptor() );
+ methodVisitor.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ HELPER_TYPE_NAME,
+ "areEquals",
+ Type.getMethodDescriptor(
+ Type.BOOLEAN_TYPE,
+ PE_INTERCEPTABLE_TYPE,
+ STRING_TYPE,
+ fieldType,
+ fieldType
+ ),
+ false
+ );
}
else {
- methodVisitor.visitInsn( Opcodes.ISUB );
+ methodVisitor.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ HELPER_TYPE_NAME,
+ "areEquals",
+ Type.getMethodDescriptor(
+ Type.BOOLEAN_TYPE,
+ PE_INTERCEPTABLE_TYPE,
+ STRING_TYPE,
+ OBJECT_TYPE,
+ OBJECT_TYPE
+ ),
+ false
+ );
}
- branchCode = Opcodes.IFEQ;
+ branchCode = Opcodes.IFNE;
}
else {
- methodVisitor.visitMethodInsn(
- Opcodes.INVOKESTATIC,
- Type.getInternalName( Objects.class ),
- "deepEquals",
- Type.getMethodDescriptor( Type.getType( boolean.class ), Type.getType( Object.class ), Type.getType( Object.class ) ),
- false
- );
- branchCode = Opcodes.IFNE;
+ if ( persistentField.getType().isPrimitive() ) {
+ if ( persistentField.getType().represents( long.class ) ) {
+ methodVisitor.visitInsn( Opcodes.LCMP );
+ }
+ else if ( persistentField.getType().represents( float.class ) ) {
+ methodVisitor.visitInsn( Opcodes.FCMPL );
+ }
+ else if ( persistentField.getType().represents( double.class ) ) {
+ methodVisitor.visitInsn( Opcodes.DCMPL );
+ }
+ else {
+ methodVisitor.visitInsn( Opcodes.ISUB );
+ }
+ branchCode = Opcodes.IFEQ;
+ }
+ else {
+ methodVisitor.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ Type.getInternalName( Objects.class ),
+ "deepEquals",
+ Type.getMethodDescriptor(
+ Type.BOOLEAN_TYPE,
+ OBJECT_TYPE,
+ OBJECT_TYPE
+ ),
+ false
+ );
+ branchCode = Opcodes.IFNE;
+ }
}
Label skip = new Label();
methodVisitor.visitJumpInsn( branchCode, skip );
@@ -151,7 +212,7 @@ else if ( persistentField.getType().represents( double.class ) ) {
Opcodes.INVOKEVIRTUAL,
managedCtClass.getInternalName(),
EnhancerConstants.TRACKER_CHANGER_NAME,
- Type.getMethodDescriptor( Type.getType( void.class ), Type.getType( String.class ) ),
+ Type.getMethodDescriptor( Type.VOID_TYPE, STRING_TYPE ),
false
);
// }
@@ -159,7 +220,7 @@ else if ( persistentField.getType().represents( double.class ) ) {
if ( implementationContext.getClassFileVersion().isAtLeast( ClassFileVersion.JAVA_V6 ) ) {
methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null );
}
- return new Size( 1 + 2 * persistentField.getType().asErasure().getStackSize().getSize(), instrumentedMethod.getStackSize() );
+ return new Size( 3 + 2 * persistentField.getType().asErasure().getStackSize().getSize(), instrumentedMethod.getStackSize() );
}
@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java
index 287ce6b793e4..94c2e65a36db 100644
--- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java
+++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/PersistentAttributeTransformer.java
@@ -91,6 +91,12 @@ public static PersistentAttributeTransformer collectPersistentFields(
ByteBuddyEnhancementContext enhancementContext,
TypePool classPool) {
List persistentFieldList = new ArrayList<>();
+ // HHH-10646 Add fields inherited from @MappedSuperclass
+ // HHH-10981 There is no need to do it for @MappedSuperclass
+ // HHH-15505 This needs to be done first so that fields with the same name in the mappedsuperclass and entity are handled correctly
+ if ( !enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
+ persistentFieldList.addAll( collectInheritPersistentFields( managedCtClass, enhancementContext ) );
+ }
for ( FieldDescription ctField : managedCtClass.getDeclaredFields() ) {
// skip static fields and skip fields added by enhancement and outer reference in inner classes
if ( ctField.getName().startsWith( "$$_hibernate_" ) || "this$0".equals( ctField.getName() ) ) {
@@ -101,11 +107,6 @@ public static PersistentAttributeTransformer collectPersistentFields(
persistentFieldList.add( annotatedField );
}
}
- // HHH-10646 Add fields inherited from @MappedSuperclass
- // HHH-10981 There is no need to do it for @MappedSuperclass
- if ( !enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
- persistentFieldList.addAll( collectInheritPersistentFields( managedCtClass, enhancementContext ) );
- }
AnnotatedFieldDescription[] orderedFields = enhancementContext.order( persistentFieldList.toArray( new AnnotatedFieldDescription[0] ) );
log.debugf( "Persistent fields for entity %s: %s", managedCtClass.getName(), Arrays.toString( orderedFields ) );
@@ -200,7 +201,8 @@ private AnnotatedFieldDescription getEnhancedField(String owner, String name, St
return null;
}
- DynamicType.Builder> applyTo(DynamicType.Builder> builder) {
+ DynamicType.Builder> applyTo(DynamicType.Builder> builder, EnhancerImpl.EnhancementStatus es) {
+ builder = es.applySuperInterfaceOptimisations(builder);
boolean compositeOwner = false;
builder = builder.visit( new AsmVisitorWrapper.ForDeclaredMethods().invokable( NOT_HIBERNATE_GENERATED, this ) );
@@ -255,7 +257,7 @@ DynamicType.Builder> applyTo(DynamicType.Builder> builder) {
}
if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
- builder = applyExtended( builder );
+ builder = applyExtended( builder, es );
}
return builder;
@@ -304,7 +306,7 @@ private Implementation fieldWriterImplementation(AnnotatedFieldDescription enhan
}
}
- DynamicType.Builder> applyExtended(DynamicType.Builder> builder) {
+ DynamicType.Builder> applyExtended(DynamicType.Builder> builder, EnhancerImpl.EnhancementStatus es) {
AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper enhancer = new FieldAccessEnhancer( managedCtClass, enhancementContext, classPool );
return builder.visit( new AsmVisitorWrapper.ForDeclaredMethods().invokable( NOT_HIBERNATE_GENERATED, enhancer ) );
}
diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/BytecodeLazyAttributeInterceptor.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/BytecodeLazyAttributeInterceptor.java
index a4fd9a7593e0..5ff6b950c8c3 100644
--- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/BytecodeLazyAttributeInterceptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/BytecodeLazyAttributeInterceptor.java
@@ -36,6 +36,7 @@ public interface BytecodeLazyAttributeInterceptor extends SessionAssociableInter
*/
void attributeInitialized(String name);
+ @Override
boolean isAttributeLoaded(String fieldName);
boolean hasAnyUninitializedAttributes();
diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java
index e5a65e356579..005889668d2c 100644
--- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java
@@ -72,8 +72,9 @@ public EnhancementAsProxyLazinessInterceptor(
this.inLineDirtyChecking = entityPersister.getEntityMode() == EntityMode.POJO
&& SelfDirtinessTracker.class.isAssignableFrom( entityPersister.getMappedClass() );
// if self-dirty tracking is enabled but DynamicUpdate is not enabled then we need to initialise the entity
- // because the pre-computed update statement contains even not dirty properties and so we need all the values
- initializeBeforeWrite = !( inLineDirtyChecking && entityPersister.getEntityMetamodel().isDynamicUpdate() );
+ // because the pre-computed update statement contains even not dirty properties and so we need all the values
+ // we have to initialise it even if it's versioned to fetch the current version
+ initializeBeforeWrite = !( inLineDirtyChecking && entityPersister.getEntityMetamodel().isDynamicUpdate() ) || entityPersister.isVersioned();
status = Status.UNINITIALIZED;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/LazyAttributeLoadingInterceptor.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/LazyAttributeLoadingInterceptor.java
index 1b084cccd223..ecdbade9de96 100644
--- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/LazyAttributeLoadingInterceptor.java
+++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/LazyAttributeLoadingInterceptor.java
@@ -17,11 +17,15 @@
import org.hibernate.bytecode.enhance.spi.CollectionTracker;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.collection.spi.PersistentCollection;
+import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.spi.SelfDirtinessTracker;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.persister.entity.EntityPersister;
+import static org.hibernate.engine.internal.ManagedTypeHelper.asSelfDirtinessTracker;
+import static org.hibernate.engine.internal.ManagedTypeHelper.isSelfDirtinessTracker;
+
/**
* Interceptor that loads attributes lazily
*
@@ -30,6 +34,8 @@
*/
public class LazyAttributeLoadingInterceptor extends AbstractLazyLoadInterceptor {
private final Object identifier;
+
+ //N.B. this Set needs to be treated as immutable
private final Set lazyFields;
private Set initializedLazyFields;
@@ -40,7 +46,8 @@ public LazyAttributeLoadingInterceptor(
SharedSessionContractImplementor session) {
super( entityName, session );
this.identifier = identifier;
- this.lazyFields = lazyFields;
+ //Important optimisation to not actually do a Map lookup for entities which don't have any lazy fields at all:
+ this.lazyFields = org.hibernate.internal.util.collections.CollectionHelper.toSmallSet( lazyFields );
}
@Override
@@ -121,7 +128,7 @@ public boolean isAttributeLoaded(String fieldName) {
}
private boolean isLazyAttribute(String fieldName) {
- return lazyFields == null || lazyFields.contains( fieldName );
+ return lazyFields.contains( fieldName );
}
private boolean isInitializedLazyField(String fieldName) {
@@ -129,7 +136,7 @@ private boolean isInitializedLazyField(String fieldName) {
}
public boolean hasAnyUninitializedAttributes() {
- if ( lazyFields == null || lazyFields.isEmpty() ) {
+ if ( lazyFields.isEmpty() ) {
return false;
}
@@ -152,13 +159,14 @@ public String toString() {
}
private void takeCollectionSizeSnapshot(Object target, String fieldName, Object value) {
- if ( value instanceof Collection && target instanceof SelfDirtinessTracker ) {
+ if ( value instanceof Collection && isSelfDirtinessTracker( target ) ) {
// This must be called first, so that we remember that there is a collection out there,
// even if we don't know its size (see below).
- CollectionTracker tracker = ( (SelfDirtinessTracker) target ).$$_hibernate_getCollectionTracker();
+ final SelfDirtinessTracker trackerAsSDT = asSelfDirtinessTracker( target );
+ CollectionTracker tracker = trackerAsSDT.$$_hibernate_getCollectionTracker();
if ( tracker == null ) {
- ( (SelfDirtinessTracker) target ).$$_hibernate_clearDirtyAttributes();
- tracker = ( (SelfDirtinessTracker) target ).$$_hibernate_getCollectionTracker();
+ trackerAsSDT.$$_hibernate_clearDirtyAttributes();
+ tracker = trackerAsSDT.$$_hibernate_getCollectionTracker();
}
if ( value instanceof PersistentCollection && !( (PersistentCollection) value ).wasInitialized() ) {
diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java
index 90c5b78a0890..593773e6fda2 100644
--- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java
+++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java
@@ -24,6 +24,7 @@
import java.util.function.Function;
import org.hibernate.HibernateException;
+import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.bytecode.spi.BasicProxyFactory;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.proxy.ProxyConfiguration;
@@ -188,6 +189,10 @@ public Unloaded> make(Function> makeProxyFun
return make( makeProxyFunction.apply( byteBuddy ) );
}
+ public Unloaded> make(TypePool typePool, Function> makeProxyFunction) {
+ return make( typePool, makeProxyFunction.apply( byteBuddy ) );
+ }
+
private Unloaded> make(DynamicType.Builder> builder) {
return make( null, builder );
}
@@ -248,7 +253,7 @@ public static class ProxyDefinitionHelpers {
private final ElementMatcher super MethodDescription> groovyGetMetaClassFilter;
private final ElementMatcher super MethodDescription> virtualNotFinalizerFilter;
- private final ElementMatcher super MethodDescription> hibernateGeneratedMethodFilter;
+ private final ElementMatcher super MethodDescription> proxyNonInterceptedMethodFilter;
private final MethodDelegation delegateToInterceptorDispatcherMethodDelegation;
private final FieldAccessor.PropertyConfigurable interceptorFieldAccessor;
@@ -256,7 +261,11 @@ private ProxyDefinitionHelpers() {
this.groovyGetMetaClassFilter = isSynthetic().and( named( "getMetaClass" )
.and( returns( td -> "groovy.lang.MetaClass".equals( td.getName() ) ) ) );
this.virtualNotFinalizerFilter = isVirtual().and( not( isFinalizer() ) );
- this.hibernateGeneratedMethodFilter = nameStartsWith( "$$_hibernate_" ).and( isVirtual() );
+ this.proxyNonInterceptedMethodFilter = nameStartsWith( "$$_hibernate_" ).and( isVirtual() )
+ // HHH-15090: Don't apply extended enhancement reader/writer methods to the proxy;
+ // those need to be executed on the actual entity.
+ .and( not( nameStartsWith( EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX ) ) )
+ .and( not( nameStartsWith( EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX ) ) );
PrivilegedAction delegateToInterceptorDispatcherMethodDelegationPrivilegedAction =
new PrivilegedAction() {
@@ -294,8 +303,8 @@ public ElementMatcher super MethodDescription> getVirtualNotFinalizerFilter()
return virtualNotFinalizerFilter;
}
- public ElementMatcher super MethodDescription> getHibernateGeneratedMethodFilter() {
- return hibernateGeneratedMethodFilter;
+ public ElementMatcher super MethodDescription> getProxyNonInterceptedMethodFilter() {
+ return proxyNonInterceptedMethodFilter;
}
public MethodDelegation getDelegateToInterceptorDispatcherMethodDelegation() {
diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/CollectionCacheInvalidator.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/CollectionCacheInvalidator.java
index 81793fb23d46..491af0356634 100644
--- a/hibernate-core/src/main/java/org/hibernate/cache/internal/CollectionCacheInvalidator.java
+++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/CollectionCacheInvalidator.java
@@ -178,13 +178,14 @@ private void evict(Serializable id, CollectionPersister collectionPersister, Eve
if ( LOG.isDebugEnabled() ) {
LOG.debug( "Evict CollectionRegion " + collectionPersister.getRole() + " for id " + id );
}
- AfterTransactionCompletionProcess afterTransactionProcess = new CollectionEvictCacheAction(
+ CollectionEvictCacheAction evictCacheAction = new CollectionEvictCacheAction(
collectionPersister,
null,
id,
session
- ).lockCache();
- session.getActionQueue().registerProcess( afterTransactionProcess );
+ );
+ evictCacheAction.execute();
+ session.getActionQueue().registerProcess( evictCacheAction.getAfterTransactionCompletionProcess() );
}
//execute the same process as invalidation with collection operations
@@ -199,13 +200,9 @@ protected CollectionEvictCacheAction(
@Override
public void execute() throws HibernateException {
- }
-
- public AfterTransactionCompletionProcess lockCache() {
beforeExecutions();
- return getAfterTransactionCompletionProcess();
+ evict();
}
-
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java
index 9ce34807a33b..ae218d636f83 100644
--- a/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java
+++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java
@@ -491,6 +491,10 @@ public QueryResultsCache getQueryResultsCacheStrictly(String regionName) {
return null;
}
+ if ( regionName == null || regionName.equals( getDefaultQueryResultsCache().getRegion().getName() ) ) {
+ return getDefaultQueryResultsCache();
+ }
+
return namedQueryResultsCacheMap.get( regionName );
}
diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/NoCachingTransactionSynchronizationImpl.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/NoCachingTransactionSynchronizationImpl.java
index b6914f3a484a..920e64158601 100644
--- a/hibernate-core/src/main/java/org/hibernate/cache/internal/NoCachingTransactionSynchronizationImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/NoCachingTransactionSynchronizationImpl.java
@@ -16,4 +16,9 @@ public class NoCachingTransactionSynchronizationImpl extends AbstractCacheTransa
public NoCachingTransactionSynchronizationImpl(RegionFactory regionFactory) {
super( regionFactory );
}
+
+ @Override
+ public long getCachingTimestamp() {
+ throw new UnsupportedOperationException( "Method not supported when 2LC is not enabled" );
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheTransactionSynchronization.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheTransactionSynchronization.java
index 415abdc70c48..8d98aab96af2 100644
--- a/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheTransactionSynchronization.java
+++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheTransactionSynchronization.java
@@ -48,9 +48,29 @@ public interface CacheTransactionSynchronization {
*
* @implSpec This "timestamp" need not be related to timestamp in the Java
* Date/millisecond sense. It just needs to be an incrementing value.
+ *
+ * @deprecated Use {@link CacheTransactionSynchronization#getCachingTimestamp()} instead.
*/
+ @Deprecated
long getCurrentTransactionStartTimestamp();
+ /**
+ * What is the start time of this context object?
+ *
+ * @apiNote If not currently joined to a transaction, the timestamp from
+ * the last transaction is safe to use. If not ever/yet joined to a
+ * transaction, a timestamp at the time the Session/CacheTransactionSynchronization
+ * were created should be returned.
+ *
+ * @implSpec This "timestamp" need not be related to timestamp in the Java
+ * Date/millisecond sense. It just needs to be an incrementing value.
+ *
+ * An UnsupportedOperationException is thrown if the Second Level Cache has not been enabled
+ */
+ default long getCachingTimestamp(){
+ return getCurrentTransactionStartTimestamp();
+ }
+
/**
* Callback that owning Session has become joined to a resource transaction.
*
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
index 786acc6b1296..49a059e61628 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
@@ -149,7 +149,6 @@
import org.hibernate.cfg.internal.NullableDiscriminatorColumnSecondPass;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.spi.FilterDefinition;
-import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.jpa.event.internal.CallbackDefinitionResolverLegacyImpl;
@@ -171,6 +170,7 @@
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.UnionSubclass;
+import static org.hibernate.cfg.BinderHelper.isEmptyAnnotationValue;
import static org.hibernate.internal.CoreLogging.messageLogger;
/**
@@ -467,20 +467,6 @@ private static IdentifierGeneratorDefinition buildIdGenerator(
IdentifierGeneratorDefinition.Builder definitionBuilder = new IdentifierGeneratorDefinition.Builder();
- if ( context.getMappingDefaults().getImplicitSchemaName() != null ) {
- definitionBuilder.addParam(
- PersistentIdentifierGenerator.SCHEMA,
- context.getMappingDefaults().getImplicitSchemaName()
- );
- }
-
- if ( context.getMappingDefaults().getImplicitCatalogName() != null ) {
- definitionBuilder.addParam(
- PersistentIdentifierGenerator.CATALOG,
- context.getMappingDefaults().getImplicitCatalogName()
- );
- }
-
if ( generatorAnn instanceof TableGenerator ) {
context.getBuildingOptions().getIdGenerationTypeInterpreter().interpretTableGenerator(
(TableGenerator) generatorAnn,
@@ -706,7 +692,7 @@ else if ( InheritanceType.JOINED.equals( inheritanceState.getType() ) ) {
SimpleValue key = new DependantValue( context, jsc.getTable(), jsc.getIdentifier() );
jsc.setKey( key );
ForeignKey fk = clazzToProcess.getAnnotation( ForeignKey.class );
- if ( fk != null && !BinderHelper.isEmptyAnnotationValue( fk.name() ) ) {
+ if ( fk != null && !isEmptyAnnotationValue( fk.name() ) ) {
key.setForeignKeyName( fk.name() );
}
else {
@@ -1386,7 +1372,7 @@ private static void bindTypeDef(TypeDef defAnn, MetadataBuildingContext context)
params.setProperty( param.name(), param.value() );
}
- if ( BinderHelper.isEmptyAnnotationValue( defAnn.name() ) && defAnn.defaultForType().equals( void.class ) ) {
+ if ( isEmptyAnnotationValue( defAnn.name() ) && defAnn.defaultForType().equals( void.class ) ) {
throw new AnnotationException(
"Either name or defaultForType (or both) attribute should be set in TypeDef having typeClass " +
defAnn.typeClass().getName()
@@ -1394,7 +1380,7 @@ private static void bindTypeDef(TypeDef defAnn, MetadataBuildingContext context)
}
final String typeBindMessageF = "Binding type definition: %s";
- if ( !BinderHelper.isEmptyAnnotationValue( defAnn.name() ) ) {
+ if ( !isEmptyAnnotationValue( defAnn.name() ) ) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( typeBindMessageF, defAnn.name() );
}
@@ -1809,10 +1795,12 @@ private static void processElementAnnotations(
);
}
+ final NotFound notFound = property.getAnnotation( NotFound.class );
+ final NotFoundAction notFoundAction = notFound == null ? null : notFound.action();
+ final boolean hasNotFound = notFoundAction != null;
+ checkFetchModeAgainstNotFound( propertyHolder.getEntityName(), property.getName(), hasNotFound, ann.fetch() );
+
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
- NotFound notFound = property.getAnnotation( NotFound.class );
- boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
- matchIgnoreNotFoundWithFetchType(propertyHolder.getEntityName(), property.getName(), ignoreNotFound, ann.fetch());
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
JoinTable assocTable = propertyHolder.getJoinTable( property );
@@ -1828,15 +1816,14 @@ private static void processElementAnnotations(
// is mandatory (even if the association has optional=true).
// If a @MapsId association has optional=true and is mapped with @NotFound(IGNORE) then
// the association is optional.
- final boolean mandatory =
- !ann.optional() ||
- property.isAnnotationPresent( Id.class ) ||
- ( property.isAnnotationPresent( MapsId.class ) && !ignoreNotFound );
+ final boolean mandatory = !ann.optional()
+ || property.isAnnotationPresent( Id.class )
+ || ( property.isAnnotationPresent( MapsId.class ) && !hasNotFound );
bindManyToOne(
getCascadeStrategy( ann.cascade(), hibernateCascade, false, forcePersist ),
joinColumns,
!mandatory,
- ignoreNotFound,
+ notFoundAction,
onDeleteCascade,
ToOneBinder.getTargetEntity( inferredData, context ),
propertyHolder,
@@ -1864,9 +1851,12 @@ else if ( property.isAnnotationPresent( OneToOne.class ) ) {
final boolean hasPkjc = property.isAnnotationPresent( PrimaryKeyJoinColumn.class )
|| property.isAnnotationPresent( PrimaryKeyJoinColumns.class );
boolean trueOneToOne = hasPkjc;
- Cascade hibernateCascade = property.getAnnotation( Cascade.class );
- NotFound notFound = property.getAnnotation( NotFound.class );
- boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
+ final Cascade hibernateCascade = property.getAnnotation( Cascade.class );
+ final NotFound notFound = property.getAnnotation( NotFound.class );
+ final NotFoundAction notFoundAction = notFound == null ? null : notFound.action();
+ final boolean hasNotFound = notFoundAction != null;
+ checkFetchModeAgainstNotFound( propertyHolder.getEntityName(), property.getName(), hasNotFound, ann.fetch() );
+
// MapsId means the columns belong to the pk;
// A @MapsId association (obviously) must be non-null when the entity is first persisted.
// If a @MapsId association is not mapped with @NotFound(IGNORE), then the association
@@ -1874,14 +1864,14 @@ else if ( property.isAnnotationPresent( OneToOne.class ) ) {
// If a @MapsId association has optional=true and is mapped with @NotFound(IGNORE) then
// the association is optional.
// @OneToOne(optional = true) with @PKJC makes the association optional.
- final boolean mandatory =
- !ann.optional() ||
- property.isAnnotationPresent( Id.class ) ||
- ( property.isAnnotationPresent( MapsId.class ) && !ignoreNotFound );
- matchIgnoreNotFoundWithFetchType(propertyHolder.getEntityName(), property.getName(), ignoreNotFound, ann.fetch());
- OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
- boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
- JoinTable assocTable = propertyHolder.getJoinTable( property );
+ final boolean mandatory = !ann.optional()
+ || property.isAnnotationPresent( Id.class )
+ || ( property.isAnnotationPresent( MapsId.class ) && !hasNotFound );
+ checkFetchModeAgainstNotFound( propertyHolder.getEntityName(), property.getName(), hasNotFound, ann.fetch() );
+
+ final OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
+ final boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
+ final JoinTable assocTable = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for ( Ejb3JoinColumn joinColumn : joinColumns ) {
@@ -1893,7 +1883,8 @@ else if ( property.isAnnotationPresent( OneToOne.class ) ) {
joinColumns,
!mandatory,
getFetchMode( ann.fetch() ),
- ignoreNotFound, onDeleteCascade,
+ notFoundAction,
+ onDeleteCascade,
ToOneBinder.getTargetEntity( inferredData, context ),
propertyHolder,
inferredData,
@@ -2587,13 +2578,13 @@ private static void bindJoinedTableAssociation(
if ( jpaIndexes != null && jpaIndexes.length > 0 ) {
associationTableBinder.setJpaIndex( jpaIndexes );
}
- if ( !BinderHelper.isEmptyAnnotationValue( schema ) ) {
+ if ( !isEmptyAnnotationValue( schema ) ) {
associationTableBinder.setSchema( schema );
}
- if ( !BinderHelper.isEmptyAnnotationValue( catalog ) ) {
+ if ( !isEmptyAnnotationValue( catalog ) ) {
associationTableBinder.setCatalog( catalog );
}
- if ( !BinderHelper.isEmptyAnnotationValue( tableName ) ) {
+ if ( !isEmptyAnnotationValue( tableName ) ) {
associationTableBinder.setName( tableName );
}
associationTableBinder.setUniqueConstraints( uniqueConstraints );
@@ -3055,7 +3046,7 @@ private static void bindManyToOne(
String cascadeStrategy,
Ejb3JoinColumn[] columns,
boolean optional,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
boolean cascadeOnDelete,
XClass targetEntity,
PropertyHolder propertyHolder,
@@ -3075,7 +3066,7 @@ private static void bindManyToOne(
final XProperty property = inferredData.getProperty();
defineFetchingStrategy( value, property );
//value.setFetchMode( fetchMode );
- value.setIgnoreNotFound( ignoreNotFound );
+ value.setNotFoundAction( notFoundAction );
value.setCascadeDeleteEnabled( cascadeOnDelete );
//value.setLazy( fetchMode != FetchMode.JOIN );
if ( !optional ) {
@@ -3105,7 +3096,7 @@ private static void bindManyToOne(
}
if ( property.isAnnotationPresent( ManyToOne.class ) && joinColumn != null
- && ! BinderHelper.isEmptyAnnotationValue( joinColumn.name() )
+ && ! isEmptyAnnotationValue( joinColumn.name() )
&& joinColumn.name().equals( columnName )
&& !property.isAnnotationPresent( MapsId.class ) ) {
hasSpecjManyToOne = true;
@@ -3168,11 +3159,24 @@ else if (hasSpecjManyToOne) {
propertyBinder.setXToMany( true );
final Property boundProperty = propertyBinder.makePropertyAndBind();
+ boundProperty.setOptional( optional && isNullable( joinColumns, joinColumn ) );
+ }
+
+ private static boolean isNullable(JoinColumns joinColumns, JoinColumn joinColumn) {
if ( joinColumn != null ) {
- boundProperty.setOptional( joinColumn.nullable() && optional );
+ return joinColumn.nullable();
+ }
+ else if ( joinColumns != null ) {
+ final JoinColumn[] col = joinColumns.value();
+ for ( int i = 0; i < col.length; i++ ) {
+ if ( joinColumns.value()[i].nullable() ) {
+ return true;
+ }
+ }
+ return false;
}
else {
- boundProperty.setOptional( optional );
+ return true;
}
}
@@ -3181,6 +3185,8 @@ protected static void defineFetchingStrategy(ToOne toOne, XProperty property) {
Fetch fetch = property.getAnnotation( Fetch.class );
ManyToOne manyToOne = property.getAnnotation( ManyToOne.class );
OneToOne oneToOne = property.getAnnotation( OneToOne.class );
+ NotFound notFound = property.getAnnotation( NotFound.class );
+
FetchType fetchType;
if ( manyToOne != null ) {
fetchType = manyToOne.fetch();
@@ -3193,7 +3199,12 @@ else if ( oneToOne != null ) {
"Define fetch strategy on a property not annotated with @OneToMany nor @OneToOne"
);
}
- if ( lazy != null ) {
+
+ if ( notFound != null ) {
+ toOne.setLazy( false );
+ toOne.setUnwrapProxy( true );
+ }
+ else if ( lazy != null ) {
toOne.setLazy( !( lazy.value() == LazyToOneOption.FALSE ) );
toOne.setUnwrapProxy( ( lazy.value() == LazyToOneOption.NO_PROXY ) );
}
@@ -3202,6 +3213,7 @@ else if ( oneToOne != null ) {
toOne.setUnwrapProxy( fetchType != FetchType.LAZY );
toOne.setUnwrapProxyImplicit( true );
}
+
if ( fetch != null ) {
if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {
toOne.setFetchMode( FetchMode.JOIN );
@@ -3228,7 +3240,7 @@ private static void bindOneToOne(
Ejb3JoinColumn[] joinColumns,
boolean optional,
FetchMode fetchMode,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
boolean cascadeOnDelete,
XClass targetEntity,
PropertyHolder propertyHolder,
@@ -3272,7 +3284,7 @@ private static void bindOneToOne(
}
}
}
- if ( trueOneToOne || mapToPK || !BinderHelper.isEmptyAnnotationValue( mappedBy ) ) {
+ if ( trueOneToOne || mapToPK || !isEmptyAnnotationValue( mappedBy ) ) {
//is a true one-to-one
//FIXME referencedColumnName ignored => ordering may fail.
OneToOneSecondPass secondPass = new OneToOneSecondPass(
@@ -3282,7 +3294,7 @@ private static void bindOneToOne(
propertyHolder,
inferredData,
targetEntity,
- ignoreNotFound,
+ notFoundAction,
cascadeOnDelete,
optional,
cascadeStrategy,
@@ -3293,19 +3305,25 @@ private static void bindOneToOne(
secondPass.doSecondPass( context.getMetadataCollector().getEntityBindingMap() );
}
else {
- context.getMetadataCollector().addSecondPass(
- secondPass,
- BinderHelper.isEmptyAnnotationValue( mappedBy )
- );
+ context.getMetadataCollector().addSecondPass( secondPass, isEmptyAnnotationValue( mappedBy ) );
}
}
else {
//has a FK on the table
bindManyToOne(
- cascadeStrategy, joinColumns, optional, ignoreNotFound, cascadeOnDelete,
+ cascadeStrategy,
+ joinColumns,
+ optional,
+ notFoundAction,
+ cascadeOnDelete,
targetEntity,
- propertyHolder, inferredData, true, isIdentifierMapper, inSecondPass,
- propertyBinder, context
+ propertyHolder,
+ inferredData,
+ true,
+ isIdentifierMapper,
+ inSecondPass,
+ propertyBinder,
+ context
);
}
}
@@ -3477,10 +3495,20 @@ public static void bindForeignKeyNameAndDefinition(
JoinColumns joinColumns,
MetadataBuildingContext context) {
final boolean noConstraintByDefault = context.getBuildingOptions().isNoConstraintByDefault();
- if ( ( joinColumn != null && ( joinColumn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
- || joinColumn.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) )
- || ( joinColumns != null && ( joinColumns.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
- || joinColumns.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) ) ) {
+
+ final NotFound notFoundAnn= property.getAnnotation( NotFound.class );
+ if ( notFoundAnn != null ) {
+ // supersedes all others
+ value.setForeignKeyName( "none" );
+ }
+ else if ( joinColumn != null && (
+ joinColumn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
+ || ( joinColumn.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) ) ) {
+ value.setForeignKeyName( "none" );
+ }
+ else if ( joinColumns != null && (
+ joinColumns.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
+ || ( joinColumns.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) ) ) {
value.setForeignKeyName( "none" );
}
else {
@@ -3639,12 +3667,12 @@ private static boolean hasAnnotationsOnIdClass(XClass idClass) {
return false;
}
- private static void matchIgnoreNotFoundWithFetchType(
+ private static void checkFetchModeAgainstNotFound(
String entity,
String association,
- boolean ignoreNotFound,
+ boolean hasNotFound,
FetchType fetchType) {
- if ( ignoreNotFound && fetchType == FetchType.LAZY ) {
+ if ( hasNotFound && fetchType == FetchType.LAZY ) {
LOG.ignoreNotFoundWithFetchTypeLazy( entity, association );
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java
index 4dd9882d5a17..b30c2180c54f 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java
@@ -525,15 +525,6 @@ public static void makeIdGenerator(
PersistentIdentifierGenerator.TABLE, table.getName()
);
- final String implicitCatalogName = buildingContext.getBuildingOptions().getMappingDefaults().getImplicitCatalogName();
- if ( implicitCatalogName != null ) {
- params.put( PersistentIdentifierGenerator.CATALOG, implicitCatalogName );
- }
- final String implicitSchemaName = buildingContext.getBuildingOptions().getMappingDefaults().getImplicitSchemaName();
- if ( implicitSchemaName != null ) {
- params.put( PersistentIdentifierGenerator.SCHEMA, implicitSchemaName );
- }
-
if ( id.getColumnSpan() == 1 ) {
params.setProperty(
PersistentIdentifierGenerator.PK,
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/CollectionSecondPass.java b/hibernate-core/src/main/java/org/hibernate/cfg/CollectionSecondPass.java
index 24256e2e37a0..4f4108fa9932 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/CollectionSecondPass.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/CollectionSecondPass.java
@@ -16,6 +16,7 @@
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.IndexedCollection;
import org.hibernate.mapping.OneToMany;
+import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Value;
@@ -68,8 +69,7 @@ public void doSecondPass(java.util.Map persistentClasses)
}
}
- abstract public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
- throws MappingException;
+ abstract public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas);
private static String columns(Value val) {
StringBuilder columns = new StringBuilder();
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java
index 77be05530038..f9d7107fffea 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java
@@ -253,7 +253,7 @@ private static Ejb3JoinColumn buildJoinColumn(
String suffixForDefaultColumnName,
MetadataBuildingContext buildingContext) {
if ( ann != null ) {
- if ( BinderHelper.isEmptyAnnotationValue( mappedBy ) ) {
+ if ( !BinderHelper.isEmptyOrNullAnnotationValue( mappedBy ) ) {
throw new AnnotationException(
"Illegal attempt to define a @JoinColumn with a mappedBy association: "
+ BinderHelper.getRelativePath( propertyHolder, propertyName )
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java b/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java
index 7a376ee894b4..6da83d1da963 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java
@@ -8,14 +8,13 @@
import java.util.Iterator;
import java.util.Map;
-
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import org.hibernate.AnnotationException;
-import org.hibernate.FetchMode;
import org.hibernate.MappingException;
import org.hibernate.annotations.LazyGroup;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.PropertyBinder;
@@ -41,7 +40,7 @@ public class OneToOneSecondPass implements SecondPass {
private String ownerEntity;
private String ownerProperty;
private PropertyHolder propertyHolder;
- private boolean ignoreNotFound;
+ private NotFoundAction notFoundAction;
private PropertyData inferredData;
private XClass targetEntity;
private boolean cascadeOnDelete;
@@ -57,7 +56,7 @@ public OneToOneSecondPass(
PropertyHolder propertyHolder,
PropertyData inferredData,
XClass targetEntity,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
boolean cascadeOnDelete,
boolean optional,
String cascadeStrategy,
@@ -68,7 +67,7 @@ public OneToOneSecondPass(
this.mappedBy = mappedBy;
this.propertyHolder = propertyHolder;
this.buildingContext = buildingContext;
- this.ignoreNotFound = ignoreNotFound;
+ this.notFoundAction = notFoundAction;
this.inferredData = inferredData;
this.targetEntity = targetEntity;
this.cascadeOnDelete = cascadeOnDelete;
@@ -109,6 +108,7 @@ public void doSecondPass(Map persistentClasses) throws MappingException {
PropertyBinder binder = new PropertyBinder();
binder.setName( propertyName );
+ binder.setProperty( inferredData.getProperty() );
binder.setValue( value );
binder.setCascade( cascadeStrategy );
binder.setAccessType( inferredData.getDefaultAccess() );
@@ -191,7 +191,7 @@ else if ( otherSideProperty.getValue() instanceof ManyToOne ) {
);
ManyToOne manyToOne = new ManyToOne( buildingContext, mappedByJoin.getTable() );
//FIXME use ignore not found here
- manyToOne.setIgnoreNotFound( ignoreNotFound );
+ manyToOne.setNotFoundAction( notFoundAction );
manyToOne.setCascadeDeleteEnabled( value.isCascadeDeleteEnabled() );
manyToOne.setFetchMode( value.getFetchMode() );
manyToOne.setLazy( value.isLazy() );
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java b/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java
index 244835656fce..ad9c46ad38e9 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java
@@ -50,8 +50,8 @@ public Settings(SessionFactoryOptions sessionFactoryOptions) {
public Settings(SessionFactoryOptions sessionFactoryOptions, Metadata metadata) {
this(
sessionFactoryOptions,
- extractName( metadata.getDatabase().getDefaultNamespace().getName().getCatalog() ),
- extractName( metadata.getDatabase().getDefaultNamespace().getName().getSchema() )
+ extractName( metadata.getDatabase().getPhysicalImplicitNamespaceName().getCatalog() ),
+ extractName( metadata.getDatabase().getPhysicalImplicitNamespaceName().getSchema() )
);
}
@@ -61,8 +61,8 @@ private static String extractName(Identifier identifier) {
public Settings(SessionFactoryOptions sessionFactoryOptions, String defaultCatalogName, String defaultSchemaName) {
this.sessionFactoryOptions = sessionFactoryOptions;
- this.defaultCatalogName = defaultCatalogName;
- this.defaultSchemaName = defaultSchemaName;
+ this.defaultCatalogName = sessionFactoryOptions.getDefaultCatalog() != null ? sessionFactoryOptions.getDefaultCatalog() : defaultCatalogName;
+ this.defaultSchemaName = sessionFactoryOptions.getDefaultSchema() != null ? sessionFactoryOptions.getDefaultSchema() : defaultSchemaName;
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "SessionFactory name : %s", sessionFactoryOptions.getSessionFactoryName() );
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java b/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java
index b04c27d16e18..f79c8df5dfe6 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java
@@ -106,7 +106,9 @@ public void doSecondPass(java.util.Map persistentClasses) throws MappingExceptio
/*
* HbmMetadataSourceProcessorImpl does this only when property-ref != null, but IMO, it makes sense event if it is null
*/
- if ( !manyToOne.isIgnoreNotFound() ) manyToOne.createPropertyRefConstraints( persistentClasses );
+ if ( manyToOne.getNotFoundAction() == null ) {
+ manyToOne.createPropertyRefConstraints( persistentClasses );
+ }
}
else if ( value instanceof OneToOne ) {
value.createForeignKey();
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java
index 9570c1df3d79..23a141065762 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java
@@ -50,6 +50,8 @@
import org.hibernate.annotations.LazyGroup;
import org.hibernate.annotations.Loader;
import org.hibernate.annotations.ManyToAny;
+import org.hibernate.annotations.NotFound;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.OptimisticLock;
@@ -156,7 +158,7 @@ public abstract class CollectionBinder {
private Ejb3Column[] elementColumns;
private boolean isEmbedded;
private XProperty property;
- private boolean ignoreNotFound;
+ private NotFoundAction notFoundAction;
private TableBinder tableBinder;
private Ejb3Column[] mapKeyColumns;
private Ejb3JoinColumn[] mapKeyManyToManyColumns;
@@ -572,7 +574,7 @@ public void bind() {
isEmbedded,
property,
collectionType,
- ignoreNotFound,
+ notFoundAction,
oneToMany,
tableBinder,
buildingContext
@@ -714,6 +716,8 @@ private void defineFetchingStrategy() {
ManyToMany manyToMany = property.getAnnotation( ManyToMany.class );
ElementCollection elementCollection = property.getAnnotation( ElementCollection.class );
ManyToAny manyToAny = property.getAnnotation( ManyToAny.class );
+ NotFound notFound = property.getAnnotation( NotFound.class );
+
FetchType fetchType;
if ( oneToMany != null ) {
fetchType = oneToMany.fetch();
@@ -732,34 +736,57 @@ else if ( manyToAny != null ) {
"Define fetch strategy on a property not annotated with @ManyToOne nor @OneToMany nor @CollectionOfElements"
);
}
- if ( lazy != null ) {
- collection.setLazy( !( lazy.value() == LazyCollectionOption.FALSE ) );
- collection.setExtraLazy( lazy.value() == LazyCollectionOption.EXTRA );
+
+ if ( notFound != null ) {
+ collection.setLazy( false );
+
+ if ( lazy != null ) {
+ collection.setExtraLazy( lazy.value() == LazyCollectionOption.EXTRA );
+ }
+
+ if ( fetch != null ) {
+ if ( fetch.value() != null ) {
+ collection.setFetchMode( fetch.value().getHibernateFetchMode() );
+ if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
+ collection.setSubselectLoadable( true );
+ collection.getOwner().setSubselectLoadableCollections( true );
+ }
+ }
+ }
+ else {
+ collection.setFetchMode( AnnotationBinder.getFetchMode( fetchType ) );
+ }
}
else {
- collection.setLazy( fetchType == FetchType.LAZY );
- collection.setExtraLazy( false );
- }
- if ( fetch != null ) {
- if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {
- collection.setFetchMode( FetchMode.JOIN );
- collection.setLazy( false );
+ if ( lazy != null ) {
+ collection.setLazy( !( lazy.value() == LazyCollectionOption.FALSE ) );
+ collection.setExtraLazy( lazy.value() == LazyCollectionOption.EXTRA );
}
- else if ( fetch.value() == org.hibernate.annotations.FetchMode.SELECT ) {
- collection.setFetchMode( FetchMode.SELECT );
+ else {
+ collection.setLazy( fetchType == FetchType.LAZY );
+ collection.setExtraLazy( false );
}
- else if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
- collection.setFetchMode( FetchMode.SELECT );
- collection.setSubselectLoadable( true );
- collection.getOwner().setSubselectLoadableCollections( true );
+ if ( fetch != null ) {
+ if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {
+ collection.setFetchMode( FetchMode.JOIN );
+ collection.setLazy( false );
+ }
+ else if ( fetch.value() == org.hibernate.annotations.FetchMode.SELECT ) {
+ collection.setFetchMode( FetchMode.SELECT );
+ }
+ else if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
+ collection.setFetchMode( FetchMode.SELECT );
+ collection.setSubselectLoadable( true );
+ collection.getOwner().setSubselectLoadableCollections( true );
+ }
+ else {
+ throw new AssertionFailure( "Unknown FetchMode: " + fetch.value() );
+ }
}
else {
- throw new AssertionFailure( "Unknown FetchMode: " + fetch.value() );
+ collection.setFetchMode( AnnotationBinder.getFetchMode( fetchType ) );
}
}
- else {
- collection.setFetchMode( AnnotationBinder.getFetchMode( fetchType ) );
- }
}
private XClass getCollectionType() {
@@ -788,14 +815,14 @@ public SecondPass getSecondPass(
final boolean isEmbedded,
final XProperty property,
final XClass collType,
- final boolean ignoreNotFound,
+ final NotFoundAction notFoundAction,
final boolean unique,
final TableBinder assocTableBinder,
final MetadataBuildingContext buildingContext) {
return new CollectionSecondPass( buildingContext, collection ) {
@SuppressWarnings("rawtypes")
@Override
- public void secondPass(Map persistentClasses, Map inheritedMetas) throws MappingException {
+ public void secondPass(Map persistentClasses, Map inheritedMetas) {
bindStarToManySecondPass(
persistentClasses,
collType,
@@ -807,7 +834,7 @@ public void secondPass(Map persistentClasses, Map inheritedMetas) throws Mapping
property,
unique,
assocTableBinder,
- ignoreNotFound,
+ notFoundAction,
buildingContext
);
}
@@ -828,7 +855,7 @@ protected boolean bindStarToManySecondPass(
XProperty property,
boolean unique,
TableBinder associationTableBinder,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
MetadataBuildingContext buildingContext) {
PersistentClass persistentClass = persistentClasses.get( collType.getName() );
boolean reversePropertyInJoin = false;
@@ -863,7 +890,7 @@ protected boolean bindStarToManySecondPass(
fkJoinColumns,
collType,
cascadeDeleteEnabled,
- ignoreNotFound,
+ notFoundAction,
buildingContext,
inheritanceStatePerClass
);
@@ -878,7 +905,7 @@ protected boolean bindStarToManySecondPass(
inverseColumns,
elementColumns,
isEmbedded, collType,
- ignoreNotFound, unique,
+ notFoundAction, unique,
cascadeDeleteEnabled,
associationTableBinder,
property,
@@ -895,7 +922,7 @@ protected void bindOneToManySecondPass(
Ejb3JoinColumn[] fkJoinColumns,
XClass collectionType,
boolean cascadeDeleteEnabled,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
MetadataBuildingContext buildingContext,
Map inheritanceStatePerClass) {
@@ -910,7 +937,7 @@ protected void bindOneToManySecondPass(
org.hibernate.mapping.OneToMany oneToMany = new org.hibernate.mapping.OneToMany( buildingContext, collection.getOwner() );
collection.setElement( oneToMany );
oneToMany.setReferencedEntityName( collectionType.getName() );
- oneToMany.setIgnoreNotFound( ignoreNotFound );
+ oneToMany.setNotFoundAction( notFoundAction );
String assocClass = oneToMany.getReferencedEntityName();
PersistentClass associatedClass = persistentClasses.get( assocClass );
@@ -1314,7 +1341,7 @@ private void bindManyToManySecondPass(
Ejb3Column[] elementColumns,
boolean isEmbedded,
XClass collType,
- boolean ignoreNotFound, boolean unique,
+ NotFoundAction notFoundAction, boolean unique,
boolean cascadeDeleteEnabled,
TableBinder associationTableBinder,
XProperty property,
@@ -1457,7 +1484,7 @@ else if ( anyAnn != null ) {
//make the second join non lazy
element.setFetchMode( FetchMode.JOIN );
element.setLazy( false );
- element.setIgnoreNotFound( ignoreNotFound );
+ element.setNotFoundAction( notFoundAction );
// as per 11.1.38 of JPA 2.0 spec, default to primary key if no column is specified by @OrderBy.
if ( hqlOrderBy != null ) {
collValue.setManyToManyOrdering(
@@ -1824,8 +1851,21 @@ public void setProperty(XProperty property) {
this.property = property;
}
+ public NotFoundAction getNotFoundAction() {
+ return notFoundAction;
+ }
+
+ public void setNotFoundAction(NotFoundAction notFoundAction) {
+ this.notFoundAction = notFoundAction;
+ }
+
public void setIgnoreNotFound(boolean ignoreNotFound) {
- this.ignoreNotFound = ignoreNotFound;
+ if ( ignoreNotFound ) {
+ setNotFoundAction( NotFoundAction.IGNORE );
+ }
+ else {
+ setNotFoundAction( null );
+ }
}
public void setMapKeyColumns(Ejb3Column[] mapKeyColumns) {
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/IdBagBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/IdBagBinder.java
index 34ef384638e7..9bfb2b62f44d 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/IdBagBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/IdBagBinder.java
@@ -13,6 +13,7 @@
import org.hibernate.AnnotationException;
import org.hibernate.annotations.CollectionId;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
@@ -52,11 +53,11 @@ protected boolean bindStarToManySecondPass(
XProperty property,
boolean unique,
TableBinder associationTableBinder,
- boolean ignoreNotFound,
+ NotFoundAction notFoundAction,
MetadataBuildingContext buildingContext) {
boolean result = super.bindStarToManySecondPass(
persistentClasses, collType, fkJoinColumns, keyColumns, inverseColumns, elementColumns, isEmbedded,
- property, unique, associationTableBinder, ignoreNotFound, getBuildingContext()
+ property, unique, associationTableBinder, notFoundAction, getBuildingContext()
);
CollectionId collectionIdAnn = property.getAnnotation( CollectionId.class );
if ( collectionIdAnn != null ) {
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/ListBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/ListBinder.java
index 69aa6c597699..5f09945873d8 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/ListBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/ListBinder.java
@@ -10,6 +10,7 @@
import org.hibernate.AnnotationException;
import org.hibernate.MappingException;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OrderBy;
import org.hibernate.annotations.Sort;
import org.hibernate.annotations.common.reflection.XClass;
@@ -76,14 +77,13 @@ public SecondPass getSecondPass(
final boolean isEmbedded,
final XProperty property,
final XClass collType,
- final boolean ignoreNotFound,
+ final NotFoundAction notFoundAction,
final boolean unique,
final TableBinder assocTableBinder,
final MetadataBuildingContext buildingContext) {
return new CollectionSecondPass( getBuildingContext(), ListBinder.this.collection ) {
@Override
- public void secondPass(Map persistentClasses, Map inheritedMetas)
- throws MappingException {
+ public void secondPass(Map persistentClasses, Map inheritedMetas) {
bindStarToManySecondPass(
persistentClasses,
collType,
@@ -95,7 +95,7 @@ public void secondPass(Map persistentClasses, Map inheritedMetas)
property,
unique,
assocTableBinder,
- ignoreNotFound,
+ notFoundAction,
buildingContext
);
bindIndex( buildingContext );
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java
index 4a4f83fe1c97..2d9920dacbf7 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java
@@ -23,13 +23,17 @@
import org.hibernate.AssertionFailure;
import org.hibernate.FetchMode;
import org.hibernate.MappingException;
+import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
+import org.hibernate.boot.model.relational.SqlStringGenerationContext;
+import org.hibernate.boot.model.relational.internal.SqlStringGenerationContextImpl;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.AccessType;
import org.hibernate.cfg.AnnotatedClassType;
import org.hibernate.cfg.AnnotationBinder;
+import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.BinderHelper;
import org.hibernate.cfg.CollectionPropertyHolder;
import org.hibernate.cfg.CollectionSecondPass;
@@ -41,6 +45,8 @@
import org.hibernate.cfg.PropertyPreloadedData;
import org.hibernate.cfg.SecondPass;
import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.engine.config.spi.ConfigurationService;
+import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
@@ -56,6 +62,7 @@
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
+import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.Template;
/**
@@ -87,16 +94,15 @@ public SecondPass getSecondPass(
final boolean isEmbedded,
final XProperty property,
final XClass collType,
- final boolean ignoreNotFound,
+ final NotFoundAction notFoundAction,
final boolean unique,
final TableBinder assocTableBinder,
final MetadataBuildingContext buildingContext) {
return new CollectionSecondPass( buildingContext, MapBinder.this.collection ) {
- public void secondPass(Map persistentClasses, Map inheritedMetas)
- throws MappingException {
+ public void secondPass(Map persistentClasses, Map inheritedMetas) {
bindStarToManySecondPass(
persistentClasses, collType, fkJoinColumns, keyColumns, inverseColumns, elementColumns,
- isEmbedded, property, unique, assocTableBinder, ignoreNotFound, buildingContext
+ isEmbedded, property, unique, assocTableBinder, notFoundAction, buildingContext
);
bindKeyFromAssociationTable(
collType, persistentClasses, mapKeyPropertyName, property, isEmbedded, buildingContext,
@@ -412,6 +418,14 @@ protected Value createFormulatedValue(
MetadataBuildingContext buildingContext) {
Value element = collection.getElement();
String fromAndWhere = null;
+ final ServiceRegistry serviceRegistry = buildingContext.getBootstrapContext().getServiceRegistry();
+ final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class);
+ final SqlStringGenerationContext generationContext = SqlStringGenerationContextImpl.fromExplicit(
+ serviceRegistry.getService( JdbcServices.class).getJdbcEnvironment(),
+ buildingContext.getMetadataCollector().getDatabase(),
+ configurationService.getSetting(AvailableSettings.DEFAULT_CATALOG, String.class, null),
+ configurationService.getSetting( AvailableSettings.DEFAULT_SCHEMA, String.class, null)
+ );
if ( !( element instanceof OneToMany ) ) {
String referencedPropertyName = null;
if ( element instanceof ToOne ) {
@@ -435,7 +449,7 @@ else if ( element instanceof DependantValue ) {
referencedEntityColumns = referencedProperty.getColumnIterator();
}
fromAndWhere = getFromAndWhereFormula(
- associatedClass.getTable().getQualifiedTableName().toString(),
+ generationContext.format(associatedClass.getTable().getQualifiedTableName()),
element.getColumnIterator(),
referencedEntityColumns
);
@@ -444,9 +458,7 @@ else if ( element instanceof DependantValue ) {
// HHH-11005 - only if we are OneToMany and location of map key property is at a different level, need to add a select
if ( !associatedClass.equals( targetPropertyPersistentClass ) ) {
fromAndWhere = getFromAndWhereFormula(
- targetPropertyPersistentClass.getTable()
- .getQualifiedTableName()
- .toString(),
+ generationContext.format(targetPropertyPersistentClass.getTable().getQualifiedTableName()),
element.getColumnIterator(),
associatedClass.getIdentifier().getColumnIterator()
);
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java
index d3986b61cc92..7cb33fd33347 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java
@@ -273,7 +273,9 @@ public Property makeProperty() {
prop.setPropertyAccessorName( accessType.getType() );
if ( property != null ) {
- prop.setValueGenerationStrategy( determineValueGenerationStrategy( property ) );
+ if ( entityBinder != null ) {
+ prop.setValueGenerationStrategy( determineValueGenerationStrategy( property ) );
+ }
if ( property.isAnnotationPresent( AttributeAccessor.class ) ) {
final AttributeAccessor accessor = property.getAnnotation( AttributeAccessor.class );
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java
index 2e5995f45647..7b9f7c5eff7b 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java
@@ -477,10 +477,10 @@ public static Table buildAndFillTable(
String subselect,
InFlightMetadataCollector.EntityTableXref denormalizedSuperTableXref) {
schema = BinderHelper.isEmptyOrNullAnnotationValue( schema )
- ? extract( buildingContext.getMetadataCollector().getDatabase().getDefaultNamespace().getPhysicalName().getSchema() )
+ ? null
: schema;
catalog = BinderHelper.isEmptyOrNullAnnotationValue( catalog )
- ? extract( buildingContext.getMetadataCollector().getDatabase().getDefaultNamespace().getPhysicalName().getCatalog() )
+ ? null
: catalog;
final Table table;
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java
index 8a8c3167522c..7f4553145901 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java
@@ -179,7 +179,6 @@
import org.hibernate.boot.jaxb.mapping.spi.JaxbStoredProcedureParameter;
import org.hibernate.boot.jaxb.mapping.spi.JaxbTable;
import org.hibernate.boot.jaxb.mapping.spi.JaxbTableGenerator;
-import org.hibernate.boot.jaxb.mapping.spi.JaxbTransient;
import org.hibernate.boot.jaxb.mapping.spi.JaxbUniqueConstraint;
import org.hibernate.boot.jaxb.mapping.spi.JaxbVersion;
import org.hibernate.boot.jaxb.mapping.spi.LifecycleCallbackContainer;
@@ -407,7 +406,9 @@ public Annotation[] getAnnotations() {
*/
private void initAnnotations() {
if ( annotations == null ) {
- XMLContext.Default defaults = xmlContext.getDefault( className );
+ // We don't want the global catalog and schema here: they are applied much later,
+ // when SQL gets rendered.
+ XMLContext.Default defaults = xmlContext.getDefaultWithoutGlobalCatalogAndSchema( className );
if ( className != null && propertyName == null ) {
//is a class
ManagedType managedTypeOverride = xmlContext.getManagedTypeOverride( className );
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenMetadataProvider.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenMetadataProvider.java
index fef5c77f0b3f..04733894cc78 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenMetadataProvider.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenMetadataProvider.java
@@ -36,7 +36,7 @@
* @author Emmanuel Bernard
*/
@SuppressWarnings("unchecked")
-public final class JPAXMLOverriddenMetadataProvider implements MetadataProvider {
+public class JPAXMLOverriddenMetadataProvider implements MetadataProvider {
private static final MetadataProvider STATELESS_BASE_DELEGATE = new JavaMetadataProvider();
@@ -94,7 +94,7 @@ public Map
*/
-final class PropertyMappingElementCollector {
- static final Function PERSISTENT_ATTRIBUTE_NAME = PersistentAttribute::getName;
- static final Function JAXB_TRANSIENT_NAME = JaxbTransient::getName;
+public final class PropertyMappingElementCollector {
+ public static final Function PERSISTENT_ATTRIBUTE_NAME = PersistentAttribute::getName;
+ public static final Function JAXB_TRANSIENT_NAME = JaxbTransient::getName;
static final Function LIFECYCLE_CALLBACK_NAME = LifecycleCallback::getMethodName;
private final String propertyName;
@@ -70,7 +70,7 @@ final class PropertyMappingElementCollector {
private List postUpdate;
private List postLoad;
- PropertyMappingElementCollector(String propertyName) {
+ public PropertyMappingElementCollector(String propertyName) {
this.propertyName = propertyName;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/XMLContext.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/XMLContext.java
index 06f1da27f913..c43223900ead 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/XMLContext.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/XMLContext.java
@@ -124,12 +124,12 @@ private void addClass(List extends ManagedType> managedTypes, String packageNa
managedTypeOverride.put( className, element );
Default mergedDefaults = new Default();
// Apply entity mapping defaults
- mergedDefaults.override( defaults );
+ mergedDefaults.overrideWithCatalogAndSchema( defaults );
// ... then apply entity settings
Default fileDefaults = new Default();
fileDefaults.setMetadataComplete( element.isMetadataComplete() );
fileDefaults.setAccess( element.getAccess() );
- mergedDefaults.override( fileDefaults );
+ mergedDefaults.overrideWithCatalogAndSchema( fileDefaults );
// ... and we get the merged defaults for that entity
defaultsOverride.put( className, mergedDefaults );
@@ -196,16 +196,22 @@ public static String buildSafeClassName(String className, Default defaults) {
return buildSafeClassName( className, defaults.getPackageName() );
}
- public Default getDefault(String className) {
+ public Default getDefaultWithoutGlobalCatalogAndSchema(String className) {
Default xmlDefault = new Default();
- xmlDefault.override( globalDefaults );
+ xmlDefault.overrideWithoutCatalogAndSchema( globalDefaults );
if ( className != null ) {
Default entityMappingOverriding = defaultsOverride.get( className );
- xmlDefault.override( entityMappingOverriding );
+ xmlDefault.overrideWithCatalogAndSchema( entityMappingOverriding );
}
return xmlDefault;
}
+ public Default getDefaultWithGlobalCatalogAndSchema() {
+ Default xmlDefault = new Default();
+ xmlDefault.overrideWithCatalogAndSchema( globalDefaults );
+ return xmlDefault;
+ }
+
public ManagedType getManagedTypeOverride(String className) {
return managedTypeOverride.get( className );
}
@@ -292,7 +298,19 @@ void setCascadePersist(Boolean cascadePersist) {
this.cascadePersist = cascadePersist;
}
- public void override(Default globalDefault) {
+ public void overrideWithCatalogAndSchema(Default override) {
+ overrideWithoutCatalogAndSchema( override );
+ if ( override != null ) {
+ if ( override.getSchema() != null ) {
+ schema = override.getSchema();
+ }
+ if ( override.getCatalog() != null ) {
+ catalog = override.getCatalog();
+ }
+ }
+ }
+
+ public void overrideWithoutCatalogAndSchema(Default globalDefault) {
if ( globalDefault != null ) {
if ( globalDefault.getAccess() != null ) {
access = globalDefault.getAccess();
@@ -300,12 +318,6 @@ public void override(Default globalDefault) {
if ( globalDefault.getPackageName() != null ) {
packageName = globalDefault.getPackageName();
}
- if ( globalDefault.getSchema() != null ) {
- schema = globalDefault.getSchema();
- }
- if ( globalDefault.getCatalog() != null ) {
- catalog = globalDefault.getCatalog();
- }
if ( globalDefault.getDelimitedIdentifier() != null ) {
delimitedIdentifier = globalDefault.getDelimitedIdentifier();
}
diff --git a/hibernate-core/src/main/java/org/hibernate/collection/spi/LazyInitializable.java b/hibernate-core/src/main/java/org/hibernate/collection/spi/LazyInitializable.java
new file mode 100644
index 000000000000..38c8345181ec
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/collection/spi/LazyInitializable.java
@@ -0,0 +1,33 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
+ * See the lgpl.txt file in the root directory or .
+ */
+package org.hibernate.collection.spi;
+
+import org.hibernate.Incubating;
+
+/**
+ * Hibernate "wraps" a java collection in an instance of PersistentCollection. Envers uses custom collection
+ * wrappers (ListProxy, SetProxy, etc). All of them need to extend LazyInitializable, so the
+ * Hibernate.isInitialized method can check if the collection is initialized or not.
+ *
+ * @author Fabricio Gregorio
+ */
+@Incubating
+public interface LazyInitializable {
+
+ /**
+ * Is this instance initialized?
+ *
+ * @return Was this collection initialized? Or is its data still not (fully) loaded?
+ */
+ boolean wasInitialized();
+
+ /**
+ * To be called internally by the session, forcing immediate initialization.
+ */
+ void forceInitialization();
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java b/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java
index 0a98641c38b7..8fa8817d0d60 100644
--- a/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java
+++ b/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java
@@ -45,7 +45,7 @@
*
* @author Gavin King
*/
-public interface PersistentCollection {
+public interface PersistentCollection extends LazyInitializable {
/**
* Get the owning entity. Note that the owner is only
* set during the flush cycle, and when a new collection
@@ -271,11 +271,6 @@ Object readFrom(ResultSet rs, CollectionPersister role, CollectionAliases descri
*/
Serializable getSnapshot(CollectionPersister persister);
- /**
- * To be called internally by the session, forcing immediate initialization.
- */
- void forceInitialization();
-
/**
* Does the given element/entry exist in the collection?
*
@@ -335,13 +330,6 @@ Object readFrom(ResultSet rs, CollectionPersister role, CollectionAliases descri
*/
boolean isWrapper(Object collection);
- /**
- * Is this instance initialized?
- *
- * @return Was this collection initialized? Or is its data still not (fully) loaded?
- */
- boolean wasInitialized();
-
/**
* Does this instance have any "queued" operations?
*
diff --git a/hibernate-core/src/main/java/org/hibernate/criterion/CriteriaQuery.java b/hibernate-core/src/main/java/org/hibernate/criterion/CriteriaQuery.java
index 3dbebe2e9db4..d5b672687419 100755
--- a/hibernate-core/src/main/java/org/hibernate/criterion/CriteriaQuery.java
+++ b/hibernate-core/src/main/java/org/hibernate/criterion/CriteriaQuery.java
@@ -8,6 +8,7 @@
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
+import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.type.Type;
@@ -200,4 +201,16 @@ public interface CriteriaQuery {
* @return The generated alias
*/
public String generateSQLAlias();
+
+ default Type getForeignKeyType(Criteria criteria, String associationPropertyName){
+ throw new NotYetImplementedException("CriteriaQuery#getForeignKeyType() has not been yet implemented!");
+ }
+
+ default String[] getForeignKeyColumns(Criteria criteria, String associationPropertyName){
+ throw new NotYetImplementedException("CriteriaQuery#getForeignKeyColumns() has not been yet implemented!");
+ }
+
+ default TypedValue getForeignKeyTypeValue(Criteria criteria, String associationPropertyName, Object value){
+ throw new NotYetImplementedException("CriteriaQuery#getForeignKeyTypeValue() has not been yet implemented!");
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/criterion/ForeignKeyExpression.java b/hibernate-core/src/main/java/org/hibernate/criterion/ForeignKeyExpression.java
new file mode 100644
index 000000000000..8fc343e69d66
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/criterion/ForeignKeyExpression.java
@@ -0,0 +1,40 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
+ * See the lgpl.txt file in the root directory or .
+ */
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.engine.spi.TypedValue;
+import org.hibernate.internal.util.StringHelper;
+
+public class ForeignKeyExpression implements Criterion {
+ private final String associationPropertyName;
+ private final Object value;
+ private final String operator;
+
+ public ForeignKeyExpression(String associationPropertyName, Object value, String operator) {
+ this.associationPropertyName = associationPropertyName;
+ this.value = value;
+ this.operator = operator;
+ }
+
+ @Override
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) {
+ final String[] columns = criteriaQuery.getForeignKeyColumns( criteria, associationPropertyName );
+
+ String result = String.join( " and ", StringHelper.suffix( columns, operator + " ?" ) );
+ if ( columns.length > 1 ) {
+ result = '(' + result + ')';
+ }
+ return result;
+ }
+
+ @Override
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) {
+ return new TypedValue[] { criteriaQuery.getForeignKeyTypeValue( criteria, associationPropertyName, value ) };
+ }
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/criterion/ForeignKeyNullExpression.java b/hibernate-core/src/main/java/org/hibernate/criterion/ForeignKeyNullExpression.java
new file mode 100644
index 000000000000..fa81e944f369
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/criterion/ForeignKeyNullExpression.java
@@ -0,0 +1,52 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
+ * See the lgpl.txt file in the root directory or .
+ */
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.engine.spi.TypedValue;
+import org.hibernate.internal.util.StringHelper;
+
+public class ForeignKeyNullExpression implements Criterion {
+ private static final TypedValue[] NO_VALUES = new TypedValue[0];
+
+ private final String associationPropertyName;
+ private final boolean negated;
+
+ public ForeignKeyNullExpression(String associationPropertyName) {
+ this.associationPropertyName = associationPropertyName;
+ this.negated = false;
+ }
+
+ public ForeignKeyNullExpression(String associationPropertyName, boolean negated) {
+ this.associationPropertyName = associationPropertyName;
+ this.negated = negated;
+ }
+
+ @Override
+ public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) {
+ final String[] columns = criteriaQuery.getForeignKeyColumns( criteria, associationPropertyName );
+
+ String result = String.join( " and ", StringHelper.suffix( columns, getSuffix() ) );
+ if ( columns.length > 1 ) {
+ result = '(' + result + ')';
+ }
+ return result;
+ }
+
+ private String getSuffix() {
+ if ( negated ) {
+ return " is not null";
+ }
+ return " is null";
+ }
+
+ @Override
+ public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) {
+ return NO_VALUES;
+ }
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/criterion/ForeingKeyProjection.java b/hibernate-core/src/main/java/org/hibernate/criterion/ForeingKeyProjection.java
new file mode 100644
index 000000000000..6df5fb1cc749
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/criterion/ForeingKeyProjection.java
@@ -0,0 +1,55 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
+ * See the lgpl.txt file in the root directory or .
+ */
+package org.hibernate.criterion;
+
+import org.hibernate.Criteria;
+import org.hibernate.type.Type;
+
+public class ForeingKeyProjection extends SimpleProjection {
+ private String associationPropertyName;
+
+ protected ForeingKeyProjection(String associationPropertyName) {
+ this.associationPropertyName = associationPropertyName;
+ }
+
+ @Override
+ public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery) {
+ return new Type[] { criteriaQuery.getForeignKeyType( criteria, associationPropertyName ) };
+ }
+
+ @Override
+ public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery) {
+ final StringBuilder buf = new StringBuilder();
+ final String[] cols = criteriaQuery.getForeignKeyColumns( criteria, associationPropertyName );
+ for ( int i = 0; i < cols.length; i++ ) {
+ buf.append( cols[i] )
+ .append( " as y" )
+ .append( position + i )
+ .append( '_' );
+ if ( i < cols.length - 1 ) {
+ buf.append( ", " );
+ }
+ }
+ return buf.toString();
+ }
+
+ @Override
+ public boolean isGrouped() {
+ return false;
+ }
+
+ @Override
+ public String toGroupSqlString(Criteria criteria, CriteriaQuery criteriaQuery) {
+ return super.toGroupSqlString( criteria, criteriaQuery );
+ }
+
+ @Override
+ public String toString() {
+ return "fk";
+ }
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/criterion/LikeExpression.java b/hibernate-core/src/main/java/org/hibernate/criterion/LikeExpression.java
index e266a70d784d..b846cacc80d6 100644
--- a/hibernate-core/src/main/java/org/hibernate/criterion/LikeExpression.java
+++ b/hibernate-core/src/main/java/org/hibernate/criterion/LikeExpression.java
@@ -78,7 +78,7 @@ public String toSqlString(Criteria criteria,CriteriaQuery criteriaQuery) {
@Override
public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) {
- final String matchValue = ignoreCase ? value.toString().toLowerCase(Locale.ROOT) : value.toString();
+ final String matchValue = ignoreCase ? value.toString().toLowerCase() : value.toString();
return new TypedValue[] { criteriaQuery.getTypedValue( criteria, propertyName, matchValue ) };
}
diff --git a/hibernate-core/src/main/java/org/hibernate/criterion/Projections.java b/hibernate-core/src/main/java/org/hibernate/criterion/Projections.java
index e2826eb7930d..dbb310622d75 100755
--- a/hibernate-core/src/main/java/org/hibernate/criterion/Projections.java
+++ b/hibernate-core/src/main/java/org/hibernate/criterion/Projections.java
@@ -61,6 +61,16 @@ public static IdentifierProjection id() {
return new IdentifierProjection();
}
+ /*
+ * An foreign key value projection.
+ *
+ * @return The foreign key projection
+ *
+ */
+ public static ForeingKeyProjection fk(String associationPropertyName) {
+ return new ForeingKeyProjection(associationPropertyName);
+ }
+
/**
* Create a distinct projection from a projection.
*
diff --git a/hibernate-core/src/main/java/org/hibernate/criterion/Restrictions.java b/hibernate-core/src/main/java/org/hibernate/criterion/Restrictions.java
index 548ed9e09b51..64993d4ad3c9 100755
--- a/hibernate-core/src/main/java/org/hibernate/criterion/Restrictions.java
+++ b/hibernate-core/src/main/java/org/hibernate/criterion/Restrictions.java
@@ -36,6 +36,22 @@ public class Restrictions {
public static Criterion idEq(Object value) {
return new IdentifierEqExpression( value );
}
+
+ public static Criterion fkEq(String associationPropertyName, Object value) {
+ return new ForeignKeyExpression( associationPropertyName, value, "=" );
+ }
+
+ public static Criterion fkNe(String associationPropertyName, Object value) {
+ return new ForeignKeyExpression( associationPropertyName, value, "<>" );
+ }
+
+ public static Criterion fkIsNotNull(String associationPropertyName) {
+ return new ForeignKeyNullExpression( associationPropertyName, true);
+ }
+
+ public static Criterion fkIsNull(String associationPropertyName) {
+ return new ForeignKeyNullExpression( associationPropertyName );
+ }
/**
* Apply an "equal" constraint to the named property
*
diff --git a/hibernate-core/src/main/java/org/hibernate/criterion/SimpleExpression.java b/hibernate-core/src/main/java/org/hibernate/criterion/SimpleExpression.java
index 5d377d78de7b..caee68875543 100644
--- a/hibernate-core/src/main/java/org/hibernate/criterion/SimpleExpression.java
+++ b/hibernate-core/src/main/java/org/hibernate/criterion/SimpleExpression.java
@@ -39,7 +39,7 @@ protected SimpleExpression(String propertyName, Object value, String op, boolean
this.op = op;
}
- protected final String getOp() {
+ public final String getOp() {
return op;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java
index 2c087e2ca3eb..61670a4fa4e5 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java
@@ -40,8 +40,10 @@
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.ScrollMode;
+import org.hibernate.boot.Metadata;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.boot.model.naming.Identifier;
+import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.function.AnsiTrimFunction;
import org.hibernate.dialect.function.NoArgSQLFunction;
@@ -728,14 +730,16 @@ public int getMaxLobPrefetchSize() {
private final StandardTableExporter hanaTableExporter = new StandardTableExporter( this ) {
@Override
- public String[] getSqlCreateStrings(org.hibernate.mapping.Table table, org.hibernate.boot.Metadata metadata) {
- String[] sqlCreateStrings = super.getSqlCreateStrings( table, metadata );
+ public String[] getSqlCreateStrings(Table table, Metadata metadata,
+ SqlStringGenerationContext context) {
+ String[] sqlCreateStrings = super.getSqlCreateStrings( table, metadata, context );
return quoteTypeIfNecessary( table, sqlCreateStrings, getCreateTableString() );
}
@Override
- public String[] getSqlDropStrings(Table table, org.hibernate.boot.Metadata metadata) {
- String[] sqlDropStrings = super.getSqlDropStrings( table, metadata );
+ public String[] getSqlDropStrings(Table table, Metadata metadata,
+ SqlStringGenerationContext context) {
+ String[] sqlDropStrings = super.getSqlDropStrings( table, metadata, context );
return quoteTypeIfNecessary( table, sqlDropStrings, "drop table" );
}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
index d157f8c59988..3d4a4b243141 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
@@ -2953,6 +2953,10 @@ public boolean isJdbcLogWarningsEnabledByDefault() {
return true;
}
+ public void augmentPhysicalTableTypes(List tableTypesList) {
+ // nothing to do
+ }
+
public void augmentRecognizedTableTypes(List tableTypesList) {
// noihing to do
}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java
index 319b64943d9d..5b1dff81e203 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java
@@ -8,6 +8,7 @@
import java.sql.SQLException;
import java.sql.Types;
+import java.util.List;
import org.hibernate.JDBCException;
import org.hibernate.PessimisticLockException;
@@ -15,9 +16,11 @@
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.function.AvgWithArgumentCastFunction;
import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.dialect.hint.IndexQueryHintHandler;
+import org.hibernate.dialect.identity.H2FinalTableIdentityColumnSupport;
import org.hibernate.dialect.identity.H2IdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.pagination.AbstractLimitHandler;
@@ -74,6 +77,8 @@ public boolean bindLimitParametersInReverseOrder() {
};
private final boolean supportsTuplesInSubqueries;
+ private final boolean hasOddDstBehavior;
+ private final boolean isVersion2;
private final String querySequenceString;
private final SequenceInformationExtractor sequenceInformationExtractor;
@@ -85,6 +90,8 @@ public H2Dialect() {
int buildId = Integer.MIN_VALUE;
boolean supportsTuplesInSubqueries = false;
+ boolean hasOddDstBehavior = false;
+ boolean isVersion2 = false;
try {
// HHH-2300
@@ -97,6 +104,9 @@ public H2Dialect() {
LOG.unsupportedMultiTableBulkHqlJpaql( majorVersion, minorVersion, buildId );
}
supportsTuplesInSubqueries = majorVersion > 1 || minorVersion > 4 || buildId >= 198;
+ // As of 1.4.200, the DST handling for timestamp without time zone is odd
+ hasOddDstBehavior = majorVersion > 1 || minorVersion > 4 || buildId >= 200;
+ isVersion2 = majorVersion > 1;
}
catch ( Exception e ) {
// probably H2 not in the classpath, though in certain app server environments it might just mean we are
@@ -115,6 +125,8 @@ public H2Dialect() {
this.querySequenceString = null;
}
this.supportsTuplesInSubqueries = supportsTuplesInSubqueries;
+ this.hasOddDstBehavior = hasOddDstBehavior;
+ this.isVersion2 = isVersion2;
registerColumnType( Types.BOOLEAN, "boolean" );
registerColumnType( Types.BIGINT, "bigint" );
@@ -140,6 +152,12 @@ public H2Dialect() {
registerColumnType( Types.BLOB, "blob" );
registerColumnType( Types.CLOB, "clob" );
+ if ( isVersion2 ) {
+ registerColumnType( Types.LONGVARCHAR, "character varying" );
+ registerColumnType( Types.BINARY, "binary($l)" );
+ registerFunction( "str", new SQLFunctionTemplate( StandardBasicTypes.STRING, "cast(?1 as character varying)") );
+ }
+
// Aggregations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
registerFunction( "avg", new AvgWithArgumentCastFunction( "double" ) );
@@ -235,6 +253,15 @@ public H2Dialect() {
getDefaultProperties().setProperty( AvailableSettings.NON_CONTEXTUAL_LOB_CREATION, "true" );
}
+ public boolean hasOddDstBehavior() {
+ // H2 1.4.200 has a bug: https://github.com/h2database/h2database/issues/3184
+ return hasOddDstBehavior;
+ }
+
+ public boolean isVersion2() {
+ return isVersion2;
+ }
+
@Override
public String getAddColumnString() {
return "add column";
@@ -245,6 +272,11 @@ public String getForUpdateString() {
return " for update";
}
+ @Override
+ public String toBooleanValueString(boolean bool) {
+ return String.valueOf( bool );
+ }
+
@Override
public LimitHandler getLimitHandler() {
return LIMIT_HANDLER;
@@ -422,6 +454,13 @@ public boolean supportsUnionAll() {
// Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ @Override
+ public void augmentPhysicalTableTypes(List tableTypesList) {
+ if ( isVersion2 ) {
+ tableTypesList.add( "BASE TABLE" );
+ }
+ }
+
@Override
public boolean supportsLobValueChangePropogation() {
return false;
@@ -467,7 +506,7 @@ public boolean supportsIfExistsBeforeTableName() {
@Override
public IdentityColumnSupport getIdentityColumnSupport() {
- return new H2IdentityColumnSupport();
+ return isVersion2 ? H2FinalTableIdentityColumnSupport.INSTANCE : H2IdentityColumnSupport.INSTANCE;
}
@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleTypesHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleTypesHelper.java
index 4b757ab4385a..8c18edd73978 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleTypesHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleTypesHelper.java
@@ -36,7 +36,7 @@ private OracleTypesHelper() {
typeCode = extractOracleCursorTypeValue();
}
catch (Exception e) {
- log.warn( "Unable to resolve Oracle CURSOR JDBC type code", e );
+ log.warn( "Unable to resolve Oracle CURSOR JDBC type code: the class OracleTypesHelper was initialized but the Oracle JDBC driver could not be loaded." );
}
oracleCursorTypeSqlType = typeCode;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL81Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL81Dialect.java
index ad4ee3a3eef4..09b8df5cb52a 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL81Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL81Dialect.java
@@ -114,7 +114,7 @@ public PostgreSQL81Dialect() {
registerColumnType( Types.BINARY, "bytea" );
registerColumnType( Types.LONGVARCHAR, "text" );
registerColumnType( Types.LONGVARBINARY, "bytea" );
- registerColumnType( Types.CLOB, "text" );
+ registerColumnType( Types.CLOB, "oid" );
registerColumnType( Types.BLOB, "oid" );
registerColumnType( Types.NUMERIC, "numeric($p, $s)" );
registerColumnType( Types.OTHER, "uuid" );
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2012Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2012Dialect.java
index b25672486650..1818f5042ba9 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2012Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServer2012Dialect.java
@@ -9,9 +9,9 @@
import org.hibernate.boot.Metadata;
import org.hibernate.boot.model.relational.QualifiedSequenceName;
import org.hibernate.boot.model.relational.Sequence;
+import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.SQLServer2012LimitHandler;
-import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.tool.schema.internal.StandardSequenceExporter;
import org.hibernate.tool.schema.spi.Exporter;
@@ -118,14 +118,12 @@ public SqlServerSequenceExporter(Dialect dialect) {
}
@Override
- protected String getFormattedSequenceName(QualifiedSequenceName name, Metadata metadata) {
- if ( name.getCatalogName() != null ) {
- // SQL Server does not allow the catalog in the sequence name.
- // See https://docs.microsoft.com/en-us/sql/t-sql/statements/create-sequence-transact-sql?view=sql-server-ver15&viewFallbackFrom=sql-server-ver12
- // Keeping the catalog in the name does not break on ORM, but it fails using Vert.X for Reactive.
- name = new QualifiedSequenceName( null, name.getSchemaName(), name.getObjectName() );
- }
- return super.getFormattedSequenceName( name, metadata );
+ protected String getFormattedSequenceName(QualifiedSequenceName name, Metadata metadata,
+ SqlStringGenerationContext context) {
+ // SQL Server does not allow the catalog in the sequence name.
+ // See https://docs.microsoft.com/en-us/sql/t-sql/statements/create-sequence-transact-sql?view=sql-server-ver15&viewFallbackFrom=sql-server-ver12
+ // Keeping the catalog in the name does not break on ORM, but it fails using Vert.X for Reactive.
+ return context.formatWithoutCatalog( name );
}
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java
index d6e7186b5f65..e28b5371a035 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java
@@ -50,6 +50,12 @@ public String getNullColumnString() {
return " null";
}
+ @Override
+ public boolean canCreateSchema() {
+ // As far as I can tell, it does not
+ return false;
+ }
+
@Override
public String getCurrentSchemaCommand() {
return "select db_name()";
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Teradata14Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Teradata14Dialect.java
index 9744036649a6..1526bbb975e5 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/Teradata14Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/Teradata14Dialect.java
@@ -17,6 +17,7 @@
import org.hibernate.LockOptions;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.model.relational.QualifiedNameImpl;
+import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.identity.IdentityColumnSupport;
@@ -214,22 +215,19 @@ public TeradataIndexExporter(Dialect dialect) {
}
@Override
- public String[] getSqlCreateStrings(Index index, Metadata metadata) {
+ public String[] getSqlCreateStrings(Index index, Metadata metadata,
+ SqlStringGenerationContext context) {
final JdbcEnvironment jdbcEnvironment = metadata.getDatabase().getJdbcEnvironment();
- final String tableName = jdbcEnvironment.getQualifiedObjectNameFormatter().format(
- index.getTable().getQualifiedTableName(),
- jdbcEnvironment.getDialect()
- );
+ final String tableName = context.format( index.getTable().getQualifiedTableName() );
final String indexNameForCreation;
if ( getDialect().qualifyIndexName() ) {
- indexNameForCreation = jdbcEnvironment.getQualifiedObjectNameFormatter().format(
+ indexNameForCreation = context.format(
new QualifiedNameImpl(
index.getTable().getQualifiedTableName().getCatalogName(),
index.getTable().getQualifiedTableName().getSchemaName(),
jdbcEnvironment.getIdentifierHelper().toIdentifier( index.getName() )
- ),
- jdbcEnvironment.getDialect()
+ )
);
}
else {
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/SQLFunctionRegistry.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/SQLFunctionRegistry.java
index 48d413fa6b71..3c8d95bd7a68 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/function/SQLFunctionRegistry.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/SQLFunctionRegistry.java
@@ -39,9 +39,12 @@ public SQLFunctionRegistry(Dialect dialect, Map userFunctio
*
* @param functionName The name of the function to locate
*
- * @return The located function, maye return {@code null}
+ * @return The located function, may return {@code null}
*/
- public SQLFunction findSQLFunction(String functionName) {
+ public SQLFunction findSQLFunction(final String functionName) {
+ if ( functionName == null ) {
+ return null;
+ }
return functionMap.get( functionName );
}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GetGeneratedKeysDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GetGeneratedKeysDelegate.java
index 38cc8e323c47..ef4c6e389640 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GetGeneratedKeysDelegate.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GetGeneratedKeysDelegate.java
@@ -11,6 +11,7 @@
import java.sql.ResultSet;
import java.sql.SQLException;
+import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGeneratorHelper;
@@ -37,7 +38,7 @@ public GetGeneratedKeysDelegate(PostInsertIdentityPersister persister, Dialect d
}
@Override
- public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
+ public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert(SqlStringGenerationContext context) {
IdentifierGeneratingInsert insert = new IdentifierGeneratingInsert( dialect );
insert.addIdentityColumn( persister.getRootTableKeyColumnNames()[0] );
return insert;
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/H2FinalTableIdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/H2FinalTableIdentityColumnSupport.java
new file mode 100644
index 000000000000..3f93ae73868c
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/H2FinalTableIdentityColumnSupport.java
@@ -0,0 +1,29 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
+ */
+package org.hibernate.dialect.identity;
+
+/**
+ * Identity column support for H2 2+ versions
+ * @author Jan Schatteman
+ */
+public class H2FinalTableIdentityColumnSupport extends H2IdentityColumnSupport {
+
+ public static final H2FinalTableIdentityColumnSupport INSTANCE = new H2FinalTableIdentityColumnSupport();
+
+ private H2FinalTableIdentityColumnSupport() {
+ }
+
+ @Override
+ public boolean supportsInsertSelectIdentity() {
+ return true;
+ }
+
+ @Override
+ public String appendIdentitySelectToInsert(String identityColumnName, String insertString) {
+ return "select " + identityColumnName + " from final table ( " + insertString + " )";
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/H2IdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/H2IdentityColumnSupport.java
index a114319e98d8..35bcf5d32b59 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/identity/H2IdentityColumnSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/H2IdentityColumnSupport.java
@@ -10,6 +10,12 @@
* @author Andrea Boriero
*/
public class H2IdentityColumnSupport extends IdentityColumnSupportImpl {
+
+ public static final H2IdentityColumnSupport INSTANCE = new H2IdentityColumnSupport();
+
+ protected H2IdentityColumnSupport() {
+ }
+
@Override
public boolean supportsIdentityColumns() {
return true;
@@ -28,6 +34,6 @@ public String getIdentitySelectString(String table, String column, int type) {
@Override
public String getIdentityInsertString() {
- return "null";
+ return "default";
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/IdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/IdentityColumnSupport.java
index e63592fbe556..e000d8357f32 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/identity/IdentityColumnSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/IdentityColumnSupport.java
@@ -56,6 +56,23 @@ public interface IdentityColumnSupport {
*/
String appendIdentitySelectToInsert(String insertString);
+ /**
+ * Provided we {@link #supportsInsertSelectIdentity}, then attach the
+ * "select identity" clause to the insert statement.
+ *
+ * Note, if {@link #supportsInsertSelectIdentity} == false then
+ * the insert-string should be returned without modification.
+ *
+ * @param identityColumnName The name of the identity column
+ * @param insertString The insert command
+ *
+ * @return The insert command with any necessary identity select
+ * clause attached.
+ */
+ default String appendIdentitySelectToInsert(String identityColumnName, String insertString) {
+ return appendIdentitySelectToInsert( insertString );
+ }
+
/**
* Get the select command to use to retrieve the last generated IDENTITY
* value for a particular table
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DB2UniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DB2UniqueDelegate.java
index b866eb762b95..1770053ab807 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DB2UniqueDelegate.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DB2UniqueDelegate.java
@@ -9,6 +9,7 @@
import java.util.Iterator;
import org.hibernate.boot.Metadata;
+import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.Dialect;
import org.hibernate.mapping.UniqueKey;
@@ -29,10 +30,11 @@ public DB2UniqueDelegate( Dialect dialect ) {
}
@Override
- public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata) {
+ public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
+ SqlStringGenerationContext context) {
if ( hasNullable( uniqueKey ) ) {
return org.hibernate.mapping.Index.buildSqlCreateIndexString(
- dialect,
+ context,
uniqueKey.getName(),
uniqueKey.getTable(),
uniqueKey.columnIterator(),
@@ -42,23 +44,21 @@ public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata m
);
}
else {
- return super.getAlterTableToAddUniqueKeyCommand( uniqueKey, metadata );
+ return super.getAlterTableToAddUniqueKeyCommand( uniqueKey, metadata, context );
}
}
@Override
- public String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata) {
+ public String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
+ SqlStringGenerationContext context) {
if ( hasNullable( uniqueKey ) ) {
return org.hibernate.mapping.Index.buildSqlDropIndexString(
uniqueKey.getName(),
- metadata.getDatabase().getJdbcEnvironment().getQualifiedObjectNameFormatter().format(
- uniqueKey.getTable().getQualifiedTableName(),
- metadata.getDatabase().getJdbcEnvironment().getDialect()
- )
+ context.format( uniqueKey.getTable().getQualifiedTableName() )
);
}
else {
- return super.getAlterTableToDropUniqueKeyCommand( uniqueKey, metadata );
+ return super.getAlterTableToDropUniqueKeyCommand( uniqueKey, metadata, context );
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java
index eec4fbd62f1f..f73d3ca30db3 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java
@@ -9,8 +9,10 @@
import java.util.Iterator;
import org.hibernate.boot.Metadata;
+import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.Dialect;
-import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey;
/**
@@ -34,23 +36,21 @@ public DefaultUniqueDelegate( Dialect dialect ) {
// legacy model ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
- public String getColumnDefinitionUniquenessFragment(org.hibernate.mapping.Column column) {
+ public String getColumnDefinitionUniquenessFragment(Column column,
+ SqlStringGenerationContext context) {
return "";
}
@Override
- public String getTableCreationUniqueConstraintsFragment(org.hibernate.mapping.Table table) {
+ public String getTableCreationUniqueConstraintsFragment(Table table,
+ SqlStringGenerationContext context) {
return "";
}
@Override
- public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata) {
- final JdbcEnvironment jdbcEnvironment = metadata.getDatabase().getJdbcEnvironment();
-
- final String tableName = jdbcEnvironment.getQualifiedObjectNameFormatter().format(
- uniqueKey.getTable().getQualifiedTableName(),
- dialect
- );
+ public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
+ SqlStringGenerationContext context) {
+ final String tableName = context.format( uniqueKey.getTable().getQualifiedTableName() );
final String constraintName = dialect.quote( uniqueKey.getName() );
return dialect.getAlterTableString( tableName )
@@ -76,13 +76,9 @@ protected String uniqueConstraintSql(UniqueKey uniqueKey) {
}
@Override
- public String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata) {
- final JdbcEnvironment jdbcEnvironment = metadata.getDatabase().getJdbcEnvironment();
-
- final String tableName = jdbcEnvironment.getQualifiedObjectNameFormatter().format(
- uniqueKey.getTable().getQualifiedTableName(),
- dialect
- );
+ public String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
+ SqlStringGenerationContext context) {
+ final String tableName = context.format( uniqueKey.getTable().getQualifiedTableName() );
final StringBuilder buf = new StringBuilder( dialect.getAlterTableString(tableName) );
buf.append( getDropUnique() );
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/InformixUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/InformixUniqueDelegate.java
index ae8fc4204cac..cffa7b5ffe82 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/InformixUniqueDelegate.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/InformixUniqueDelegate.java
@@ -7,6 +7,7 @@
package org.hibernate.dialect.unique;
import org.hibernate.boot.Metadata;
+import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.Dialect;
import org.hibernate.mapping.UniqueKey;
@@ -24,13 +25,11 @@ public InformixUniqueDelegate( Dialect dialect ) {
// legacy model ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
- public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata) {
+ public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
+ SqlStringGenerationContext context) {
// Do this here, rather than allowing UniqueKey/Constraint to do it.
// We need full, simplified control over whether or not it happens.
- final String tableName = metadata.getDatabase().getJdbcEnvironment().getQualifiedObjectNameFormatter().format(
- uniqueKey.getTable().getQualifiedTableName(),
- metadata.getDatabase().getJdbcEnvironment().getDialect()
- );
+ final String tableName = context.format( uniqueKey.getTable().getQualifiedTableName() );
final String constraintName = dialect.quote( uniqueKey.getName() );
return dialect.getAlterTableString( tableName )
+ " add constraint " + uniqueConstraintSql( uniqueKey ) + " constraint " + constraintName;
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java
index 89ef372eba2c..a00dff0f7e05 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java
@@ -7,6 +7,9 @@
package org.hibernate.dialect.unique;
import org.hibernate.boot.Metadata;
+import org.hibernate.boot.model.relational.SqlStringGenerationContext;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey;
/**
@@ -32,17 +35,34 @@
* @author Brett Meyer
*/
public interface UniqueDelegate {
+ /**
+ * Get the fragment that can be used to make a column unique as part of its column definition.
+ *
+ * This is intended for dialects which do not support unique constraints
+ *
+ * @param column The column to which to apply the unique
+ * @return The fragment (usually "unique"), empty string indicates the uniqueness will be indicated using a
+ * different approach
+ * @deprecated Implement {@link #getColumnDefinitionUniquenessFragment(Column, SqlStringGenerationContext)} instead.
+ */
+ @Deprecated
+ default String getColumnDefinitionUniquenessFragment(Column column) {
+ throw new IllegalStateException("getColumnDefinitionUniquenessFragment(...) was not implemented!");
+ }
+
/**
* Get the fragment that can be used to make a column unique as part of its column definition.
*
* This is intended for dialects which do not support unique constraints
*
* @param column The column to which to apply the unique
- *
+ * @param context A context for SQL string generation
* @return The fragment (usually "unique"), empty string indicates the uniqueness will be indicated using a
* different approach
*/
- public String getColumnDefinitionUniquenessFragment(org.hibernate.mapping.Column column);
+ default String getColumnDefinitionUniquenessFragment(Column column, SqlStringGenerationContext context) {
+ return getColumnDefinitionUniquenessFragment( column );
+ }
/**
* Get the fragment that can be used to apply unique constraints as part of table creation. The implementation
@@ -53,30 +73,82 @@ public interface UniqueDelegate {
* Intended for Dialects which support unique constraint definitions, but just not in separate ALTER statements.
*
* @param table The table for which to generate the unique constraints fragment
+ * @return The fragment, typically in the form {@code ", unique(col1, col2), unique( col20)"}. NOTE: The leading
+ * comma is important!
+ * @deprecated Implement {@link #getTableCreationUniqueConstraintsFragment(Table, SqlStringGenerationContext)} instead.
+ */
+ @Deprecated
+ default String getTableCreationUniqueConstraintsFragment(Table table) {
+ throw new IllegalStateException("getTableCreationUniqueConstraintsFragment(...) was not implemented!");
+ }
+
+ /**
+ * Get the fragment that can be used to apply unique constraints as part of table creation. The implementation
+ * should iterate over the {@link org.hibernate.mapping.UniqueKey} instances for the given table (see
+ * {@link org.hibernate.mapping.Table#getUniqueKeyIterator()} and generate the whole fragment for all
+ * unique keys
+ *
+ * Intended for Dialects which support unique constraint definitions, but just not in separate ALTER statements.
*
+ * @param table The table for which to generate the unique constraints fragment
+ * @param context A context for SQL string generation
* @return The fragment, typically in the form {@code ", unique(col1, col2), unique( col20)"}. NOTE: The leading
* comma is important!
*/
- public String getTableCreationUniqueConstraintsFragment(org.hibernate.mapping.Table table);
+ default String getTableCreationUniqueConstraintsFragment(Table table, SqlStringGenerationContext context) {
+ return getTableCreationUniqueConstraintsFragment( table );
+ }
/**
* Get the SQL ALTER TABLE command to be used to create the given UniqueKey.
*
* @param uniqueKey The UniqueKey instance. Contains all information about the columns
* @param metadata Access to the bootstrap mapping information
+ * @return The ALTER TABLE command
+ * @deprecated Implement {@link #getAlterTableToAddUniqueKeyCommand(UniqueKey, Metadata, SqlStringGenerationContext)} instead.
+ */
+ @Deprecated
+ default String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata) {
+ throw new IllegalStateException("getAlterTableToAddUniqueKeyCommand(...) was not implemented!");
+ }
+
+ /**
+ * Get the SQL ALTER TABLE command to be used to create the given UniqueKey.
*
+ * @param uniqueKey The UniqueKey instance. Contains all information about the columns
+ * @param metadata Access to the bootstrap mapping information
+ * @param context A context for SQL string generation
* @return The ALTER TABLE command
*/
- public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata);
+ default String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
+ SqlStringGenerationContext context) {
+ return getAlterTableToAddUniqueKeyCommand( uniqueKey, metadata );
+ }
/**
* Get the SQL ALTER TABLE command to be used to drop the given UniqueKey.
*
* @param uniqueKey The UniqueKey instance. Contains all information about the columns
* @param metadata Access to the bootstrap mapping information
+ * @return The ALTER TABLE command
+ * @deprecated Implement {@link #getAlterTableToDropUniqueKeyCommand(UniqueKey, Metadata, SqlStringGenerationContext)} instead.
+ */
+ @Deprecated
+ default String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata) {
+ throw new IllegalStateException("getAlterTableToDropUniqueKeyCommand(...) was not implemented!");
+ }
+
+ /**
+ * Get the SQL ALTER TABLE command to be used to drop the given UniqueKey.
*
+ * @param uniqueKey The UniqueKey instance. Contains all information about the columns
+ * @param metadata Access to the bootstrap mapping information
+ * @param context A context for SQL string generation
* @return The ALTER TABLE command
*/
- public String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata);
+ default String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
+ SqlStringGenerationContext context) {
+ return getAlterTableToDropUniqueKeyCommand( uniqueKey, metadata );
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java
index cfa74558cead..abb003c63290 100644
--- a/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java
+++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java
@@ -22,6 +22,7 @@
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityEntryExtraState;
import org.hibernate.engine.spi.EntityKey;
+import org.hibernate.engine.spi.Managed;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
@@ -279,9 +280,7 @@ public void postUpdate(Object entity, Object[] updatedState, Object nextVersion)
getPersister().setPropertyValue( entity, getPersister().getVersionProperty(), nextVersion );
}
- if( entity instanceof SelfDirtinessTracker ) {
- ( (SelfDirtinessTracker) entity ).$$_hibernate_clearDirtyAttributes();
- }
+ ManagedTypeHelper.processIfSelfDirtinessTracker( entity, AbstractEntityEntry::clearDirtyAttributes );
getPersistenceContext().getSession()
.getFactory()
@@ -289,6 +288,10 @@ public void postUpdate(Object entity, Object[] updatedState, Object nextVersion)
.resetDirty( entity, getPersister(), (Session) getPersistenceContext().getSession() );
}
+ private static void clearDirtyAttributes(final SelfDirtinessTracker entity) {
+ entity.$$_hibernate_clearDirtyAttributes();
+ }
+
@Override
public void postDelete() {
setCompressedValue( EnumState.PREVIOUS_STATUS, getStatus() );
@@ -345,10 +348,10 @@ public boolean requiresDirtyCheck(Object entity) {
@SuppressWarnings( {"SimplifiableIfStatement"})
private boolean isUnequivocallyNonDirty(Object entity) {
- if ( entity instanceof SelfDirtinessTracker ) {
+ if ( ManagedTypeHelper.isSelfDirtinessTracker( entity ) ) {
boolean uninitializedProxy = false;
- if ( entity instanceof PersistentAttributeInterceptable ) {
- final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) entity;
+ if ( ManagedTypeHelper.isPersistentAttributeInterceptable( entity ) ) {
+ final PersistentAttributeInterceptable interceptable = ManagedTypeHelper.asPersistentAttributeInterceptable( entity );
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
EnhancementAsProxyLazinessInterceptor enhancementAsProxyLazinessInterceptor = (EnhancementAsProxyLazinessInterceptor) interceptor;
@@ -365,11 +368,11 @@ else if ( entity instanceof HibernateProxy ) {
// we never have to check an uninitialized proxy
return uninitializedProxy || !persister.hasCollections()
&& !persister.hasMutableProperties()
- && !( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes();
+ && !ManagedTypeHelper.asSelfDirtinessTracker( entity ).$$_hibernate_hasDirtyAttributes();
}
- if ( entity instanceof PersistentAttributeInterceptable ) {
- final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) entity;
+ if ( ManagedTypeHelper.isPersistentAttributeInterceptable( entity ) ) {
+ final PersistentAttributeInterceptable interceptable = ManagedTypeHelper.asPersistentAttributeInterceptable( entity );
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
// we never have to check an uninitialized proxy
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java
index 97f20b183509..b67e2292695e 100644
--- a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java
+++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java
@@ -9,6 +9,7 @@
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
+import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.PersistenceContext;
@@ -91,21 +92,22 @@ public void addEntityEntry(Object entity, EntityEntry entityEntry) {
ManagedEntity managedEntity = getAssociatedManagedEntity( entity );
final boolean alreadyAssociated = managedEntity != null;
if ( !alreadyAssociated ) {
- if ( ManagedEntity.class.isInstance( entity ) ) {
+ if ( ManagedTypeHelper.isManagedEntity( entity ) ) {
+ final ManagedEntity managed = ManagedTypeHelper.asManagedEntity( entity );
if ( entityEntry.getPersister().isMutable() ) {
- managedEntity = (ManagedEntity) entity;
+ managedEntity = managed;
// We know that managedEntity is not associated with the same PersistenceContext.
// Check if managedEntity is associated with a different PersistenceContext.
checkNotAssociatedWithOtherPersistenceContextIfMutable( managedEntity );
}
else {
// Create a holder for PersistenceContext-related data.
- managedEntity = new ImmutableManagedEntityHolder( (ManagedEntity) entity );
+ managedEntity = new ImmutableManagedEntityHolder( managed );
if ( immutableManagedEntityXref == null ) {
immutableManagedEntityXref = new IdentityHashMap();
}
immutableManagedEntityXref.put(
- (ManagedEntity) entity,
+ managed,
(ImmutableManagedEntityHolder) managedEntity
);
}
@@ -150,8 +152,8 @@ public void addEntityEntry(Object entity, EntityEntry entityEntry) {
}
private ManagedEntity getAssociatedManagedEntity(Object entity) {
- if ( ManagedEntity.class.isInstance( entity ) ) {
- final ManagedEntity managedEntity = (ManagedEntity) entity;
+ if ( ManagedTypeHelper.isManagedEntity( entity ) ) {
+ final ManagedEntity managedEntity = ManagedTypeHelper.asManagedEntity( entity );
if ( managedEntity.$$_hibernate_getEntityEntry() == null ) {
// it is not associated
return null;
@@ -368,7 +370,10 @@ public void downgradeLocks() {
ManagedEntity node = head;
while ( node != null ) {
- node.$$_hibernate_getEntityEntry().setLockMode( LockMode.NONE );
+ EntityEntry entityEntry = node.$$_hibernate_getEntityEntry();
+ if ( entityEntry != null ) {
+ entityEntry.setLockMode( LockMode.NONE );
+ }
node = node.$$_hibernate_getNextManagedEntity();
}
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/ManagedTypeHelper.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/ManagedTypeHelper.java
new file mode 100644
index 000000000000..284660f56b59
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/ManagedTypeHelper.java
@@ -0,0 +1,188 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
+ * See the lgpl.txt file in the root directory or .
+ */
+package org.hibernate.engine.internal;
+
+import org.hibernate.engine.spi.EnhancedEntity;
+import org.hibernate.engine.spi.Managed;
+import org.hibernate.engine.spi.ManagedEntity;
+import org.hibernate.engine.spi.PersistentAttributeInterceptable;
+import org.hibernate.engine.spi.SelfDirtinessTracker;
+
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+/**
+ * This is a helper to encapsulate an optimal strategy to execute type checks
+ * for interfaces which attempts to avoid the performance issues tracked
+ * as https://bugs.openjdk.org/browse/JDK-8180450 ;
+ * the problem is complex and best understood by reading the OpenJDK tracker;
+ * we'll focus on a possible solution here.
+ *
+ * To avoid polluting the secondary super-type cache, the important aspect is to
+ * not switch types repeatedly for the same concrete object; using a Java
+ * agent which was developed for this purpose (https://github.com/franz1981/type-pollution-agent)
+ * we identified a strong case with Hibernate ORM is triggered when the entities are
+ * using bytecode enhancement, as they are being checked by a set of interfaces:
+ * {@see org.hibernate.engine.spi.PersistentAttributeInterceptable}
+ * {@see org.hibernate.engine.spi.ManagedEntity}
+ * {@see org.hibernate.engine.spi.SelfDirtinessTracker}
+ * {@see org.hibernate.engine.spi.Managed}
+ * With our domain knowledge, we bet on the assumption that either enhancement isn't being
+ * used at all, OR that when enhancement is being used, there is a strong likelyhood for
+ * all of these supertypes to be have been injected into the managed objected of the domain
+ * model (this isn't a certainty as otherwise we'd not have multiple interfaces to separate
+ * these), but we're working based on the assumption so to at least optimise for what
+ * we expect being a very common configuration.
+ * (At this time we won't optimise also embeddables and other corner cases, which will
+ * need to be looked at separately).
+ * We therefore introduce a new marker interface {@see EnhancedEntity}, which extends
+ * all these other contracts, and have the enhancer tool apply it when all other interfaces
+ * have been applied.
+ * This then allows to check always and consistently for this type only; as fallback
+ * path, we perform the "traditional" operation as it would have been before this patch.
+ * @author Sanne Grinovero
+ */
+public final class ManagedTypeHelper {
+
+ /**
+ * @param type
+ * @return true if and only if the type is assignable to a {@see Managed} type.
+ */
+ public static boolean isManagedType(final Class type) {
+ return EnhancedEntity.class.isAssignableFrom( type ) || Managed.class.isAssignableFrom( type );
+ }
+
+ /**
+ * @param entity
+ * @return true if and only if the entity implements {@see Managed}
+ */
+ public static boolean isManaged(final Object entity) {
+ return entity instanceof EnhancedEntity || entity instanceof Managed;
+ }
+
+ /**
+ * @param entity
+ * @return true if and only if the entity implements {@see ManagedEntity}
+ */
+ public static boolean isManagedEntity(Object entity) {
+ return entity instanceof EnhancedEntity || entity instanceof ManagedEntity;
+ }
+
+ /**
+ * @param type
+ * @return true if and only if the type is assignable to a {@see PersistentAttributeInterceptable} type.
+ */
+ public static boolean isPersistentAttributeInterceptableType(final Class type) {
+ return EnhancedEntity.class.isAssignableFrom( type ) || PersistentAttributeInterceptable.class.isAssignableFrom( type );
+ }
+
+ /**
+ * @param entity
+ * @return true if and only if the entity implements {@see PersistentAttributeInterceptable}
+ */
+ public static boolean isPersistentAttributeInterceptable(final Object entity) {
+ return entity instanceof EnhancedEntity || entity instanceof PersistentAttributeInterceptable;
+ }
+
+ /**
+ * @param entity
+ * @return true if and only if the entity implements {@see SelfDirtinessTracker}
+ */
+ public static boolean isSelfDirtinessTracker(final Object entity) {
+ return entity instanceof EnhancedEntity || entity instanceof SelfDirtinessTracker;
+ }
+
+ /**
+ * Helper to execute an action on an entity, but exclusively if it's implementing the {@see PersistentAttributeInterceptable}
+ * interface. Otherwise no action is performed.
+ *
+ * @param entity
+ * @param action The action to be performed; it should take the entity as first parameter, and an additional parameter T as second parameter.
+ * @param optionalParam a parameter which can be passed to the action
+ * @param the type of the additional parameter.
+ */
+ public static void processIfPersistentAttributeInterceptable(
+ final Object entity,
+ final BiConsumer action,
+ final T optionalParam) {
+ if ( entity instanceof EnhancedEntity ) {
+ EnhancedEntity e = (EnhancedEntity) entity;
+ action.accept( e, optionalParam );
+ }
+ else if ( entity instanceof PersistentAttributeInterceptable ) {
+ PersistentAttributeInterceptable e = (PersistentAttributeInterceptable) entity;
+ action.accept( e, optionalParam );
+ }
+ }
+
+ /**
+ * If the entity is implementing SelfDirtinessTracker, apply some action to it.
+ * It is first cast to SelfDirtinessTracker using an optimal strategy.
+ * If the entity does not implement SelfDirtinessTracker, no operation is performed.
+ * @param entity
+ * @param action
+ */
+ public static void processIfSelfDirtinessTracker(final Object entity, final Consumer action) {
+ if ( entity instanceof EnhancedEntity ) {
+ EnhancedEntity e = (EnhancedEntity) entity;
+ action.accept( e );
+ }
+ else if ( entity instanceof SelfDirtinessTracker ) {
+ SelfDirtinessTracker e = (SelfDirtinessTracker) entity;
+ action.accept( e );
+ }
+ }
+
+ /**
+ * Cast the object to PersistentAttributeInterceptable
+ * (using this is highly preferrable over a direct cast)
+ * @param entity the entity to cast
+ * @return the same instance after casting
+ * @throws ClassCastException if it's not of the right type
+ */
+ public static PersistentAttributeInterceptable asPersistentAttributeInterceptable(final Object entity) {
+ if ( entity instanceof EnhancedEntity ) {
+ return (EnhancedEntity) entity;
+ }
+ else {
+ return (PersistentAttributeInterceptable) entity;
+ }
+ }
+
+ /**
+ * Cast the object to ManagedEntity
+ * (using this is highly preferrable over a direct cast)
+ * @param entity the entity to cast
+ * @return the same instance after casting
+ * @throws ClassCastException if it's not of the right type
+ */
+ public static ManagedEntity asManagedEntity(final Object entity) {
+ if ( entity instanceof EnhancedEntity ) {
+ return (EnhancedEntity) entity;
+ }
+ else {
+ return (ManagedEntity) entity;
+ }
+ }
+
+ /**
+ * Cast the object to SelfDirtinessTracker
+ * (using this is highly preferrable over a direct cast)
+ * @param entity the entity to cast
+ * @return the same instance after casting
+ * @throws ClassCastException if it's not of the right type
+ */
+ public static SelfDirtinessTracker asSelfDirtinessTracker(final Object entity) {
+ if ( entity instanceof EnhancedEntity ) {
+ return (EnhancedEntity) entity;
+ }
+ else {
+ return (SelfDirtinessTracker) entity;
+ }
+ }
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdXrefDelegate.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdXrefDelegate.java
index 71233928bfb3..ed464b0ab1ee 100644
--- a/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdXrefDelegate.java
+++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdXrefDelegate.java
@@ -87,13 +87,14 @@ public boolean cacheNaturalIdCrossReference(EntityPersister persister, Serializa
/**
* Handle removing cross reference entries for the given natural-id/pk combo
*
- * @param persister The persister representing the entity type.
- * @param pk The primary key value
+ * @param persister The persister representing the entity type.
+ * @param pk The primary key value
* @param naturalIdValues The natural id value(s)
- *
+ * @param removeOnNaturalIdCache remove the entry on shared cache too
+ *
* @return The cached values, if any. May be different from incoming values.
*/
- public Object[] removeNaturalIdCrossReference(EntityPersister persister, Serializable pk, Object[] naturalIdValues) {
+ public Object[] removeNaturalIdCrossReference(EntityPersister persister, Serializable pk, Object[] naturalIdValues, boolean removeOnNaturalIdCache) {
persister = locatePersisterForKey( persister );
validateNaturalId( persister, naturalIdValues );
@@ -108,7 +109,7 @@ public Object[] removeNaturalIdCrossReference(EntityPersister persister, Seriali
}
}
- if ( persister.hasNaturalIdCache() ) {
+ if ( removeOnNaturalIdCache && persister.hasNaturalIdCache() ) {
final NaturalIdDataAccess naturalIdCacheAccessStrategy = persister
.getNaturalIdCacheAccessStrategy();
final Object naturalIdCacheKey = naturalIdCacheAccessStrategy.generateCacheKey( naturalIdValues, persister, session() );
diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/NonNullableTransientDependencies.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/NonNullableTransientDependencies.java
index 4d064c73a194..36df2d7afae9 100644
--- a/hibernate-core/src/main/java/org/hibernate/engine/internal/NonNullableTransientDependencies.java
+++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/NonNullableTransientDependencies.java
@@ -24,7 +24,7 @@ public final class NonNullableTransientDependencies {
// for the map value.
private Map