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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user