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,57 @@
|
||||
<?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-h7</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<name>migration-h7</name>
|
||||
<description>The "after" side: Hibernate ORM 7.4.14.Final with jakarta.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>7.4.14.Final</hibernate.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-core</artifactId>
|
||||
<version>${hibernate.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.persistence</groupId>
|
||||
<artifactId>jakarta.persistence-api</artifactId>
|
||||
<version>3.2.0</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,8 @@
|
||||
package com.ankurm.migration;
|
||||
|
||||
import jakarta.persistence.Embeddable;
|
||||
|
||||
/** A Java record used directly as an embeddable. */
|
||||
@Embeddable
|
||||
public record Address(String street, String city) {
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.ankurm.migration;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
|
||||
@Entity
|
||||
public class Author {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
Long id;
|
||||
|
||||
@NaturalId
|
||||
String email;
|
||||
|
||||
String name;
|
||||
|
||||
public Author() {
|
||||
}
|
||||
|
||||
Author(String email, String name) {
|
||||
this.email = email;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.ankurm.migration;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
|
||||
@Entity
|
||||
public class Book {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
Long id;
|
||||
|
||||
String title;
|
||||
Integer pages;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
Author author;
|
||||
|
||||
public Book() {
|
||||
}
|
||||
|
||||
Book(String title, Integer pages, Author author) {
|
||||
this.title = title;
|
||||
this.pages = pages;
|
||||
this.author = author;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package com.ankurm.migration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.StatelessSession;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Snippets for the cheat sheet. Each test prints what it did to ../output/1N-cheat-*.txt and asserts the same values. */
|
||||
class CheatSheetTest {
|
||||
|
||||
@Test
|
||||
void sessionApi() {
|
||||
try (Db db = new Db(Author.class, Book.class); Transcript t = new Transcript("10-cheat-session-api")) {
|
||||
Long id;
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
s.beginTransaction();
|
||||
Author a = new Author("[email protected]", "Ann");
|
||||
s.persist(a);
|
||||
id = a.id;
|
||||
s.getTransaction().commit();
|
||||
t.line("persist(a) -> id assigned on the object: " + id);
|
||||
}
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
db.clear();
|
||||
Author found = s.find(Author.class, id);
|
||||
t.line("find(Author, id) -> " + found.name + " (" + db.sql.size() + " select)");
|
||||
assertThat(found.name).isEqualTo("Ann");
|
||||
assertThat(db.sql).hasSize(1);
|
||||
}
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
db.clear();
|
||||
Author ref = s.getReference(Author.class, id);
|
||||
t.line("getReference(Author, id) -> initialized: " + Hibernate.isInitialized(ref) + " (" + db.sql.size() + " selects)");
|
||||
assertThat(Hibernate.isInitialized(ref)).isFalse();
|
||||
assertThat(db.sql).isEmpty();
|
||||
}
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
Author detached = new Author("[email protected]", "Ann Smith");
|
||||
detached.id = id;
|
||||
s.beginTransaction();
|
||||
Author managed = s.merge(detached);
|
||||
t.line("merge(detached) -> returns a different instance: " + (managed != detached) + ", name now " + managed.name);
|
||||
s.getTransaction().commit();
|
||||
assertThat(managed).isNotSameAs(detached);
|
||||
assertThat(managed.name).isEqualTo("Ann Smith");
|
||||
}
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
s.beginTransaction();
|
||||
Author extra = new Author("[email protected]", "Bob");
|
||||
s.persist(extra);
|
||||
s.getTransaction().commit();
|
||||
Long bobId = extra.id;
|
||||
List<Author> many = s.findMultiple(Author.class, List.of(id, bobId, 999L));
|
||||
t.line("findMultiple(ids 3) -> " + many.size() + " entries, missing id gives: " + many.get(2));
|
||||
assertThat(many).hasSize(3);
|
||||
assertThat(many.get(2)).isNull();
|
||||
}
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
s.beginTransaction();
|
||||
Author a = s.find(Author.class, id);
|
||||
s.remove(a);
|
||||
s.getTransaction().commit();
|
||||
t.line("remove(a) -> find afterwards: " + s.find(Author.class, id));
|
||||
assertThat(s.find(Author.class, id)).isNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hql() {
|
||||
try (Db db = new Db(Author.class, Book.class); Transcript t = new Transcript("11-cheat-hql")) {
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
s.beginTransaction();
|
||||
Author ann = new Author("[email protected]", "Ann");
|
||||
Author bob = new Author("[email protected]", "Bob");
|
||||
s.persist(ann);
|
||||
s.persist(bob);
|
||||
s.persist(new Book("Alpha", 100, ann));
|
||||
s.persist(new Book("Beta", 300, ann));
|
||||
s.persist(new Book("Gamma", null, bob));
|
||||
s.getTransaction().commit();
|
||||
}
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
List<String> named = s.createSelectionQuery("select b.title from Book b where b.pages > :min order by b.title", String.class)
|
||||
.setParameter("min", 150).getResultList();
|
||||
t.line("named parameter : " + named);
|
||||
assertThat(named).containsExactly("Beta");
|
||||
|
||||
List<String> ordinal = s.createSelectionQuery("select b.title from Book b where b.pages > ?1 order by b.title", String.class)
|
||||
.setParameter(1, 50).getResultList();
|
||||
t.line("?1 parameter : " + ordinal);
|
||||
assertThat(ordinal).containsExactly("Alpha", "Beta");
|
||||
|
||||
List<String> page = s.createSelectionQuery("select b.title from Book b order by b.title limit 2 offset 1", String.class).getResultList();
|
||||
t.line("limit 2 offset 1 in HQL : " + page);
|
||||
assertThat(page).containsExactly("Beta", "Gamma");
|
||||
|
||||
List<String> nulls = s.createSelectionQuery("select b.title from Book b order by b.pages desc nulls last", String.class).getResultList();
|
||||
t.line("order by ... nulls last : " + nulls);
|
||||
assertThat(nulls).containsExactly("Beta", "Alpha", "Gamma");
|
||||
|
||||
Book none = s.createSelectionQuery("from Book b where b.title = 'Nope'", Book.class).getSingleResultOrNull();
|
||||
t.line("getSingleResultOrNull() : " + none);
|
||||
assertThat(none).isNull();
|
||||
|
||||
List<Book> fetched = s.createSelectionQuery("select b from Book b join fetch b.author where b.title = 'Alpha'", Book.class).getResultList();
|
||||
t.line("join fetch : author initialized = " + Hibernate.isInitialized(fetched.get(0).author));
|
||||
assertThat(Hibernate.isInitialized(fetched.get(0).author)).isTrue();
|
||||
|
||||
Long count = s.createSelectionQuery("select count(b) from Book b where b.author.name = :n", Long.class).setParameter("n", "Ann").getSingleResult();
|
||||
t.line("count over an association : " + count);
|
||||
assertThat(count).isEqualTo(2L);
|
||||
}
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
s.beginTransaction();
|
||||
int updated = s.createMutationQuery("update Book b set b.pages = b.pages + 1 where b.pages is not null").executeUpdate();
|
||||
s.getTransaction().commit();
|
||||
t.line("createMutationQuery update: " + updated + " rows");
|
||||
assertThat(updated).isEqualTo(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void statelessSession() {
|
||||
try (Db db = new Db(Author.class); Transcript t = new Transcript("12-cheat-stateless-session")) {
|
||||
try (StatelessSession s = db.sessionFactory.openStatelessSession()) {
|
||||
s.beginTransaction();
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
s.insert(new Author("user" + i + "@example.com", "User " + i));
|
||||
}
|
||||
s.getTransaction().commit();
|
||||
t.line("insert x3 (no persistence context) done");
|
||||
}
|
||||
try (StatelessSession s = db.sessionFactory.openStatelessSession()) {
|
||||
Author a1 = s.get(Author.class, 1L);
|
||||
Author a1again = s.get(Author.class, 1L);
|
||||
t.line("get twice -> same instance: " + (a1 == a1again) + " (a stateless session has no first-level cache)");
|
||||
assertThat(a1).isNotNull();
|
||||
assertThat(a1).isNotSameAs(a1again);
|
||||
t.line("rows in table: " + db.jdbc("select count(*) from Author"));
|
||||
assertThat(db.jdbc("select count(*) from Author")).containsExactly("3");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void naturalId() {
|
||||
try (Db db = new Db(Author.class); Transcript t = new Transcript("13-cheat-natural-id")) {
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
s.beginTransaction();
|
||||
s.persist(new Author("[email protected]", "Ann"));
|
||||
s.getTransaction().commit();
|
||||
}
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
db.clear();
|
||||
Author a = s.bySimpleNaturalId(Author.class).load("[email protected]");
|
||||
t.line("bySimpleNaturalId(...).load -> " + a.name);
|
||||
t.line("sql: " + db.sql.get(0));
|
||||
assertThat(a.name).isEqualTo("Ann");
|
||||
assertThat(db.sql.get(0)).contains("email");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void softDelete() {
|
||||
try (Db db = new Db(Note.class); Transcript t = new Transcript("14-cheat-soft-delete")) {
|
||||
Long id;
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
s.beginTransaction();
|
||||
Note n = new Note("remember");
|
||||
s.persist(n);
|
||||
id = n.id;
|
||||
s.getTransaction().commit();
|
||||
}
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
s.beginTransaction();
|
||||
db.clear();
|
||||
s.remove(s.find(Note.class, id));
|
||||
s.getTransaction().commit();
|
||||
t.line("remove(note) ran: " + db.sql.get(db.sql.size() - 1));
|
||||
assertThat(db.sql.get(db.sql.size() - 1)).startsWith("update");
|
||||
}
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
t.line("find afterwards : " + s.find(Note.class, id));
|
||||
t.line("row still in the table: " + db.jdbc("select * from Note"));
|
||||
assertThat(s.find(Note.class, id)).isNull();
|
||||
assertThat(db.jdbc("select count(*) from Note")).containsExactly("1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void recordEmbeddable() {
|
||||
try (Db db = new Db(Shop.class); Transcript t = new Transcript("15-cheat-record-embeddable")) {
|
||||
Long id;
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
s.beginTransaction();
|
||||
Shop shop = new Shop("Corner", new Address("1 Main St", "Pune"));
|
||||
s.persist(shop);
|
||||
id = shop.id;
|
||||
s.getTransaction().commit();
|
||||
}
|
||||
try (Session s = db.sessionFactory.openSession()) {
|
||||
Shop shop = s.find(Shop.class, id);
|
||||
t.line("loaded: " + shop.address);
|
||||
t.line("columns: " + db.jdbc("select column_name from information_schema.columns where table_name = 'SHOP' order by column_name"));
|
||||
assertThat(shop.address).isEqualTo(new Address("1 Main St", "Pune"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.ankurm.migration;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||
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 List<String> sql = java.util.Collections.synchronizedList(new ArrayList<>());
|
||||
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("jakarta.persistence.jdbc.url", url)
|
||||
.applySetting("jakarta.persistence.jdbc.user", "sa")
|
||||
.applySetting("jakarta.persistence.jdbc.password", "")
|
||||
.applySetting("hibernate.hbm2ddl.auto", "create-drop")
|
||||
.applySetting("hibernate.session_factory.statement_inspector", (StatementInspector) x -> {
|
||||
sql.add(x);
|
||||
return x;
|
||||
})
|
||||
.build();
|
||||
MetadataSources sources = new MetadataSources(registry);
|
||||
for (Class<?> e : entities) {
|
||||
sources.addAnnotatedClass(e);
|
||||
}
|
||||
this.sessionFactory = sources.buildMetadata().buildSessionFactory();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
sql.clear();
|
||||
}
|
||||
|
||||
/** 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,64 @@
|
||||
package com.ankurm.migration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** The same probes as the Hibernate 5 module, run on 7.4.14.Final. Output goes to ../output. */
|
||||
class MigrationProbeTest {
|
||||
|
||||
private static final String V = "7.4.14";
|
||||
|
||||
@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();
|
||||
Probe p = new Probe("alpha");
|
||||
s.persist(p);
|
||||
t.line("session.persist(...) assigned the id: " + p.id + " (" + p.id.getClass().getSimpleName() + ")");
|
||||
s.getTransaction().commit();
|
||||
assertThat(p.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);
|
||||
assertThat(ordinal).startsWith("1 row");
|
||||
assertThat(bare).startsWith("failed");
|
||||
assertThat(enumInt).startsWith("0 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,23 @@
|
||||
package com.ankurm.migration;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
|
||||
@Entity
|
||||
@SoftDelete
|
||||
public class Note {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
Long id;
|
||||
|
||||
String text;
|
||||
|
||||
public Note() {
|
||||
}
|
||||
|
||||
Note(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.ankurm.migration;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.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,26 @@
|
||||
package com.ankurm.migration;
|
||||
|
||||
import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
@Entity
|
||||
public class Shop {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
Long id;
|
||||
|
||||
String name;
|
||||
|
||||
@Embedded
|
||||
Address address;
|
||||
|
||||
public Shop() {
|
||||
}
|
||||
|
||||
Shop(String name, Address address) {
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.ankurm.migration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
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;
|
||||
|
||||
/** Opens the database the Hibernate 5.6.15 module created, with the same entity, and asks Hibernate 7 to validate it. */
|
||||
class ValidateLegacySchemaTest {
|
||||
|
||||
@Test
|
||||
void validateAndRunAgainstSchemaCreatedByHibernate5() throws Exception {
|
||||
Path dir = Path.of("..", "target", "legacy-db");
|
||||
assumeTrue(Files.exists(dir.resolve("probe.mv.db")), "run the h5 module first (scripts/run-all.sh does)");
|
||||
String url = "jdbc:h2:file:" + dir.toAbsolutePath().normalize() + "/probe";
|
||||
try (Transcript t = new Transcript("08-validate-against-5.6.15-schema-7.4.14")) {
|
||||
t.line("Hibernate 7.4.14 opening the H2 file database written by Hibernate 5.6.15 (same Probe entity)");
|
||||
String validate = open(url, "validate");
|
||||
t.line("hbm2ddl.auto=validate: " + validate);
|
||||
assertThat(validate).startsWith("failed");
|
||||
String none = open(url, "none");
|
||||
t.line("hbm2ddl.auto=none : " + none);
|
||||
}
|
||||
}
|
||||
|
||||
private static String open(String url, String mode) {
|
||||
StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
|
||||
.applySetting("jakarta.persistence.jdbc.url", url)
|
||||
.applySetting("jakarta.persistence.jdbc.user", "sa")
|
||||
.applySetting("jakarta.persistence.jdbc.password", "")
|
||||
.applySetting("hibernate.hbm2ddl.auto", mode)
|
||||
.build();
|
||||
try (SessionFactory sf = new MetadataSources(registry).addAnnotatedClass(Probe.class).buildMetadata().buildSessionFactory()) {
|
||||
try (var s = sf.openSession()) {
|
||||
Probe p = s.createSelectionQuery("from Probe", Probe.class).getResultList().get(0);
|
||||
return "opened; loaded the row written by 5.6.15: name=" + p.name + ", uuid=" + p.uuid;
|
||||
} catch (RuntimeException e) {
|
||||
return "opened, but the first query failed: " + e.getClass().getSimpleName() + ": " + first(e);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
return "failed: " + e.getClass().getSimpleName() + ": " + first(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String first(Throwable e) {
|
||||
String m = String.valueOf(e.getMessage()).lines().findFirst().orElse("");
|
||||
return m.length() > 220 ? m.substring(0, 220) + "..." : m;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user