diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/autoflush/HibernateAutoflushTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/autoflush/HibernateAutoflushTest.java new file mode 100644 index 000000000000..bafd5d430dff --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/autoflush/HibernateAutoflushTest.java @@ -0,0 +1,125 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.jpa.autoflush; + +import jakarta.persistence.Entity; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.Id; +import org.hibernate.query.QueryFlushMode; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SessionFactory +@DomainModel(annotatedClasses = HibernateAutoflushTest.Thing.class) +public class HibernateAutoflushTest { + + @Test void test1(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncate(); + scope.inTransaction( em -> { + em.persist( new Thing( "Widget" ) ); + List resultList = + em.createNativeQuery( "select * from Thing", Thing.class ) + .getResultList(); + // Hibernate does NOT autoflush before a native query + assertEquals( 0, resultList.size() ); + } ); + } + + @Test void test2(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncate(); + scope.inTransaction( em -> { + em.persist( new Thing("Widget") ); + List resultList = + em.createNativeQuery( "select typeOfThing from Thing", String.class ) + .getResultList(); + // Hibernate does NOT autoflush before a native query + assertEquals(0, resultList.size()); + } ); + } + + @Test void test3(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncate(); + scope.inSession( em -> { + em.persist( new Thing("Widget") ); + List resultList = + em.createNativeQuery( "select * from Thing", Thing.class ) + .getResultList(); + // spec says we must NOT flush before native query outside tx + assertEquals(0, resultList.size()); + } ); + } + + @Test void test4(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncate(); + scope.inTransaction( em -> { + em.setFlushMode( FlushModeType.COMMIT ); + em.persist( new Thing("Widget") ); + List resultList = + em.createNativeQuery( "select * from Thing", Thing.class ) + .getResultList(); + // spec says we must NOT flush before native query with FMT.COMMIT + assertEquals(0, resultList.size()); + } ); + } + + @Test void test5(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncate(); + scope.inTransaction( em -> { + em.persist( new Thing("Widget") ); + List resultList = + em.createNativeQuery( "select * from Thing", Thing.class ) + .addSynchronizedQuerySpace( "Thing" ) + .getResultList(); + // we should not flush because user specified that the query touches the table + assertEquals(1, resultList.size()); + } ); + } + + @Test void test6(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncate(); + scope.inTransaction( em -> { + em.persist( new Thing("Widget") ); + List resultList = + em.createNativeQuery( "select * from Thing", Thing.class ) + .addSynchronizedQuerySpace( "XXX" ) + .getResultList(); + // we should not flush because user specified that the query doesn't touch the table + assertEquals(0, resultList.size()); + } ); + } + + @Test void test7(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncate(); + scope.inTransaction( em -> { + em.persist( new Thing( "Widget" ) ); + List resultList = + em.createNativeQuery( "select * from Thing", Thing.class ) + .setQueryFlushMode( QueryFlushMode.FLUSH ) + .getResultList(); + // we should flush because of the QueryFlushMode + assertEquals( 1, resultList.size() ); + } ); + } + + @Entity(name="Thing") + public static class Thing { + @Id + long id; + String typeOfThing; + + public Thing(String typeOfThing) { + this.typeOfThing = typeOfThing; + } + + public Thing() { + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/autoflush/JpaAutoflushTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/autoflush/JpaAutoflushTest.java new file mode 100644 index 000000000000..31f9c1adab3c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/autoflush/JpaAutoflushTest.java @@ -0,0 +1,110 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.jpa.autoflush; + +import jakarta.persistence.Entity; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.Id; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.hibernate.jpa.HibernateHints.HINT_NATIVE_SPACES; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Jpa(annotatedClasses = JpaAutoflushTest.Thing.class) +public class JpaAutoflushTest { + + @Test void test1(EntityManagerFactoryScope scope) { + scope.getEntityManagerFactory().getSchemaManager().truncate(); + scope.inTransaction( em -> { + em.persist( new Thing( "Widget" ) ); + List resultList = + em.createNativeQuery( "select * from Thing", Thing.class ) + .getResultList(); + // spec says we must flush before native query in tx + assertEquals( 1, resultList.size() ); + } ); + } + + @Test void test2(EntityManagerFactoryScope scope) { + scope.getEntityManagerFactory().getSchemaManager().truncate(); + scope.inTransaction( em -> { + em.persist( new Thing("Widget") ); + List resultList = + em.createNativeQuery( "select typeOfThing from Thing", String.class ) + .getResultList(); + // spec says we must flush before native query in tx + assertEquals(1, resultList.size()); + } ); + } + + @Test void test3(EntityManagerFactoryScope scope) { + scope.getEntityManagerFactory().getSchemaManager().truncate(); + scope.inEntityManager( em -> { + em.persist( new Thing("Widget") ); + List resultList = + em.createNativeQuery( "select * from Thing", Thing.class ) + .getResultList(); + // spec says we must NOT flush before native query outside tx + assertEquals(0, resultList.size()); + } ); + } + + @Test void test4(EntityManagerFactoryScope scope) { + scope.getEntityManagerFactory().getSchemaManager().truncate(); + scope.inTransaction( em -> { + em.setFlushMode( FlushModeType.COMMIT ); + em.persist( new Thing("Widget") ); + List resultList = + em.createNativeQuery( "select * from Thing", Thing.class ) + .getResultList(); + // spec says we must NOT flush before native query with FMT.COMMIT + assertEquals(0, resultList.size()); + } ); + } + + @Test void test5(EntityManagerFactoryScope scope) { + scope.getEntityManagerFactory().getSchemaManager().truncate(); + scope.inTransaction( em -> { + em.persist( new Thing("Widget") ); + List resultList = + em.createNativeQuery( "select * from Thing", Thing.class ) + .setHint( HINT_NATIVE_SPACES, "Thing" ) + .getResultList(); + // we should not flush because user specified that the query touches the table + assertEquals(1, resultList.size()); + } ); + } + + @Test void test6(EntityManagerFactoryScope scope) { + scope.getEntityManagerFactory().getSchemaManager().truncate(); + scope.inTransaction( em -> { + em.persist( new Thing("Widget") ); + List resultList = + em.createNativeQuery( "select * from Thing", Thing.class ) + .setHint( HINT_NATIVE_SPACES, "XXX" ) + .getResultList(); + // we should not flush because user specified that the query doesn't touch the table + assertEquals(0, resultList.size()); + } ); + } + + @Entity(name="Thing") + public static class Thing { + @Id + long id; + String typeOfThing; + + public Thing(String typeOfThing) { + this.typeOfThing = typeOfThing; + } + + public Thing() { + } + } +}