Skip to content

Commit

Permalink
add systematic tests for before native query flushing in JPA and plai…
Browse files Browse the repository at this point in the history
…n Hibernate
  • Loading branch information
gavinking committed Jan 14, 2025
1 parent 7d78b00 commit 453cc01
Show file tree
Hide file tree
Showing 2 changed files with 235 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<Thing> 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<String> 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<Thing> 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<Thing> 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<Thing> 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<Thing> 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<Thing> 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() {
}
}
}
Original file line number Diff line number Diff line change
@@ -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() {
}
}
}

0 comments on commit 453cc01

Please sign in to comment.