HHH-11732 - HHH000352: in StatelessSession on rollback with JDBC batch

This commit is contained in:
Vlad Mihalcea 2017-05-11 16:28:03 +03:00 committed by Gail Badner
parent becc1d7473
commit 1392b43852
8 changed files with 169 additions and 17 deletions

View File

@ -38,8 +38,10 @@ import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.jdbc.WorkExecutor;
import org.hibernate.jdbc.WorkExecutorVisitable;
import org.hibernate.resource.jdbc.ResourceRegistry;
import org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl;
import org.hibernate.resource.jdbc.internal.LogicalConnectionProvidedImpl;
import org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl;
import org.hibernate.resource.jdbc.spi.JdbcSessionOwner;
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
import org.hibernate.resource.transaction.backend.jdbc.spi.JdbcResourceTransaction;
@ -94,13 +96,17 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
JdbcSessionOwner owner) {
this.isUserSuppliedConnection = userSuppliedConnection != null;
final ResourceRegistry resourceRegistry = new ResourceRegistryStandardImpl(
owner.getJdbcSessionContext().getObserver()
);
if ( isUserSuppliedConnection ) {
this.logicalConnection = new LogicalConnectionProvidedImpl( userSuppliedConnection );
this.logicalConnection = new LogicalConnectionProvidedImpl( userSuppliedConnection, resourceRegistry );
}
else {
this.logicalConnection = new LogicalConnectionManagedImpl(
owner.getJdbcConnectionAccess(),
owner.getJdbcSessionContext()
owner.getJdbcSessionContext(),
resourceRegistry
);
}
this.owner = owner;

View File

@ -83,4 +83,14 @@ public class JdbcObserverImpl implements JdbcObserver {
public void jdbcExecuteBatchEnd() {
session.getEventListenerManager().jdbcExecuteBatchEnd();
}
@Override
public void jdbcReleaseRegistryResourcesStart() {
session.getJdbcCoordinator().abortBatch();
}
@Override
public void jdbcReleaseRegistryResourcesEnd() {
}
}

View File

@ -15,9 +15,6 @@ import java.sql.SQLException;
import org.hibernate.ConnectionAcquisitionMode;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.ResourceClosedException;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
@ -48,12 +45,6 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple
private boolean providerDisablesAutoCommit;
public LogicalConnectionManagedImpl(
JdbcConnectionAccess jdbcConnectionAccess,
JdbcSessionContext jdbcSessionContext) {
this( jdbcConnectionAccess, jdbcSessionContext, new ResourceRegistryStandardImpl() );
}
public LogicalConnectionManagedImpl(
JdbcConnectionAccess jdbcConnectionAccess,
JdbcSessionContext jdbcSessionContext,

View File

@ -28,10 +28,6 @@ public class LogicalConnectionProvidedImpl extends AbstractLogicalConnectionImpl
private final boolean initiallyAutoCommit;
private boolean closed;
public LogicalConnectionProvidedImpl(Connection providedConnection) {
this( providedConnection, new ResourceRegistryStandardImpl() );
}
public LogicalConnectionProvidedImpl(Connection providedConnection, ResourceRegistry resourceRegistry) {
this.resourceRegistry = resourceRegistry;
if ( providedConnection == null ) {
@ -89,7 +85,7 @@ public class LogicalConnectionProvidedImpl extends AbstractLogicalConnectionImpl
public LogicalConnectionImplementor makeShareableCopy() {
errorIfClosed();
return new LogicalConnectionProvidedImpl( providedConnection );
return new LogicalConnectionProvidedImpl( providedConnection, new ResourceRegistryStandardImpl() );
}
@Override

View File

@ -25,6 +25,7 @@ import org.hibernate.JDBCException;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.resource.jdbc.ResourceRegistry;
import org.hibernate.resource.jdbc.spi.JdbcObserver;
/**
* @author Steve Ebersole
@ -32,6 +33,8 @@ import org.hibernate.resource.jdbc.ResourceRegistry;
public class ResourceRegistryStandardImpl implements ResourceRegistry {
private static final CoreMessageLogger log = CoreLogging.messageLogger( ResourceRegistryStandardImpl.class );
private final JdbcObserver jdbcObserver;
private final Map<Statement, Set<ResultSet>> xref = new HashMap<Statement, Set<ResultSet>>();
private final Set<ResultSet> unassociatedResultSets = new HashSet<ResultSet>();
@ -41,6 +44,14 @@ public class ResourceRegistryStandardImpl implements ResourceRegistry {
private Statement lastQuery;
public ResourceRegistryStandardImpl() {
this( null );
}
public ResourceRegistryStandardImpl(JdbcObserver jdbcObserver) {
this.jdbcObserver = jdbcObserver;
}
@Override
public boolean hasRegisteredResources() {
return hasRegistered( xref )
@ -285,6 +296,10 @@ public class ResourceRegistryStandardImpl implements ResourceRegistry {
public void releaseResources() {
log.trace( "Releasing JDBC resources" );
if ( jdbcObserver != null ) {
jdbcObserver.jdbcReleaseRegistryResourcesStart();
}
for ( Map.Entry<Statement, Set<ResultSet>> entry : xref.entrySet() ) {
if ( entry.getValue() != null ) {
closeAll( entry.getValue() );
@ -331,6 +346,9 @@ public class ResourceRegistryStandardImpl implements ResourceRegistry {
nclobs.clear();
}
if ( jdbcObserver != null ) {
jdbcObserver.jdbcReleaseRegistryResourcesEnd();
}
}
private boolean hasRegistered(Map resource) {

View File

@ -27,4 +27,8 @@ public interface JdbcObserver {
public void jdbcExecuteBatchStart();
public void jdbcExecuteBatchEnd();
default public void jdbcReleaseRegistryResourcesStart() {}
default public void jdbcReleaseRegistryResourcesEnd() {}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.test.resource.transaction.jdbc;
import org.hibernate.resource.jdbc.internal.LogicalConnectionProvidedImpl;
import org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl;
import org.hibernate.test.resource.common.DatabaseConnectionInfo;
@ -15,6 +16,6 @@ import org.hibernate.test.resource.common.DatabaseConnectionInfo;
*/
public class LogicalConnectionTestingImpl extends LogicalConnectionProvidedImpl {
public LogicalConnectionTestingImpl() throws Exception {
super( DatabaseConnectionInfo.INSTANCE.makeConnection() );
super( DatabaseConnectionInfo.INSTANCE.makeConnection(), new ResourceRegistryStandardImpl() );
}
}

View File

@ -0,0 +1,126 @@
package org.hibernate.test.stateless;
import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.Transaction;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.SessionImpl;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.logger.LoggerInspectionRule;
import org.hibernate.testing.logger.Triggerable;
import org.junit.Rule;
import org.junit.Test;
import org.jboss.logging.Logger;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
/**
* @author Vlad Mihalcea
*/
public class StatelessSessionConnectionTest extends BaseEntityManagerFunctionalTestCase {
@Rule
public LoggerInspectionRule logInspection = new LoggerInspectionRule( Logger.getMessageLogger( CoreMessageLogger.class, AbstractBatchImpl.class.getName() ) );
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Employee.class
};
}
protected void addConfigOptions(Map options) {
options.put( AvailableSettings.STATEMENT_BATCH_SIZE, 10 );
}
@Test
@TestForIssue( jiraKey = "HHH-11732" )
public void test() {
Triggerable triggerable = logInspection.watchForLogMessages( "HHH000352" );
triggerable.reset();
StatelessSession session = entityManagerFactory().unwrap( SessionFactory.class ).openStatelessSession();
Transaction tx = session.beginTransaction();
try {
Employee employee = new Employee( "1", "2", 1 );
employee.setId( 1 );
session.insert( employee );
tx.rollback();
}
catch (HibernateException e) {
if ( tx != null ) {
tx.rollback();
}
}
finally {
session.close();
assertFalse( triggerable.wasTriggered() );
}
}
@Entity(name = "Employee")
public static class Employee {
@Id
private Integer id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public Integer getId() {
return id;
}
public void setId( Integer id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
}