"""
+ }
+
+ 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..2e84eb08b639 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,25 +14,18 @@ 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 'nu.studer.credentials' version '2.2'
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 +44,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..a04d03f69663
--- /dev/null
+++ b/ci/release/Jenkinsfile
@@ -0,0 +1,275 @@
+#! /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 8 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.filemgmt.jboss.org', '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 release') {
+ 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([
+ // https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh
+ // TODO: HHH-19309:
+ // Once we switch to maven-central publishing (from nexus2) we need to add a new credentials
+ // to use the following env variable names to set the user/password:
+ // - JRELEASER_MAVENCENTRAL_USERNAME
+ // - JRELEASER_MAVENCENTRAL_TOKEN
+ // Also use the new `credentialsId` for Maven Central, e.g.:
+ // usernamePassword(credentialsId: '???????', passwordVariable: 'JRELEASER_MAVENCENTRAL_TOKEN', usernameVariable: 'JRELEASER_MAVENCENTRAL_USERNAME'),
+ usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'JRELEASER_NEXUS2_PASSWORD', usernameVariable: 'JRELEASER_NEXUS2_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', 'hibernate.filemgmt.jboss.org', 'hibernate-ci.frs.sourceforge.net']) {
+ // performs documentation upload and Sonatype release
+ // push to github
+ withEnv([
+ "DISABLE_REMOTE_GRADLE_CACHE=true"
+ ]) {
+ sh ".release/scripts/publish.sh -j ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION} ${env.GIT_BRANCH}"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ stage('Website release') {
+ 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', 'hibernate.filemgmt.jboss.org', 'hibernate-ci.frs.sourceforge.net'] ) {
+ 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..e625f7eda044
--- /dev/null
+++ b/ci/snapshot-publish.Jenkinsfile
@@ -0,0 +1,76 @@
+/*
+ * 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
+ // TODO: HHH-19309:
+ // Once we switch to maven-central publishing (from nexus2) we need to update credentialsId:
+ // https://docs.gradle.org/current/samples/sample_publishing_credentials.html#:~:text=via%20environment%20variables
+ usernamePassword(credentialsId: 'ossrh.sonatype.org', 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/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..3e9dc94f3dd4 100644
--- a/gradle/published-java-module.gradle
+++ b/gradle/published-java-module.gradle
@@ -165,6 +165,5 @@ publishing {
task ciBuild( dependsOn: [test, publish] )
-task release( dependsOn: [test, publishToSonatype] )
-publishToSonatype.mustRunAfter test
+task release( dependsOn: [test] )
diff --git a/gradle/publishing-repos.gradle b/gradle/publishing-repos.gradle
index b417e1eba3ca..a0fb56fa24fd 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://oss.sonatype.org/content/repositories/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