diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java index fd945ac0a9e6..0d552a889409 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java @@ -14,6 +14,7 @@ import java.util.Properties; import java.util.function.BiConsumer; +import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.MappingException; @@ -584,6 +585,11 @@ private IntegralDataTypeHolder nextValue( else { final int defaultValue = storeLastUsedValue ? 0 : 1; value.initialize( selectRS, defaultValue ); + if ( selectRS.wasNull() ) { + throw new HibernateException( + String.format( "%s for %s '%s' is null", valueColumnName, segmentColumnName, + segmentValue ) ); + } } selectRS.close(); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/idgenerator/TableGeneratorNextValNullTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/idgenerator/TableGeneratorNextValNullTest.java new file mode 100644 index 000000000000..ae2cf829db25 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/idgenerator/TableGeneratorNextValNullTest.java @@ -0,0 +1,63 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.schemaupdate.idgenerator; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +import jakarta.persistence.TableGenerator; +import org.hibernate.HibernateException; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@JiraKey(value = "HHH-8535") +@DomainModel(annotatedClasses = {TableGeneratorNextValNullTest.Author.class}) +@SessionFactory +public class TableGeneratorNextValNullTest { + + @AfterAll + public void dropTestData(SessionFactoryScope scope) { + scope.dropData(); + } + + @Test + void hhh8535Test(SessionFactoryScope scope) { + + // This situation can only happen via human being or bad migration/clone script. + // Simulate this record being updated post table generation. + scope.inTransaction( session -> { + session.createNativeMutationQuery( + "UPDATE generator SET next_val = null where sequence_name = 'Author'" + ).executeUpdate(); + } ); + + HibernateException hibernateException = assertThrows( HibernateException.class, + () -> scope.inTransaction( session -> { + Author author = new Author(); + session.persist( author ); + } ) ); + + assertEquals( "next_val for sequence_name 'Author' is null", hibernateException.getMessage() ); + } + + @Entity(name = "Author") + public static class Author { + + @Id + @GeneratedValue(strategy = GenerationType.TABLE, generator = "generator") + @TableGenerator(name = "generator", table = "generator") + long id; + } + +}