HHH-8487 - Auto-flush on JPA native SQL query

This commit is contained in:
Steve Ebersole 2013-09-11 09:18:04 -05:00
parent 05dcb8f5c1
commit c6fbd4be4c
4 changed files with 87 additions and 15 deletions

View File

@ -51,8 +51,7 @@ import org.hibernate.jpa.graph.internal.EntityGraphImpl;
*/ */
public class EntityManagerImpl extends AbstractEntityManagerImpl implements SessionOwner { public class EntityManagerImpl extends AbstractEntityManagerImpl implements SessionOwner {
public static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class, public static final EntityManagerMessageLogger LOG = HEMLogging.messageLogger( EntityManagerImpl.class.getName() );
EntityManagerImpl.class.getName());
protected Session session; protected Session session;
protected boolean open; protected boolean open;

View File

@ -49,6 +49,7 @@ import org.hibernate.CacheMode;
import org.hibernate.FlushMode; import org.hibernate.FlushMode;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.SQLQuery;
import org.hibernate.TypeMismatchException; import org.hibernate.TypeMismatchException;
import org.hibernate.engine.query.spi.NamedParameterDescriptor; import org.hibernate.engine.query.spi.NamedParameterDescriptor;
import org.hibernate.engine.query.spi.OrdinalParameterDescriptor; import org.hibernate.engine.query.spi.OrdinalParameterDescriptor;
@ -382,6 +383,7 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
@SuppressWarnings({ "unchecked", "RedundantCast" }) @SuppressWarnings({ "unchecked", "RedundantCast" })
public List<X> getResultList() { public List<X> getResultList() {
getEntityManager().checkOpen( true ); getEntityManager().checkOpen( true );
beforeQuery();
try { try {
return query.list(); return query.list();
} }
@ -396,10 +398,33 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
} }
} }
/**
* 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 @Override
@SuppressWarnings({ "unchecked", "RedundantCast" }) @SuppressWarnings({ "unchecked", "RedundantCast" })
public X getSingleResult() { public X getSingleResult() {
getEntityManager().checkOpen( true ); getEntityManager().checkOpen( true );
beforeQuery();
try { try {
final List<X> result = query.list(); final List<X> result = query.list();

View File

@ -35,6 +35,8 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.hibernate.jpa.test.metamodel.AbstractMetamodelSpecificTest; 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.Customer_;
import org.hibernate.jpa.test.metamodel.Order; import org.hibernate.jpa.test.metamodel.Order;
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(); 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<CreditCard> criteriaQuery = builder.createQuery( CreditCard.class );
Root<CreditCard> root = criteriaQuery.from( CreditCard.class );
criteriaQuery.where( builder.isFalse( root.get( CreditCard_.approved ) ) );
em.createQuery( criteriaQuery ).getResultList();
}
{
CriteriaQuery<Order> criteriaQuery = builder.createQuery( Order.class );
Root<Order> 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();
}
} }

View File

@ -29,6 +29,7 @@ import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.Parameter; import javax.persistence.Parameter;
import javax.persistence.Query; import javax.persistence.Query;
@ -38,10 +39,15 @@ import javax.persistence.Tuple;
import org.junit.Test; import org.junit.Test;
import org.hibernate.Hibernate; 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.BaseEntityManagerFunctionalTestCase;
import org.hibernate.jpa.test.Distributor; import org.hibernate.jpa.test.Distributor;
import org.hibernate.jpa.test.Item; import org.hibernate.jpa.test.Item;
import org.hibernate.jpa.test.Wallet; import org.hibernate.jpa.test.Wallet;
import org.hibernate.stat.Statistics;
import junit.framework.Assert;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
@ -56,6 +62,22 @@ import static org.junit.Assert.fail;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class QueryTest extends BaseEntityManagerFunctionalTestCase { 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 @Test
@TestForIssue( jiraKey = "HHH-7192" ) @TestForIssue( jiraKey = "HHH-7192" )
public void testTypedManipulationQueryError() { public void testTypedManipulationQueryError() {
@ -300,8 +322,13 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
assertTrue( em.contains( item ) ); assertTrue( em.contains( item ) );
em.getTransaction().commit(); em.getTransaction().commit();
Statistics stats = em.getEntityManagerFactory().unwrap( SessionFactoryImplementor.class ).getStatistics();
stats.clear();
assertEquals( 0, stats.getFlushCount() );
em.getTransaction().begin(); em.getTransaction().begin();
item = (Item) em.createNativeQuery( "select * from Item", Item.class ).getSingleResult(); item = (Item) em.createNativeQuery( "select * from Item", Item.class ).getSingleResult();
assertEquals( 1, stats.getFlushCount() );
assertNotNull( item ); assertNotNull( item );
assertEquals( "Micro$oft mouse", item.getDescr() ); assertEquals( "Micro$oft mouse", item.getDescr() );
em.remove( item ); em.remove( item );
@ -590,14 +617,18 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
//success //success
} }
assertTrue( "thrown IllegalArgumentException should of caused transaction to be marked for rollback only", assertTrue(
true == em.getTransaction().getRollbackOnly() ); "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 em.getTransaction().rollback(); // HHH-8442 changed to rollback since thrown ISE causes
// transaction to be marked for rollback only. // transaction to be marked for rollback only.
// No need to remove entity since it was rolled back. // No need to remove entity since it was rolled back.
assertNull( "entity should not of been saved to database since IllegalArgumentException should of" + assertNull(
"caused transaction to be marked for rollback only", em.find(Item.class, item.getName())); "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(); em.close();
} }
@ -652,13 +683,4 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
em.close(); em.close();
} }
@Override
public Class[] getAnnotatedClasses() {
return new Class[]{
Item.class,
Distributor.class,
Wallet.class
};
}
} }