diff --git a/h8-preview/README.md b/h8-preview/README.md new file mode 100644 index 0000000..8b30461 --- /dev/null +++ b/h8-preview/README.md @@ -0,0 +1,15 @@ +# h8-preview + +Companion module for the ankurm.com article *Hibernate 8 and Jakarta Persistence 4.0 Preview: What Will Break*. +Hibernate ORM **8.0.0.Beta3** and Jakarta Persistence **4.0.0-M7** are pre-release; the numbers below can change. + +| Path | What it is | +|---|---| +| `pom.xml` | standalone project (no Spring); `-Dhibernate.version=7.4.5.Final -Dpersistence.version=3.2.0 -Dh.label=h7` runs the same tests on Hibernate 7 | +| `src/test/.../ExceptionContractsTest`, `FlushOrderTest`, `MutatingNamedQueryTest` | same code on both versions; each writes a transcript | +| `src/broken/` | not built; compiled by `scripts/capture-compile-errors.sh` to capture the compiler's own words | +| `scripts/run-suite.sh` | runs the whole `../src` suite (the Hibernate 7 chapters) in a scratch copy against a chosen Hibernate / Boot pair | +| `output/` | every transcript quoted in the article | + +There is deliberately no `docs/` folder for this module; the explanations are in the article. +The parent repository's older `docs/` chapters belong to earlier articles and are unchanged. diff --git a/h8-preview/output/01-exception-contracts-h7.txt b/h8-preview/output/01-exception-contracts-h7.txt new file mode 100644 index 0000000..6b6c2ce --- /dev/null +++ b/h8-preview/output/01-exception-contracts-h7.txt @@ -0,0 +1,7 @@ +# 01-exception-contracts (Hibernate ORM 7.4.5.Final, JDK 25) +unbound parameter : threw org.hibernate.QueryParameterException +null parameter : returned 0 +type mismatch : threw org.hibernate.query.QueryArgumentException +unique violation : threw org.hibernate.exception.ConstraintViolationException <- org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException +unknown parameter : threw java.lang.IllegalArgumentException <- org.hibernate.query.UnknownParameterException +named query, no row: 0 rows diff --git a/h8-preview/output/01-exception-contracts-h8.txt b/h8-preview/output/01-exception-contracts-h8.txt new file mode 100644 index 0000000..cfc9046 --- /dev/null +++ b/h8-preview/output/01-exception-contracts-h8.txt @@ -0,0 +1,7 @@ +# 01-exception-contracts (Hibernate ORM 8.0.0.Beta3, JDK 25) +unbound parameter : threw org.hibernate.QueryParameterException +null parameter : returned 0 +type mismatch : threw org.hibernate.query.QueryArgumentException +unique violation : threw org.hibernate.exception.ConstraintViolationException <- org.h2.jdbc.JdbcBatchUpdateException +unknown parameter : threw java.lang.IllegalArgumentException <- org.hibernate.query.UnknownParameterException +named query, no row: 0 rows diff --git a/h8-preview/output/02-flush-order-h7.txt b/h8-preview/output/02-flush-order-h7.txt new file mode 100644 index 0000000..1daaf96 --- /dev/null +++ b/h8-preview/output/02-flush-order-h7.txt @@ -0,0 +1,4 @@ +# 02-flush-order (Hibernate ORM 7.4.5.Final, JDK 25) +default : [Parent, Child, Parent, Child] +hibernate.flush.queue.type=legacy: [Parent, Child, Parent, Child] +hibernate.flush.queue.type=graph : [Parent, Child, Parent, Child] diff --git a/h8-preview/output/02-flush-order-h8.txt b/h8-preview/output/02-flush-order-h8.txt new file mode 100644 index 0000000..53dddab --- /dev/null +++ b/h8-preview/output/02-flush-order-h8.txt @@ -0,0 +1,4 @@ +# 02-flush-order (Hibernate ORM 8.0.0.Beta3, JDK 25) +default : [Parent, Child] +hibernate.flush.queue.type=legacy: [Parent, Child, Parent, Child] +hibernate.flush.queue.type=graph : [Parent, Child] diff --git a/h8-preview/output/03-mutating-named-query-h7.txt b/h8-preview/output/03-mutating-named-query-h7.txt new file mode 100644 index 0000000..451230e --- /dev/null +++ b/h8-preview/output/03-mutating-named-query-h7.txt @@ -0,0 +1,2 @@ +# 03-mutating-named-query (Hibernate ORM 7.4.5.Final, JDK 25) +session factory built; createNamedQuery(..).executeUpdate() returned 0 diff --git a/h8-preview/output/03-mutating-named-query-h8.txt b/h8-preview/output/03-mutating-named-query-h8.txt new file mode 100644 index 0000000..bcf18ab --- /dev/null +++ b/h8-preview/output/03-mutating-named-query-h8.txt @@ -0,0 +1,2 @@ +# 03-mutating-named-query (Hibernate ORM 8.0.0.Beta3, JDK 25) +threw java.lang.IllegalArgumentException <- org.hibernate.query.IllegalSelectQueryException :: Expecting a SELECT Query [org.hibernate.query.sqm.tree.spi.select.SqmSelectStatement], but found org.hibernate.query.sqm.tree.spi.update.SqmUpdateStatement [update MutatingNamedQueryEntity m set m.name = :name] diff --git a/h8-preview/output/04-compile-errors.txt b/h8-preview/output/04-compile-errors.txt new file mode 100644 index 0000000..b38d231 --- /dev/null +++ b/h8-preview/output/04-compile-errors.txt @@ -0,0 +1,49 @@ +# compile-errors (javac --release 25 -Xlint:removal -Xlint:deprecation; files in src/broken/) +--- UsesTransformers on h7 +UsesTransformers.java:14: warning: [deprecation] Transformers in org.hibernate.transform has been deprecated + .setResultTransformer(Transformers.aliasToBean(Dto.class)).list(); +UsesTransformers.java:14: warning: [deprecation] setResultTransformer(ResultTransformer) in NativeQuery has been deprecated + .setResultTransformer(Transformers.aliasToBean(Dto.class)).list(); + where S is a type-variable: + S extends Object declared in method setResultTransformer(ResultTransformer) +Note: src/broken/UsesTransformers.java uses unchecked or unsafe operations. +Note: Recompile with -Xlint:unchecked for details. +2 warnings +--- UsesTransformers on h8 +UsesTransformers.java:3: error: package org.hibernate.transform does not exist +import org.hibernate.transform.Transformers; +UsesTransformers.java:14: error: cannot find symbol + .setResultTransformer(Transformers.aliasToBean(Dto.class)).list(); + symbol: variable Transformers + location: class UsesTransformers +2 errors +--- UsesReplicate on h7 +UsesReplicate.java:8: warning: [deprecation] ReplicationMode in org.hibernate has been deprecated + s.replicate(detached, ReplicationMode.OVERWRITE); +UsesReplicate.java:8: warning: [deprecation] replicate(Object,ReplicationMode) in Session has been deprecated + s.replicate(detached, ReplicationMode.OVERWRITE); +2 warnings +--- UsesReplicate on h8 +UsesReplicate.java:1: error: cannot find symbol +import org.hibernate.ReplicationMode; + symbol: class ReplicationMode + location: package org.hibernate +UsesReplicate.java:8: error: cannot find symbol + s.replicate(detached, ReplicationMode.OVERWRITE); + symbol: variable ReplicationMode + location: class UsesReplicate +2 errors +--- StatelessInsertReturnsId on h7 +--- StatelessInsertReturnsId on h8 +StatelessInsertReturnsId.java:7: error: incompatible types: void cannot be converted to Object + Object id = ss.insert(new com.ankurm.h8.Author("A", "a@example.com")); +1 error +--- QueryVariable on h7 +--- QueryVariable on h8 +QueryVariable.java:9: warning: [removal] getResultList() in Query has been deprecated and marked for removal + return q.getResultList().size(); + where T is a type-variable: + T extends Object declared in interface Query +1 warning +--- TypedQueryVariable on h7 +--- TypedQueryVariable on h8 diff --git a/h8-preview/output/05-binary-compatibility.txt b/h8-preview/output/05-binary-compatibility.txt new file mode 100644 index 0000000..fdb607f --- /dev/null +++ b/h8-preview/output/05-binary-compatibility.txt @@ -0,0 +1,5 @@ +# binary-compatibility (BinaryProbe compiled against one Hibernate, run on the other) +--- compiled on h7, run on h8 +Exception in thread "main" java.lang.NoSuchMethodError: 'org.hibernate.query.Query org.hibernate.Session.createQuery(java.lang.String, java.lang.Class)' +--- compiled on h8, run on h7 +Exception in thread "main" java.lang.NoSuchMethodError: 'org.hibernate.query.SelectionQuery org.hibernate.Session.createQuery(java.lang.String, java.lang.Class)' diff --git a/h8-preview/output/06-persistence-spi.txt b/h8-preview/output/06-persistence-spi.txt new file mode 100644 index 0000000..ae939f0 --- /dev/null +++ b/h8-preview/output/06-persistence-spi.txt @@ -0,0 +1,12 @@ +# persistence-spi (javap on jakarta.persistence.spi.PersistenceUnitInfo, and on Spring's implementation) +--- methods that exist in Jakarta Persistence 4.0.0-M7 but not in 3.2.0 + public abstract FetchType getDefaultToOneFetchType(); + public abstract List getAllClassNames(); + public abstract List getAllModuleDescriptors(); + public abstract List getAllPackageDescriptors(); + public abstract List getManagedModuleDescriptors(); + public abstract List getManagedPackageDescriptors(); +--- org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo 7.0.9: methods mentioning those names + class found, 15 methods and constructors listed; matches for the six new names: 0 +--- org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo 7.1.0-M2: methods mentioning those names + class found, 15 methods and constructors listed; matches for the six new names: 0 diff --git a/h8-preview/output/07-hibernate-core-pom.txt b/h8-preview/output/07-hibernate-core-pom.txt new file mode 100644 index 0000000..7a53fb4 --- /dev/null +++ b/h8-preview/output/07-hibernate-core-pom.txt @@ -0,0 +1,4 @@ +# dependency-versions (hibernate-core 8.0.0.Beta3 POM: what it declares) +jakarta.persistence-api 4.0.0-M7 +hibernate-models 1.3.4 +byte-buddy 1.18.11 diff --git a/h8-preview/output/suite-h7.txt b/h8-preview/output/suite-h7.txt new file mode 100644 index 0000000..a9cfb56 --- /dev/null +++ b/h8-preview/output/suite-h7.txt @@ -0,0 +1,9 @@ +# suite on h7 hibernate=managed by Boot 4.1.1 persistence-api=managed by Boot 4.1.1 +spring boot parent: 4.1.1 +pins (hibernate-models 1.3.4, jandex 3.6.0): no +test-compile exit code: 0 +tests run: 205 failures: 0 errors: 2 skipped: 0 +test classes with a problem: 1 +distinct final causes (count = failing test methods): + 1 org.hibernate.query.UnknownNamedQueryException: No query named 'XmlQueryEmployee.findBySalaryAboveXml' + 1 org.hibernate.query.UnknownParameterException: No parameter named ':min' in query with named parameters [] diff --git a/h8-preview/output/suite-h8-boot42.txt b/h8-preview/output/suite-h8-boot42.txt new file mode 100644 index 0000000..cc061f2 --- /dev/null +++ b/h8-preview/output/suite-h8-boot42.txt @@ -0,0 +1,11 @@ +# suite on h8-boot42 hibernate=8.0.0.Beta3 persistence-api=4.0.0-M7 +spring boot parent: 4.2.0-M2 +pins (hibernate-models 1.3.4, jandex 3.6.0): yes +test-compile exit code: 0 +tests run: 205 failures: 1 errors: 158 skipped: 0 +test classes with a problem: 52 +distinct final causes (count = failing test methods): + 140 (context failed earlier in the same JVM; see the first failure of that context) + 14 java.lang.NoSuchMethodException: org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo.getAllPackageDescriptors() + 4 org.hibernate.query.NamedQueryValidationException: Errors in named queries: + 1 java.lang.AssertionError: diff --git a/h8-preview/output/suite-h8-pinned.txt b/h8-preview/output/suite-h8-pinned.txt new file mode 100644 index 0000000..7a385a8 --- /dev/null +++ b/h8-preview/output/suite-h8-pinned.txt @@ -0,0 +1,11 @@ +# suite on h8-pinned hibernate=8.0.0.Beta3 persistence-api=4.0.0-M7 +spring boot parent: 4.1.1 +pins (hibernate-models 1.3.4, jandex 3.6.0): yes +test-compile exit code: 0 +tests run: 205 failures: 1 errors: 158 skipped: 0 +test classes with a problem: 52 +distinct final causes (count = failing test methods): + 140 (context failed earlier in the same JVM; see the first failure of that context) + 14 java.lang.NoSuchMethodException: org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo.getAllPackageDescriptors() + 4 org.hibernate.query.NamedQueryValidationException: Errors in named queries: + 1 java.lang.AssertionError: diff --git a/h8-preview/output/suite-h8-unpinned.txt b/h8-preview/output/suite-h8-unpinned.txt new file mode 100644 index 0000000..dba5919 --- /dev/null +++ b/h8-preview/output/suite-h8-unpinned.txt @@ -0,0 +1,13 @@ +# suite on h8-unpinned hibernate=8.0.0.Beta3 persistence-api=4.0.0-M7 +spring boot parent: 4.1.1 +pins (hibernate-models 1.3.4, jandex 3.6.0): no +test-compile exit code: 0 +tests run: 202 failures: 2 errors: 181 skipped: 0 +test classes with a problem: 67 +distinct final causes (count = failing test methods): + 140 (context failed earlier in the same JVM; see the first failure of that context) + 39 java.lang.ExceptionInInitializerError: Exception java.lang.NoSuchFieldError: Class org.hibernate.models.spi.AnnotationTarget$Kind does not have member field 'org.hibernat + 1 javax.naming.NameAlreadyBoundException: Name jdbc already bound. Use rebind() to override + 1 org.opentest4j.AssertionFailedError: Expected javax.naming.NameAlreadyBoundException to be thrown, but nothing was thrown. + 1 java.lang.AssertionError: + 1 java.lang.NoSuchFieldError: Class org.hibernate.models.spi.AnnotationTarget$Kind does not have member field 'org.hibernate.models.spi.AnnotationTarget$Kind MODULE' diff --git a/h8-preview/pom.xml b/h8-preview/pom.xml new file mode 100644 index 0000000..3d590e4 --- /dev/null +++ b/h8-preview/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + com.ankurm + h8-preview + 1.0.0 + h8-preview + Hibernate ORM 8 beta against Hibernate 7: what breaks, run with plain Hibernate (no Spring) + + + 25 + UTF-8 + + 8.0.0.Beta3 + 4.0.0-M7 + h8 + + + + + org.hibernate.orm + hibernate-core + ${hibernate.version} + + + jakarta.persistence + jakarta.persistence-api + ${persistence.version} + + + com.h2database + h2 + 2.4.240 + + + org.junit.jupiter + junit-jupiter + 6.0.3 + test + + + org.assertj + assertj-core + 3.27.7 + test + + + + + + + maven-surefire-plugin + 3.6.0 + + + ${h.label} + ${hibernate.version} + + + + + + diff --git a/h8-preview/scripts/capture-compile-errors.sh b/h8-preview/scripts/capture-compile-errors.sh new file mode 100755 index 0000000..cf902ee --- /dev/null +++ b/h8-preview/scripts/capture-compile-errors.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Compiles each file in src/broken/ against Hibernate 7.4.5.Final and against 8.0.0.Beta3 and keeps the compiler's own words. +# Then compiles BinaryProbe against 7 and runs it on 8 (and the reverse) to show linkage errors. +set -uo pipefail +cd "$(dirname "$0")/.." +W=$(mktemp -d) +cp_for() { mvn -B -q dependency:build-classpath -Dmdep.outputFile="$W/cp-$1.txt" -Dhibernate.version=$2 -Dpersistence.version=$3 >/dev/null 2>&1; cat "$W/cp-$1.txt"; } +CP7=$(cp_for h7 7.4.5.Final 3.2.0); CP8=$(cp_for h8 8.0.0.Beta3 4.0.0-M7) +{ + echo "# compile-errors (javac --release 25 -Xlint:removal -Xlint:deprecation; files in src/broken/)" + for f in UsesTransformers UsesReplicate StatelessInsertReturnsId QueryVariable TypedQueryVariable; do + for v in h7 h8; do + CP=$([ $v = h7 ] && echo "$CP7" || echo "$CP8") + mkdir -p "$W/out-$v" + echo "--- $f on $v" + javac --release 25 -Xlint:removal -Xlint:deprecation -proc:none -d "$W/out-$v" -sourcepath src/main/java -cp "$CP" src/broken/$f.java 2>&1 \ + | grep -v JAVA_TOOL | sed 's#^src/broken/##' | grep -v -E '^\s*\^?$' || true + done + done +} > output/04-compile-errors.txt +{ + echo "# binary-compatibility (BinaryProbe compiled against one Hibernate, run on the other)" + for pair in "h7 h8" "h8 h7"; do + set -- $pair + CPC=$([ $1 = h7 ] && echo "$CP7" || echo "$CP8"); CPR=$([ $2 = h7 ] && echo "$CP7" || echo "$CP8") + rm -rf "$W/bin-$1"; mkdir -p "$W/bin-$1" + javac --release 25 -proc:none -d "$W/bin-$1" -sourcepath src/main/java -cp "$CPC" src/broken/BinaryProbe.java 2>&1 | grep -v JAVA_TOOL + echo "--- compiled on $1, run on $2" + java -cp "$W/bin-$1:$CPR" BinaryProbe 2>&1 | grep -E '^(createQuery ran|Exception in thread|java\.lang\.)' | cut -c1-230 | head -3 + done +} > output/05-binary-compatibility.txt diff --git a/h8-preview/scripts/capture-facts.sh b/h8-preview/scripts/capture-facts.sh new file mode 100755 index 0000000..269668d --- /dev/null +++ b/h8-preview/scripts/capture-facts.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Facts read from jars: the Jakarta Persistence SPI change and whether Spring implements it. +set -uo pipefail +cd "$(dirname "$0")/.." +W=$(mktemp -d); R="$HOME/.m2/repository" +for a in jakarta.persistence:jakarta.persistence-api:3.2.0 jakarta.persistence:jakarta.persistence-api:4.0.0-M7 org.springframework:spring-orm:7.0.9 org.springframework:spring-orm:7.1.0-M2; do + mvn -B -q dependency:get -Dartifact=$a >/dev/null 2>&1 +done +{ + echo "# persistence-spi (javap on jakarta.persistence.spi.PersistenceUnitInfo, and on Spring's implementation)" + for v in 3.2.0 4.0.0-M7; do + javap -cp "$R/jakarta/persistence/jakarta.persistence-api/$v/jakarta.persistence-api-$v.jar" jakarta.persistence.spi.PersistenceUnitInfo 2>/dev/null \ + | grep -E 'public abstract|public default' | sed 's/^ *//; s/jakarta.persistence.spi.//g; s/java.util.//g; s/java.lang.//g; s/jakarta.persistence.//g' | sort > "$W/spi-$v.txt" + done + echo "--- methods that exist in Jakarta Persistence 4.0.0-M7 but not in 3.2.0" + comm -13 "$W/spi-3.2.0.txt" "$W/spi-4.0.0-M7.txt" | sed 's/^/ /' + for v in 7.0.9 7.1.0-M2; do + echo "--- org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo $v: methods mentioning those names" + all=$(javap -cp "$R/org/springframework/spring-orm/$v/spring-orm-$v.jar" org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo 2>/dev/null | grep -c '(' || true) + n=$(javap -cp "$R/org/springframework/spring-orm/$v/spring-orm-$v.jar" org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo 2>/dev/null | grep -c -E 'AllPackageDescriptors|DefaultToOneFetchType|AllClassNames|ModuleDescriptors|ManagedPackageDescriptors' || true) + echo " class found, $all methods and constructors listed; matches for the six new names: $n" + done +} > output/06-persistence-spi.txt +{ + echo "# dependency-versions (hibernate-core 8.0.0.Beta3 POM: what it declares)" + curl -s https://repo1.maven.org/maven2/org/hibernate/orm/hibernate-core/8.0.0.Beta3/hibernate-core-8.0.0.Beta3.pom \ + | grep -B1 -A2 -E '(hibernate-models|jakarta.persistence-api|byte-buddy|jandex)' | grep -E 'artifactId|version' | sed 's/^ *//' | paste -sd' ' - | sed 's##\n#g' | sed 's/^ *//' | grep -v '^$' +} > output/07-hibernate-core-pom.txt diff --git a/h8-preview/scripts/run-all.sh b/h8-preview/scripts/run-all.sh new file mode 100755 index 0000000..531fb04 --- /dev/null +++ b/h8-preview/scripts/run-all.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Regenerates every file under output/. Needs JDK 25, Maven 3.9 and network access to Maven Central. +# ./scripts/run-all.sh +# 01-03: the plain-Hibernate tests on 8.0.0.Beta3, then on 7.4.5.Final (mvn clean between, or stale classes link wrongly) +# 04-07: javac / javap / POM facts. suite-*.txt: the whole ../src suite in a scratch copy, four ways (slow, ~10 minutes). +set -euo pipefail +cd "$(dirname "$0")/.." +mvn -B clean test +mvn -B clean test -Dhibernate.version=7.4.5.Final -Dpersistence.version=3.2.0 -Dh.label=h7 +./scripts/capture-compile-errors.sh +./scripts/capture-facts.sh +./scripts/run-suite.sh h7 +./scripts/run-suite.sh h8-unpinned 8.0.0.Beta3 4.0.0-M7 no +./scripts/run-suite.sh h8-pinned 8.0.0.Beta3 4.0.0-M7 yes +./scripts/run-suite.sh h8-boot42 8.0.0.Beta3 4.0.0-M7 yes 4.2.0-M2 +ls -1 output diff --git a/h8-preview/scripts/run-suite.sh b/h8-preview/scripts/run-suite.sh new file mode 100755 index 0000000..10fd82a --- /dev/null +++ b/h8-preview/scripts/run-suite.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Runs the whole hibernate-demo suite (../src) against a chosen Hibernate / Jakarta Persistence pair in a +# scratch copy, and writes a plain-text summary. The original tree is never touched. +# +# ./scripts/run-suite.sh