Add migration-5-to-7 module: Hibernate 5.6.15 vs 7.4.14 jar diff, compile checks, schema defaults and cheat-sheet snippets

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YPgGH6aMbSLANL3E2HHmS6
This commit is contained in:
2026-10-25 19:17:24 +00:00
co-authored by Claude Sonnet 5.5
parent 357ba84e5c
commit aea104f021
57 changed files with 5187 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ankurm</groupId>
<artifactId>migration-h5</artifactId>
<version>1.0.0</version>
<name>migration-h5</name>
<description>The "before" side: Hibernate ORM 5.6.15.Final with javax.persistence, run against in-memory H2</description>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hibernate.version>5.6.15.Final</hibernate.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Hibernate 5.6.15 pulls Byte Buddy 1.12.18, which does not know the JDK this runs on; a current one does -->
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.17.7</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<!-- note the group id: org.hibernate here, org.hibernate.orm from 6.0 on -->
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.4.240</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>6.1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.27.7</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.6.0</version>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,68 @@
package com.ankurm.migration;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.Serializable;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.junit.jupiter.api.Test;
/** The same probes as the Hibernate 7 module, run on 5.6.15.Final. Output goes to ../output. */
class BaselineTest {
private static final String V = "5.6.15";
@Test
void schemaDefaults() {
try (Db db = new Db(Probe.class); Transcript t = new Transcript("06-schema-" + V)) {
t.line("Hibernate " + V + ", H2, @GeneratedValue with no strategy");
t.line("-- sequences (name, increment)");
List<String> seq = db.jdbc("select sequence_name, increment from information_schema.sequences order by 1");
seq.forEach(t::line);
t.line("-- columns of PROBE (name, type)");
List<String> cols = db.jdbc("select column_name, data_type from information_schema.columns where table_name = 'PROBE' order by column_name");
cols.forEach(t::line);
assertThat(seq).isNotEmpty();
assertThat(cols).hasSize(11);
}
}
@Test
void legacyQueries() {
try (Db db = new Db(Probe.class); Transcript t = new Transcript("07-legacy-queries-" + V)) {
t.line("Hibernate " + V);
try (Session s = db.sessionFactory.openSession()) {
s.beginTransaction();
Serializable id = s.save(new Probe("alpha"));
t.line("session.save(...) returned: " + id + " (" + id.getClass().getSimpleName() + ")");
s.getTransaction().commit();
assertThat(id).isNotNull();
}
try (Session s = db.sessionFactory.openSession()) {
String ordinal = outcome(() -> s.createQuery("from Probe p where p.name = ?1").setParameter(1, "alpha").list());
t.line("HQL with ?1 : " + ordinal);
String bare = outcome(() -> s.createQuery("from Probe p where p.name = ?").setParameter(0, "alpha").list());
t.line("HQL with a bare ? : " + bare);
String enumInt = outcome(() -> s.createQuery("from Probe p where p.byOrdinal = 0").list());
t.line("HQL enum compared with 0 : " + enumInt);
String criteria = outcome(() -> s.createCriteria(Probe.class).add(Restrictions.eq("name", "alpha")).list());
t.line("session.createCriteria(...) : " + criteria);
assertThat(ordinal).startsWith("1 row");
assertThat(bare).startsWith("failed");
assertThat(enumInt).startsWith("0 row");
assertThat(criteria).startsWith("1 row");
}
}
}
static String outcome(java.util.function.Supplier<List<?>> query) {
try {
return query.get().size() + " row(s)";
} catch (RuntimeException e) {
String msg = String.valueOf(e.getMessage()).lines().findFirst().orElse("");
return "failed: " + e.getClass().getSimpleName() + ": " + (msg.length() > 140 ? msg.substring(0, 140) + "..." : msg);
}
}
}
@@ -0,0 +1,66 @@
package com.ankurm.migration;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/** Plain-Hibernate bootstrap against in-memory H2. */
final class Db implements AutoCloseable {
private static final AtomicInteger SEQ = new AtomicInteger();
final SessionFactory sessionFactory;
private final String url;
Db(Class<?>... entities) {
this.url = "jdbc:h2:mem:mig" + SEQ.incrementAndGet() + ";DB_CLOSE_DELAY=-1";
StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.applySetting("hibernate.connection.url", url)
.applySetting("hibernate.connection.username", "sa")
.applySetting("hibernate.connection.password", "")
.applySetting("hibernate.hbm2ddl.auto", "create-drop")
.build();
MetadataSources sources = new MetadataSources(registry);
for (Class<?> e : entities) {
sources.addAnnotatedClass(e);
}
this.sessionFactory = sources.buildMetadata().buildSessionFactory();
}
/** Raw JDBC against the same in-memory database; columns joined with a space. */
List<String> jdbc(String query) {
List<String> rows = new ArrayList<>();
try (Connection c = DriverManager.getConnection(url, "sa", "");
Statement st = c.createStatement();
ResultSet rs = st.executeQuery(query)) {
int n = rs.getMetaData().getColumnCount();
while (rs.next()) {
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= n; i++) {
if (i > 1) {
sb.append(' ');
}
sb.append(rs.getString(i));
}
rows.add(sb.toString());
}
} catch (SQLException e) {
throw new IllegalStateException(e);
}
return rows;
}
@Override
public void close() {
sessionFactory.close();
}
}
@@ -0,0 +1,39 @@
package com.ankurm.migration;
import static org.assertj.core.api.Assertions.assertThat;
import java.nio.file.Files;
import java.nio.file.Path;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.jupiter.api.Test;
/** Creates the schema Hibernate 5.6.15 generates, in an H2 file database that the Hibernate 7 module opens later. */
class LegacySchemaTest {
@Test
void createSchemaFile() throws Exception {
Path dir = Path.of("..", "target", "legacy-db");
Files.createDirectories(dir);
for (var f : dir.toFile().listFiles()) {
f.delete();
}
StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.applySetting("hibernate.connection.url", "jdbc:h2:file:" + dir.toAbsolutePath().normalize() + "/probe")
.applySetting("hibernate.connection.username", "sa")
.applySetting("hibernate.connection.password", "")
.applySetting("hibernate.hbm2ddl.auto", "create")
.build();
try (SessionFactory sf = new MetadataSources(registry).addAnnotatedClass(Probe.class).buildMetadata().buildSessionFactory();
Session s = sf.openSession()) {
s.beginTransaction();
s.persist(new Probe("legacy row"));
s.getTransaction().commit();
}
assertThat(Files.exists(dir.resolve("probe.mv.db"))).isTrue();
}
}
@@ -0,0 +1,46 @@
package com.ankurm.migration;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.UUID;
/** One field of each type whose default column mapping is worth comparing, and a generated id. */
@Entity
public class Probe {
@Id
@GeneratedValue
Long id;
String name;
boolean active;
Instant createdAt;
LocalDateTime occurredAt;
UUID uuid;
Duration span;
BigDecimal price;
byte[] payload;
@Enumerated(EnumType.ORDINAL)
Colour byOrdinal;
@Enumerated(EnumType.STRING)
Colour byName;
public enum Colour { RED, GREEN }
public Probe() {
}
Probe(String name) {
this.name = name;
}
}
@@ -0,0 +1,33 @@
package com.ankurm.migration;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
/** Collects lines and writes them to output/NN-name.txt when closed. The tests assert what they print. */
final class Transcript implements AutoCloseable {
private final Path file;
private final List<String> lines = new ArrayList<>();
Transcript(String name) {
this.file = Path.of("..", "output", name + ".txt");
}
Transcript line(String s) {
lines.add(s);
return this;
}
@Override
public void close() {
try {
Files.createDirectories(file.getParent());
Files.write(file, lines);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}