Add json-columns module: JSON columns in Hibernate 7.4.14 (@JdbcTypeCode(SqlTypes.JSON)), JSONB querying, GIN/expression indexes, dirty checking, Jackson 3 vs 2 on PostgreSQL 18.6 and MySQL 8.0.46

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017ic4b6Kg9RWSTJzyz6KP9X
This commit is contained in:
Claude
2026-10-30 17:00:46 +00:00
parent 98f4ed7411
commit b83cc9988e
51 changed files with 3178 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# Runs FormatMapperProbe under five classpaths and writes output/09-format-mapper-matrix.txt.
# Needs PostgreSQL up (scripts/start-databases.sh) and `mvn test-compile` done.
set -uo pipefail
cd "$(dirname "$0")/.."
OUT=output/09-format-mapper-matrix.txt
mkdir -p output target/cp
mvn -B -q test-compile >/dev/null 2>&1
cp_for() { # name, extra mvn args...
local name="$1"; shift
mvn -B -q dependency:build-classpath -Dmdep.includeScope=test -Dmdep.outputFile="target/cp/$name.txt" "$@" >/dev/null 2>&1
}
drop() { tr ':' '\n' < "target/cp/$1.txt" | grep -v -E "$2" | paste -sd: - > "target/cp/$3.txt"; }
cp_for j3 # Jackson 3 only: what Spring Boot 4 gives you
cp_for j2j3 -Pjackson2 # Jackson 2 and Jackson 3 side by side
cp_for j2j3t -Pjackson2,jsr310 # ... plus jackson-datatype-jsr310 for Jackson 2
drop j2j3 '/tools/jackson/' j2 # Jackson 2 only
drop j2j3t '/tools/jackson/' j2t # Jackson 2 only, with jsr310
drop j3 '/tools/jackson/|/com/fasterxml/jackson/' none # no Jackson at all
run() { # label, classpath-name, setting
local label="$1" cpname="$2" setting="${3:-}"
echo "== $label"
java -cp "target/test-classes:target/classes:$(cat target/cp/$cpname.txt)" com.ankurm.jsoncolumns.FormatMapperProbe "$setting" 2>&1 \
| grep -E '^(setting|on classpath|mapper|stored|read back|FAILED)'
echo
}
{
echo "# Hibernate $(grep -o 'hibernate-core-[0-9.A-Za-z]*' target/cp/j3.txt | head -1)"
run "A. Jackson 3 only, setting not set" j3
run "B. Jackson 2 + Jackson 3, setting not set" j2j3
run "C. Jackson 2 only, setting not set" j2
run "D. no Jackson at all, setting not set" none
run "E. Jackson 2 + Jackson 3, setting = jackson (Jackson 2)" j2j3 jackson
run "F. Jackson 3 only, setting = jackson (Jackson 2 absent)" j3 jackson
run "G. Jackson 2 + Jackson 3, setting = jackson3" j2j3 jackson3
run "H. Jackson 2 + jackson-datatype-jsr310, setting not set" j2t
run "I. Jackson 2 + Jackson 3 + jsr310, setting not set" j2j3t
} > "$OUT"
cat "$OUT"
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Writes output/12-javap-deepcopy.txt: how Hibernate takes the snapshot it later compares with the live JSON attribute.
set -euo pipefail
cd "$(dirname "$0")/.."
mkdir -p output
JAR="$(mvn -B -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout 2>/dev/null | tr ':' '\n' | grep 'hibernate-core-')"
{
echo "# $(basename "$JAR")"
echo
echo "\$ javap -p -c -cp $(basename "$JAR") org.hibernate.type.descriptor.java.spi.FormatMapperBasedJavaType (deepCopy only)"
javap -p -c -cp "$JAR" org.hibernate.type.descriptor.java.spi.FormatMapperBasedJavaType 2>&1 | grep -v '^Picked up' \
| sed -n '/ T deepCopy(T);/,/Serializable disassemble/p' | grep -v 'Serializable disassemble'
echo
echo "\$ javap -p -c -cp $(basename "$JAR") org.hibernate.type.descriptor.java.AbstractJavaType (areEqual only)"
javap -p -c -cp "$JAR" org.hibernate.type.descriptor.java.AbstractJavaType 2>&1 | grep -v '^Picked up' \
| sed -n '/boolean areEqual(T, T);/,/ireturn/p'
} > output/12-javap-deepcopy.txt
cat output/12-javap-deepcopy.txt
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
# Writes output/13-maven-facts.txt: release dates and metadata read straight from Maven Central (retries on HTTP 429).
set -uo pipefail
cd "$(dirname "$0")/.."
mkdir -p output
M=https://repo1.maven.org/maven2
get() { # url -> body (retry on 429/empty)
for i in 1 2 3 4 5 6; do
body="$(curl -sS --fail "$1" 2>/dev/null)" && [ -n "$body" ] && { printf '%s' "$body"; return 0; }
sleep 20
done
return 1
}
head_lm() { # url -> Last-Modified
for i in 1 2 3 4 5 6; do
lm="$(curl -sSI "$1" 2>/dev/null | tr -d '\r' | grep -i '^last-modified:' | cut -d' ' -f2-)" && [ -n "$lm" ] && { echo "$lm"; return 0; }
sleep 20
done
echo "unavailable"
}
meta() { # label path
local x; x="$(get "$M/$2/maven-metadata.xml")" || { echo "$1: metadata unavailable"; return; }
echo "$1: release=$(grep -o '<release>[^<]*' <<<"$x" | cut -d'>' -f2) lastUpdated=$(grep -o '<lastUpdated>[^<]*' <<<"$x" | cut -d'>' -f2)"
sleep 3
}
{
echo "# Maven Central facts, read $(date -u +%Y-%m-%d)"
echo
echo "# maven-metadata.xml: <release> and <lastUpdated> (yyyyMMddHHmmss, UTC)"
meta "com.vladmihalcea:hibernate-types-60 " com/vladmihalcea/hibernate-types-60
meta "com.vladmihalcea:hibernate-types-55 " com/vladmihalcea/hibernate-types-55
meta "com.vladmihalcea:hibernate-types-5 " com/vladmihalcea/hibernate-types-5
meta "io.hypersistence:hypersistence-utils-hibernate-71" io/hypersistence/hypersistence-utils-hibernate-71
meta "io.hypersistence:hypersistence-utils-hibernate-73" io/hypersistence/hypersistence-utils-hibernate-73
echo
echo "# jar Last-Modified on Central (upload time)"
for j in org/hibernate/orm/hibernate-core/7.4.14.Final/hibernate-core-7.4.14.Final.jar \
org/springframework/boot/spring-boot/4.1.2/spring-boot-4.1.2.jar \
io/hypersistence/hypersistence-utils-hibernate-73/3.16.0/hypersistence-utils-hibernate-73-3.16.0.jar; do
echo "$(basename "$j"): $(head_lm "$M/$j")"; sleep 3
done
echo
echo "# hibernate.version that hypersistence-utils-hibernate-73:3.16.0 was built against (its pom)"
get "$M/io/hypersistence/hypersistence-utils-hibernate-73/3.16.0/hypersistence-utils-hibernate-73-3.16.0.pom" | grep -E '^\s*<hibernate.version>' | sed 's/^ *//'
echo
echo "# newest Jackson 3 databind on Central"
get "$M/tools/jackson/core/jackson-databind/maven-metadata.xml" | grep -E '<release>' | sed 's/^ *//'
} > output/13-maven-facts.txt
cat output/13-maven-facts.txt
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Regenerates every file under output/. Needs JDK 25, Maven 3.9 with access to Maven Central, and the two database servers
# from scripts/start-databases.sh (PostgreSQL on 55432, MySQL on 53306; override with PG_URL / MYSQL_URL).
set -euo pipefail
cd "$(dirname "$0")/.."
mvn -B -q clean >/dev/null
./scripts/versions.sh >/dev/null
./scripts/maven-facts.sh >/dev/null # release dates read from Maven Central
./scripts/javap.sh >/dev/null # deepCopy / areEqual bytecode behind the dirty-checking results
mvn -B test # A01-A09, A11: default classpath (Jackson 3 only)
mvn -B test -Phypersistence -Dtest='A10*' # hypersistence-utils on a Jackson 3 only classpath
mvn -B test -Phypersistence,jackson2 -Dtest='A10*' # ... and with Jackson 2 added
./scripts/format-mapper-matrix.sh >/dev/null # nine classpath variants of the FormatMapper probe
ls -1 output
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
# Starts the two servers the tests talk to, without Docker. Idempotent. Defaults match Db.java.
# PostgreSQL 18.6 : zonky embedded-postgres-binaries-linux-amd64 18.6.0 from Maven Central (initdb, pg_ctl, postgres only), port 55432
# MySQL 8.0.x : Ubuntu packages mysql-server-core-8.0 + mysql-client-core-8.0, port 53306
# Both refuse to run as root, so this script creates two throwaway OS users (pgj, myj). Run it as root in a disposable sandbox.
set -euo pipefail
BASE="${BASE:-/tmp/work/dbs}"
PGPORT="${PGPORT:-55432}"; MYPORT="${MYPORT:-53306}"
mkdir -p "$BASE/pg" "$BASE/mysql"
id pgj >/dev/null 2>&1 || useradd -m pgj
id myj >/dev/null 2>&1 || useradd -m myj
if [ ! -x "$BASE/pg/pg/bin/postgres" ]; then
curl -sSfL -o "$BASE/pg/pg.jar" \
https://repo1.maven.org/maven2/io/zonky/test/postgres/embedded-postgres-binaries-linux-amd64/18.6.0/embedded-postgres-binaries-linux-amd64-18.6.0.jar
(cd "$BASE/pg" && unzip -o -q pg.jar && mkdir -p pg && tar -xJf postgres-linux-x86_64.txz -C pg)
fi
chmod -R a+rX "$BASE/pg"
if [ ! -f "$BASE/pg/data/PG_VERSION" ]; then
mkdir -p "$BASE/pg/data"; chown pgj "$BASE/pg/data"
su pgj -c "cd /tmp && $BASE/pg/pg/bin/initdb -D $BASE/pg/data -U postgres -A trust -E UTF8 >/dev/null"
fi
su pgj -c "$BASE/pg/pg/bin/pg_ctl -D $BASE/pg/data -o '-p $PGPORT -k /tmp' -l $BASE/pg/data/pg.log -w start" || true
if ! command -v mysqld >/dev/null; then
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends mysql-server-core-8.0 mysql-client-core-8.0
fi
if [ ! -d "$BASE/mysql/data/mysql" ]; then
mkdir -p "$BASE/mysql/data"; chown -R myj "$BASE/mysql"
su myj -c "mysqld --no-defaults --initialize-insecure --datadir=$BASE/mysql/data >/dev/null 2>&1"
fi
chown -R myj "$BASE/mysql"
if ! mysql -h127.0.0.1 -P"$MYPORT" -uroot -e 'select 1' >/dev/null 2>&1; then
su myj -c "nohup mysqld --no-defaults --secure-file-priv=NULL --datadir=$BASE/mysql/data --port=$MYPORT \
--socket=$BASE/mysql/my.sock --mysqlx=OFF --bind-address=127.0.0.1 --pid-file=$BASE/mysql/my.pid \
--log-error=$BASE/mysql/err.log >/dev/null 2>&1 &"
for _ in $(seq 1 40); do mysql -h127.0.0.1 -P"$MYPORT" -uroot -e 'select 1' >/dev/null 2>&1 && break; sleep 1; done
fi
psql -h 127.0.0.1 -p "$PGPORT" -U postgres -Atc 'select version()'
mysql -h127.0.0.1 -P"$MYPORT" -uroot -Ne 'select version()'
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Writes output/00-versions.txt: the exact versions this module ran against.
set -euo pipefail
cd "$(dirname "$0")/.."
mkdir -p output
{
echo "# java -version"
java -version 2>&1 | grep -v '^Picked up'
echo
echo "# resolved libraries (mvn dependency:list, default profile)"
mvn -B -q dependency:list -DincludeScope=test -DoutputFile=/dev/stdout 2>/dev/null \
| grep -E 'hibernate-core|jackson-(databind|core|annotations)|postgresql:|mysql-connector-j|spring-boot:jar|spring-data-jpa|jakarta.persistence-api' | sed 's/^ *//' | sort
echo
echo "# PostgreSQL server"
psql -h "${PGHOST:-127.0.0.1}" -p "${PGPORT:-55432}" -U postgres -Atc 'select version()'
echo
echo "# MySQL server"
mysql -h127.0.0.1 -P"${MYPORT:-53306}" -uroot -Ne 'select version()'
} > output/00-versions.txt
cat output/00-versions.txt