use of org.hibernate.engine.spi.SessionImplementor in project hibernate-orm by hibernate.
the class BatchingBatchFailureTest method testBasicInsertion.
@Test
public void testBasicInsertion() {
Session session = openSession();
session.getTransaction().begin();
try {
session.persist(new User(1, "ok"));
session.persist(new User(2, null));
session.persist(new User(3, "ok"));
session.persist(new User(4, "ok"));
session.persist(new User(5, "ok"));
session.persist(new User(6, "ok"));
// the flush should fail
session.flush();
fail("Expecting failed flush");
} catch (Exception expected) {
System.out.println("Caught expected exception : " + expected);
expected.printStackTrace(System.out);
try {
// at this point the transaction is still active but the batch should have been aborted (have to use reflection to get at the field)
SessionImplementor sessionImplementor = (SessionImplementor) session;
Field field = sessionImplementor.getJdbcCoordinator().getClass().getDeclaredField("currentBatch");
field.setAccessible(true);
Batch batch = (Batch) field.get(sessionImplementor.getJdbcCoordinator());
if (batch == null) {
throw new Exception("Current batch was null");
} else {
// make sure it's actually a batching impl
assertEquals(BatchingBatch.class, batch.getClass());
field = AbstractBatchImpl.class.getDeclaredField("statements");
field.setAccessible(true);
// check to see that there aren't any statements queued up (this can be an issue if using SavePoints)
assertEquals(0, ((Map) field.get(batch)).size());
}
} catch (Exception fieldException) {
fail("Couldn't inspect field " + fieldException.getMessage());
}
} finally {
session.getTransaction().rollback();
session.close();
}
}
use of org.hibernate.engine.spi.SessionImplementor in project hibernate-orm by hibernate.
the class NonBatchingBatchFailureTest method testBasicInsertion.
@Test
public void testBasicInsertion() {
Session session = openSession();
session.getTransaction().begin();
try {
session.persist(new User(1, "ok"));
session.persist(new User(2, null));
session.persist(new User(3, "ok"));
// the flush should fail
session.flush();
fail("Expecting failed flush");
} catch (Exception expected) {
System.out.println("Caught expected exception : " + expected);
expected.printStackTrace(System.out);
try {
// at this point the transaction is still active but the batch should have been aborted (have to use reflection to get at the field)
SessionImplementor sessionImplementor = (SessionImplementor) session;
Field field = sessionImplementor.getJdbcCoordinator().getClass().getDeclaredField("currentBatch");
field.setAccessible(true);
Batch batch = (Batch) field.get(sessionImplementor.getJdbcCoordinator());
if (batch != null) {
// make sure it's actually a batching impl
assertEquals(NonBatchingBatch.class, batch.getClass());
field = AbstractBatchImpl.class.getDeclaredField("statements");
field.setAccessible(true);
// check to see that there aren't any statements queued up (this can be an issue if using SavePoints)
assertEquals(0, ((Map) field.get(batch)).size());
}
} catch (Exception fieldException) {
fail("Couldn't inspect field " + fieldException.getMessage());
}
} finally {
session.getTransaction().rollback();
session.close();
}
}
use of org.hibernate.engine.spi.SessionImplementor in project hibernate-orm by hibernate.
the class ValidityAuditStrategy method perform.
@Override
public void perform(final Session session, final String entityName, final AuditEntitiesConfiguration audEntitiesCfg, final Serializable id, final Object data, final Object revision) {
final String auditedEntityName = audEntitiesCfg.getAuditEntityName(entityName);
final String revisionInfoEntityName = audEntitiesCfg.getRevisionInfoEntityName();
// Save the audit data
session.save(auditedEntityName, data);
// Update the end date of the previous row.
//
// When application reuses identifiers of previously removed entities:
// The UPDATE statement will no-op if an entity with a given identifier has been
// inserted for the first time. But in case a deleted primary key value was
// reused, this guarantees correct strategy behavior: exactly one row with
// null end date exists for each identifier.
final boolean reuseEntityIdentifier = audEntitiesCfg.getEnversService().getGlobalConfiguration().isAllowIdentifierReuse();
if (reuseEntityIdentifier || getRevisionType(audEntitiesCfg, data) != RevisionType.ADD) {
// Register transaction completion process to guarantee execution of UPDATE statement after INSERT.
((EventSource) session).getActionQueue().registerProcess(new BeforeTransactionCompletionProcess() {
@Override
public void doBeforeTransactionCompletion(final SessionImplementor sessionImplementor) {
final Queryable productionEntityQueryable = getQueryable(entityName, sessionImplementor);
final Queryable rootProductionEntityQueryable = getQueryable(productionEntityQueryable.getRootEntityName(), sessionImplementor);
final Queryable auditedEntityQueryable = getQueryable(auditedEntityName, sessionImplementor);
final Queryable rootAuditedEntityQueryable = getQueryable(auditedEntityQueryable.getRootEntityName(), sessionImplementor);
final String updateTableName;
if (UnionSubclassEntityPersister.class.isInstance(rootProductionEntityQueryable)) {
// this is the condition causing all the problems in terms of the generated SQL UPDATE
// the problem being that we currently try to update the in-line view made up of the union query
//
// this is extremely hacky means to get the root table name for the union subclass style entities.
// hacky because it relies on internal behavior of UnionSubclassEntityPersister
// !!!!!! NOTICE - using subclass persister, not root !!!!!!
updateTableName = auditedEntityQueryable.getSubclassTableName(0);
} else {
updateTableName = rootAuditedEntityQueryable.getTableName();
}
final Type revisionInfoIdType = sessionImplementor.getFactory().getMetamodel().entityPersister(revisionInfoEntityName).getIdentifierType();
final String revEndColumnName = rootAuditedEntityQueryable.toColumns(audEntitiesCfg.getRevisionEndFieldName())[0];
final boolean isRevisionEndTimestampEnabled = audEntitiesCfg.isRevisionEndTimestampEnabled();
// update audit_ent set REVEND = ? [, REVEND_TSTMP = ?] where (prod_ent_id) = ? and REV <> ? and REVEND is null
final Update update = new Update(sessionImplementor.getFactory().getJdbcServices().getDialect()).setTableName(updateTableName);
// set REVEND = ?
update.addColumn(revEndColumnName);
// set [, REVEND_TSTMP = ?]
if (isRevisionEndTimestampEnabled) {
update.addColumn(rootAuditedEntityQueryable.toColumns(audEntitiesCfg.getRevisionEndTimestampFieldName())[0]);
}
// where (prod_ent_id) = ?
update.addPrimaryKeyColumns(rootProductionEntityQueryable.getIdentifierColumnNames());
// where REV <> ?
update.addWhereColumn(rootAuditedEntityQueryable.toColumns(audEntitiesCfg.getRevisionNumberPath())[0], "<> ?");
// where REVEND is null
update.addWhereColumn(revEndColumnName, " is null");
// Now lets execute the sql...
final String updateSql = update.toStatementString();
int rowCount = sessionImplementor.doReturningWork(new ReturningWork<Integer>() {
@Override
public Integer execute(Connection connection) throws SQLException {
PreparedStatement preparedStatement = sessionImplementor.getJdbcCoordinator().getStatementPreparer().prepareStatement(updateSql);
try {
int index = 1;
// set REVEND = ?
final Number revisionNumber = audEntitiesCfg.getEnversService().getRevisionInfoNumberReader().getRevisionNumber(revision);
revisionInfoIdType.nullSafeSet(preparedStatement, revisionNumber, index, sessionImplementor);
index += revisionInfoIdType.getColumnSpan(sessionImplementor.getFactory());
// set [, REVEND_TSTMP = ?]
if (isRevisionEndTimestampEnabled) {
final Object revEndTimestampObj = revisionTimestampGetter.get(revision);
final Date revisionEndTimestamp = convertRevEndTimestampToDate(revEndTimestampObj);
final Type revEndTsType = rootAuditedEntityQueryable.getPropertyType(audEntitiesCfg.getRevisionEndTimestampFieldName());
revEndTsType.nullSafeSet(preparedStatement, revisionEndTimestamp, index, sessionImplementor);
index += revEndTsType.getColumnSpan(sessionImplementor.getFactory());
}
// where (prod_ent_id) = ?
final Type idType = rootProductionEntityQueryable.getIdentifierType();
idType.nullSafeSet(preparedStatement, id, index, sessionImplementor);
index += idType.getColumnSpan(sessionImplementor.getFactory());
// where REV <> ?
final Type revType = rootAuditedEntityQueryable.getPropertyType(audEntitiesCfg.getRevisionNumberPath());
revType.nullSafeSet(preparedStatement, revisionNumber, index, sessionImplementor);
return sessionImplementor.getJdbcCoordinator().getResultSetReturn().executeUpdate(preparedStatement);
} finally {
sessionImplementor.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release(preparedStatement);
sessionImplementor.getJdbcCoordinator().afterStatementExecution();
}
}
});
if (rowCount != 1 && (!reuseEntityIdentifier || (getRevisionType(audEntitiesCfg, data) != RevisionType.ADD))) {
throw new RuntimeException("Cannot update previous revision for entity " + auditedEntityName + " and id " + id);
}
}
});
}
sessionCacheCleaner.scheduleAuditDataRemoval(session, data);
}
use of org.hibernate.engine.spi.SessionImplementor in project hibernate-orm by hibernate.
the class DefaultFlushEntityEventListener method handleInterception.
protected boolean handleInterception(FlushEntityEvent event) {
SessionImplementor session = event.getSession();
EntityEntry entry = event.getEntityEntry();
EntityPersister persister = entry.getPersister();
Object entity = event.getEntity();
// give the Interceptor a chance to modify property values
final Object[] values = event.getPropertyValues();
final boolean intercepted = invokeInterceptor(session, entity, entry, values, persister);
// now we might need to recalculate the dirtyProperties array
if (intercepted && event.isDirtyCheckPossible() && !event.isDirtyCheckHandledByInterceptor()) {
int[] dirtyProperties;
if (event.hasDatabaseSnapshot()) {
dirtyProperties = persister.findModified(event.getDatabaseSnapshot(), values, entity, session);
} else {
dirtyProperties = persister.findDirty(values, entry.getLoadedState(), entity, session);
}
event.setDirtyProperties(dirtyProperties);
}
return intercepted;
}
use of org.hibernate.engine.spi.SessionImplementor in project hibernate-orm by hibernate.
the class DefaultPersistEventListener method onPersist.
/**
* Handle the given create event.
*
* @param event The create event to be handled.
*/
public void onPersist(PersistEvent event, Map createCache) throws HibernateException {
final SessionImplementor source = event.getSession();
final Object object = event.getObject();
final Object entity;
if (object instanceof HibernateProxy) {
LazyInitializer li = ((HibernateProxy) object).getHibernateLazyInitializer();
if (li.isUninitialized()) {
if (li.getSession() == source) {
// NOTE EARLY EXIT!
return;
} else {
throw new PersistentObjectException("uninitialized proxy passed to persist()");
}
}
entity = li.getImplementation();
} else {
entity = object;
}
final String entityName;
if (event.getEntityName() != null) {
entityName = event.getEntityName();
} else {
entityName = source.bestGuessEntityName(entity);
event.setEntityName(entityName);
}
final EntityEntry entityEntry = source.getPersistenceContext().getEntry(entity);
EntityState entityState = getEntityState(entity, entityName, entityEntry, source);
if (entityState == EntityState.DETACHED) {
// JPA 2, in its version of a "foreign generated", allows the id attribute value
// to be manually set by the user, even though this manual value is irrelevant.
// The issue is that this causes problems with the Hibernate unsaved-value strategy
// which comes into play here in determining detached/transient state.
//
// Detect if we have this situation and if so null out the id value and calculate the
// entity state again.
// NOTE: entityEntry must be null to get here, so we cannot use any of its values
EntityPersister persister = source.getFactory().getEntityPersister(entityName);
if (ForeignGenerator.class.isInstance(persister.getIdentifierGenerator())) {
if (LOG.isDebugEnabled() && persister.getIdentifier(entity, source) != null) {
LOG.debug("Resetting entity id attribute to null for foreign generator");
}
persister.setIdentifier(entity, null, source);
entityState = getEntityState(entity, entityName, entityEntry, source);
}
}
switch(entityState) {
case DETACHED:
{
throw new PersistentObjectException("detached entity passed to persist: " + getLoggableName(event.getEntityName(), entity));
}
case PERSISTENT:
{
entityIsPersistent(event, createCache);
break;
}
case TRANSIENT:
{
entityIsTransient(event, createCache);
break;
}
case DELETED:
{
entityEntry.setStatus(Status.MANAGED);
entityEntry.setDeletedState(null);
event.getSession().getActionQueue().unScheduleDeletion(entityEntry, event.getObject());
entityIsDeleted(event, createCache);
break;
}
default:
{
throw new ObjectDeletedException("deleted entity passed to persist", null, getLoggableName(event.getEntityName(), entity));
}
}
}
Aggregations