Skip to content

Commit c6fbd4b

Browse files
committed
HHH-8487 - Auto-flush on JPA native SQL query
1 parent 05dcb8f commit c6fbd4b

File tree

4 files changed

+87
-15
lines changed

4 files changed

+87
-15
lines changed

hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerImpl.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@
5151
*/
5252
public class EntityManagerImpl extends AbstractEntityManagerImpl implements SessionOwner {
5353

54-
public static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class,
55-
EntityManagerImpl.class.getName());
54+
public static final EntityManagerMessageLogger LOG = HEMLogging.messageLogger( EntityManagerImpl.class.getName() );
5655

5756
protected Session session;
5857
protected boolean open;

hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.hibernate.FlushMode;
5050
import org.hibernate.HibernateException;
5151
import org.hibernate.LockMode;
52+
import org.hibernate.SQLQuery;
5253
import org.hibernate.TypeMismatchException;
5354
import org.hibernate.engine.query.spi.NamedParameterDescriptor;
5455
import org.hibernate.engine.query.spi.OrdinalParameterDescriptor;
@@ -382,6 +383,7 @@ protected void applyAliasSpecificLockModeHint(String alias, LockMode lockMode) {
382383
@SuppressWarnings({ "unchecked", "RedundantCast" })
383384
public List<X> getResultList() {
384385
getEntityManager().checkOpen( true );
386+
beforeQuery();
385387
try {
386388
return query.list();
387389
}
@@ -396,10 +398,33 @@ public List<X> getResultList() {
396398
}
397399
}
398400

401+
/**
402+
* For JPA native SQL queries, we may need to perform a flush before executing the query.
403+
*/
404+
private void beforeQuery() {
405+
final org.hibernate.Query query = getHibernateQuery();
406+
if ( ! SQLQuery.class.isInstance( query ) ) {
407+
// this need only exists for native SQL queries, not JPQL or Criteria queries (both of which do
408+
// partial auto flushing already).
409+
return;
410+
}
411+
412+
final SQLQuery sqlQuery = (SQLQuery) query;
413+
if ( sqlQuery.getSynchronizedQuerySpaces() != null && ! sqlQuery.getSynchronizedQuerySpaces().isEmpty() ) {
414+
// The application defined query spaces on the Hibernate native SQLQuery which means the query will already
415+
// perform a partial flush according to the defined query spaces, no need to do a full flush.
416+
return;
417+
}
418+
419+
// otherwise we need to flush
420+
getEntityManager().flush();
421+
}
422+
399423
@Override
400424
@SuppressWarnings({ "unchecked", "RedundantCast" })
401425
public X getSingleResult() {
402426
getEntityManager().checkOpen( true );
427+
beforeQuery();
403428
try {
404429
final List<X> result = query.list();
405430

hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/PredicateTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import org.junit.Test;
3636

3737
import org.hibernate.jpa.test.metamodel.AbstractMetamodelSpecificTest;
38+
import org.hibernate.jpa.test.metamodel.CreditCard;
39+
import org.hibernate.jpa.test.metamodel.CreditCard_;
3840
import org.hibernate.jpa.test.metamodel.Customer_;
3941
import org.hibernate.jpa.test.metamodel.Order;
4042
import org.hibernate.jpa.test.metamodel.Order_;
@@ -282,4 +284,28 @@ public void testQuotientConversion() {
282284
em.close();
283285
}
284286

287+
@Test
288+
public void testExplicitBuilderBooleanHandling() {
289+
// just checking syntax of the resulting query
290+
EntityManager em = getOrCreateEntityManager();
291+
em.getTransaction().begin();
292+
293+
// note : these may fail on various matrix db jobs depending on how the dialect handles booleans
294+
{
295+
CriteriaQuery<CreditCard> criteriaQuery = builder.createQuery( CreditCard.class );
296+
Root<CreditCard> root = criteriaQuery.from( CreditCard.class );
297+
criteriaQuery.where( builder.isFalse( root.get( CreditCard_.approved ) ) );
298+
em.createQuery( criteriaQuery ).getResultList();
299+
}
300+
301+
{
302+
CriteriaQuery<Order> criteriaQuery = builder.createQuery( Order.class );
303+
Root<Order> root = criteriaQuery.from( Order.class );
304+
criteriaQuery.where( builder.isFalse( root.get( Order_.creditCard ).get( CreditCard_.approved ) ) );
305+
em.createQuery( criteriaQuery ).getResultList();
306+
}
307+
308+
em.getTransaction().commit();
309+
em.close();
310+
}
285311
}

hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/query/QueryTest.java

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Date;
3030
import java.util.GregorianCalendar;
3131
import java.util.List;
32+
import java.util.Map;
3233
import javax.persistence.EntityManager;
3334
import javax.persistence.Parameter;
3435
import javax.persistence.Query;
@@ -38,10 +39,15 @@
3839
import org.junit.Test;
3940

4041
import org.hibernate.Hibernate;
42+
import org.hibernate.cfg.AvailableSettings;
43+
import org.hibernate.engine.spi.SessionFactoryImplementor;
4144
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
4245
import org.hibernate.jpa.test.Distributor;
4346
import org.hibernate.jpa.test.Item;
4447
import org.hibernate.jpa.test.Wallet;
48+
import org.hibernate.stat.Statistics;
49+
50+
import junit.framework.Assert;
4551

4652
import org.hibernate.testing.TestForIssue;
4753

@@ -56,6 +62,22 @@
5662
* @author Steve Ebersole
5763
*/
5864
public class QueryTest extends BaseEntityManagerFunctionalTestCase {
65+
@Override
66+
public Class[] getAnnotatedClasses() {
67+
return new Class[] {
68+
Item.class,
69+
Distributor.class,
70+
Wallet.class
71+
};
72+
}
73+
74+
@Override
75+
@SuppressWarnings("unchecked")
76+
protected void addConfigOptions(Map options) {
77+
super.addConfigOptions( options );
78+
options.put( AvailableSettings.GENERATE_STATISTICS, "true" );
79+
}
80+
5981
@Test
6082
@TestForIssue( jiraKey = "HHH-7192" )
6183
public void testTypedManipulationQueryError() {
@@ -300,8 +322,13 @@ public void testNativeQueryByEntity() {
300322
assertTrue( em.contains( item ) );
301323
em.getTransaction().commit();
302324

325+
Statistics stats = em.getEntityManagerFactory().unwrap( SessionFactoryImplementor.class ).getStatistics();
326+
stats.clear();
327+
assertEquals( 0, stats.getFlushCount() );
328+
303329
em.getTransaction().begin();
304330
item = (Item) em.createNativeQuery( "select * from Item", Item.class ).getSingleResult();
331+
assertEquals( 1, stats.getFlushCount() );
305332
assertNotNull( item );
306333
assertEquals( "Micro$oft mouse", item.getDescr() );
307334
em.remove( item );
@@ -590,14 +617,18 @@ public void testUnavailableNamedQuery() throws Exception {
590617
catch (IllegalArgumentException e) {
591618
//success
592619
}
593-
assertTrue( "thrown IllegalArgumentException should of caused transaction to be marked for rollback only",
594-
true == em.getTransaction().getRollbackOnly() );
620+
assertTrue(
621+
"thrown IllegalArgumentException should of caused transaction to be marked for rollback only",
622+
true == em.getTransaction().getRollbackOnly()
623+
);
595624
em.getTransaction().rollback(); // HHH-8442 changed to rollback since thrown ISE causes
596625
// transaction to be marked for rollback only.
597626
// No need to remove entity since it was rolled back.
598627

599-
assertNull( "entity should not of been saved to database since IllegalArgumentException should of" +
600-
"caused transaction to be marked for rollback only", em.find(Item.class, item.getName()));
628+
assertNull(
629+
"entity should not of been saved to database since IllegalArgumentException should of" +
630+
"caused transaction to be marked for rollback only", em.find( Item.class, item.getName() )
631+
);
601632
em.close();
602633

603634
}
@@ -652,13 +683,4 @@ public void testTypedScalarQueries() {
652683

653684
em.close();
654685
}
655-
656-
@Override
657-
public Class[] getAnnotatedClasses() {
658-
return new Class[]{
659-
Item.class,
660-
Distributor.class,
661-
Wallet.class
662-
};
663-
}
664686
}

0 commit comments

Comments
 (0)