[hibernate-dev] Possible regression on master

Vlad Mihalcea mihalcea.vlad at gmail.com
Tue May 17 08:32:40 EDT 2016


Hi,

While fixing tests for Oracle after rebasing my branch, I realized that the
org.hibernate.test.optlock.OptimisticLockTest > testOptimisticLockAllDelete
fails on Oracle10gDialect, while running just fine on H2.

When reaching the following branching logic in AbstractEntityPersister:

if ( useBatch ) {
    session.getJdbcCoordinator().getBatch( deleteBatchKey ).addToBatch();
}
else {
    check(

session.getJdbcCoordinator().getResultSetReturn().executeUpdate( delete ),
            id,
            j,
            expectation,
            delete
    );
}

If using H2, we go to the useBatch branch, while for Oracle dialects that
are less than 12c (hibernate.jdbc.batch_versioned_data is set to false,
therefore JDBC batching is disabled) it goes on the second branch logic.

This way, for H2, a StaleStateException is thrown and the flush operation
flow is disrupted.

For Oracle, the check method call will throw a StaleObjectStateException
instead:

catch (StaleStateException e) {
    if ( !isNullableTable( tableNumber ) ) {
        if ( getFactory().getStatistics().isStatisticsEnabled() ) {
            getFactory().getStatisticsImplementor()
                    .optimisticFailure( getEntityName() );
        }
        throw new StaleObjectStateException( getEntityName(), id );
    }
    return false;
}

Now, there is a difference between how the ExceptionConverterImpl handles
StaleStateException and StaleObjectStateException because for the latter,
it tries to fetch the entity in question:

final Object entity = sharedSessionContract.load( sose.getEntityName(),
identifier );

Because there is no proxy loaded in the current Session, the
DefaultLoadEntityListener will execute the createProxyIfNecessary method,
and because the entity was deleted, it will return null:

EntityEntry entry = persistenceContext.getEntry( existing );
Status status = entry.getStatus();
if ( status == Status.DELETED || status == Status.GONE ) {
    return null;
}

However, getReference() throws an exception when there is no object being
found:

getFactory().getEntityNotFoundDelegate().handleEntityNotFound(
    entityPersister.getEntityName(),
    id
);

So instead of a StaleObjectStateException, we get
an ObjectNotFoundException.

One quick fix is to just catch that ObjectNotFoundException:

Object entity;
try {
    entity = sharedSessionContract.load( sose.getEntityName(), identifier );
} catch(ObjectNotFoundException e) {
    entity = null;
}

if ( entity instanceof Serializable ) {
    //avoid some user errors regarding boundary crossing
    pe = new OptimisticLockException( e.getMessage(), e, entity );
}
else {
    pe = new OptimisticLockException( e.getMessage(), e );
}

Or maybe there is some other fix that you might think of.

Vlad


More information about the hibernate-dev mailing list