From c6fbd4be4c7094823a36c94ab5b0b45d2938ec81 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 11 Sep 2013 09:18:04 -0500 Subject: [PATCH] HHH-8487 - Auto-flush on JPA native SQL query --- .../jpa/internal/EntityManagerImpl.java | 3 +- .../org/hibernate/jpa/internal/QueryImpl.java | 25 ++++++++++ .../test/criteria/basic/PredicateTest.java | 26 ++++++++++ .../hibernate/jpa/test/query/QueryTest.java | 48 ++++++++++++++----- 4 files changed, 87 insertions(+), 15 deletions(-) diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerImpl.java index 59d08af460..c4e0b699f6 100755 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerImpl.java @@ -51,8 +51,7 @@ import org.hibernate.jpa.graph.internal.EntityGraphImpl; */ public class EntityManagerImpl extends AbstractEntityManagerImpl implements SessionOwner { - public static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class, - EntityManagerImpl.class.getName()); + public static final EntityManagerMessageLogger LOG = HEMLogging.messageLogger( EntityManagerImpl.class.getName() ); protected Session session; protected boolean open; diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java index b9cc6f5398..2c56b308f6 100755 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java @@ -49,6 +49,7 @@ import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.LockMode; +import org.hibernate.SQLQuery; import org.hibernate.TypeMismatchException; import org.hibernate.engine.query.spi.NamedParameterDescriptor; import org.hibernate.engine.query.spi.OrdinalParameterDescriptor; @@ -382,6 +383,7 @@ public class QueryImpl extends AbstractQueryImpl implements TypedQuery, @SuppressWarnings({ "unchecked", "RedundantCast" }) public List getResultList() { getEntityManager().checkOpen( true ); + beforeQuery(); try { return query.list(); } @@ -396,10 +398,33 @@ public class QueryImpl extends AbstractQueryImpl implements TypedQuery, } } + /** + * For JPA native SQL queries, we may need to perform a flush before executing the query. + */ + private void beforeQuery() { + final org.hibernate.Query query = getHibernateQuery(); + if ( ! SQLQuery.class.isInstance( query ) ) { + // this need only exists for native SQL queries, not JPQL or Criteria queries (both of which do + // partial auto flushing already). + return; + } + + final SQLQuery sqlQuery = (SQLQuery) query; + if ( sqlQuery.getSynchronizedQuerySpaces() != null && ! sqlQuery.getSynchronizedQuerySpaces().isEmpty() ) { + // The application defined query spaces on the Hibernate native SQLQuery which means the query will already + // perform a partial flush according to the defined query spaces, no need to do a full flush. + return; + } + + // otherwise we need to flush + getEntityManager().flush(); + } + @Override @SuppressWarnings({ "unchecked", "RedundantCast" }) public X getSingleResult() { getEntityManager().checkOpen( true ); + beforeQuery(); try { final List result = query.list(); diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/PredicateTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/PredicateTest.java index 3a70c8665a..1e8e2dc608 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/PredicateTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/basic/PredicateTest.java @@ -35,6 +35,8 @@ import org.junit.Before; import org.junit.Test; import org.hibernate.jpa.test.metamodel.AbstractMetamodelSpecificTest; +import org.hibernate.jpa.test.metamodel.CreditCard; +import org.hibernate.jpa.test.metamodel.CreditCard_; import org.hibernate.jpa.test.metamodel.Customer_; import org.hibernate.jpa.test.metamodel.Order; import org.hibernate.jpa.test.metamodel.Order_; @@ -282,4 +284,28 @@ public class PredicateTest extends AbstractMetamodelSpecificTest { em.close(); } + @Test + public void testExplicitBuilderBooleanHandling() { + // just checking syntax of the resulting query + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + + // note : these may fail on various matrix db jobs depending on how the dialect handles booleans + { + CriteriaQuery criteriaQuery = builder.createQuery( CreditCard.class ); + Root root = criteriaQuery.from( CreditCard.class ); + criteriaQuery.where( builder.isFalse( root.get( CreditCard_.approved ) ) ); + em.createQuery( criteriaQuery ).getResultList(); + } + + { + CriteriaQuery criteriaQuery = builder.createQuery( Order.class ); + Root root = criteriaQuery.from( Order.class ); + criteriaQuery.where( builder.isFalse( root.get( Order_.creditCard ).get( CreditCard_.approved ) ) ); + em.createQuery( criteriaQuery ).getResultList(); + } + + em.getTransaction().commit(); + em.close(); + } } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/query/QueryTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/query/QueryTest.java index 5dc5d55222..cc4eca89bc 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/query/QueryTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/query/QueryTest.java @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; +import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.Parameter; import javax.persistence.Query; @@ -38,10 +39,15 @@ import javax.persistence.Tuple; import org.junit.Test; import org.hibernate.Hibernate; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.jpa.test.Distributor; import org.hibernate.jpa.test.Item; import org.hibernate.jpa.test.Wallet; +import org.hibernate.stat.Statistics; + +import junit.framework.Assert; import org.hibernate.testing.TestForIssue; @@ -56,6 +62,22 @@ import static org.junit.Assert.fail; * @author Steve Ebersole */ public class QueryTest extends BaseEntityManagerFunctionalTestCase { + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { + Item.class, + Distributor.class, + Wallet.class + }; + } + + @Override + @SuppressWarnings("unchecked") + protected void addConfigOptions(Map options) { + super.addConfigOptions( options ); + options.put( AvailableSettings.GENERATE_STATISTICS, "true" ); + } + @Test @TestForIssue( jiraKey = "HHH-7192" ) public void testTypedManipulationQueryError() { @@ -300,8 +322,13 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { assertTrue( em.contains( item ) ); em.getTransaction().commit(); + Statistics stats = em.getEntityManagerFactory().unwrap( SessionFactoryImplementor.class ).getStatistics(); + stats.clear(); + assertEquals( 0, stats.getFlushCount() ); + em.getTransaction().begin(); item = (Item) em.createNativeQuery( "select * from Item", Item.class ).getSingleResult(); + assertEquals( 1, stats.getFlushCount() ); assertNotNull( item ); assertEquals( "Micro$oft mouse", item.getDescr() ); em.remove( item ); @@ -590,14 +617,18 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { catch (IllegalArgumentException e) { //success } - assertTrue( "thrown IllegalArgumentException should of caused transaction to be marked for rollback only", - true == em.getTransaction().getRollbackOnly() ); + assertTrue( + "thrown IllegalArgumentException should of caused transaction to be marked for rollback only", + true == em.getTransaction().getRollbackOnly() + ); em.getTransaction().rollback(); // HHH-8442 changed to rollback since thrown ISE causes // transaction to be marked for rollback only. // No need to remove entity since it was rolled back. - assertNull( "entity should not of been saved to database since IllegalArgumentException should of" + - "caused transaction to be marked for rollback only", em.find(Item.class, item.getName())); + assertNull( + "entity should not of been saved to database since IllegalArgumentException should of" + + "caused transaction to be marked for rollback only", em.find( Item.class, item.getName() ) + ); em.close(); } @@ -652,13 +683,4 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { em.close(); } - - @Override - public Class[] getAnnotatedClasses() { - return new Class[]{ - Item.class, - Distributor.class, - Wallet.class - }; - } }