use of org.hibernate.action.spi.BeforeTransactionCompletionProcess in project hibernate-orm by hibernate.
the class AuditProcessManager method get.
public AuditProcess get(EventSource session) {
final Transaction transaction = session.accessTransaction();
AuditProcess auditProcess = auditProcesses.get(transaction);
if (auditProcess == null) {
// No worries about registering a transaction twice - a transaction is single thread
auditProcess = new AuditProcess(revisionInfoGenerator, session);
auditProcesses.put(transaction, auditProcess);
session.getActionQueue().registerProcess(new BeforeTransactionCompletionProcess() {
public void doBeforeTransactionCompletion(SessionImplementor session) {
final AuditProcess process = auditProcesses.get(transaction);
if (process != null) {
process.doBeforeTransactionCompletion(session);
}
}
});
session.getActionQueue().registerProcess(new AfterTransactionCompletionProcess() {
public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) {
auditProcesses.remove(transaction);
}
});
}
return auditProcess;
}
use of org.hibernate.action.spi.BeforeTransactionCompletionProcess in project hibernate-orm by hibernate.
the class FooBarTest method testLazyCollectionsTouchedDuringPreCommit.
@Test
@TestForIssue(jiraKey = "HHH-7603")
public void testLazyCollectionsTouchedDuringPreCommit() throws Exception {
Session s = openSession();
s.beginTransaction();
Qux q = new Qux();
s.save(q);
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
q = (Qux) s.load(Qux.class, q.getKey());
s.getTransaction().commit();
// clear the session
s.clear();
// now reload the proxy and delete it
s.beginTransaction();
final Qux qToDelete = (Qux) s.load(Qux.class, q.getKey());
// register a pre commit process that will touch the collection and delete the entity
((EventSource) s).getActionQueue().registerProcess(new BeforeTransactionCompletionProcess() {
@Override
public void doBeforeTransactionCompletion(SessionImplementor session) {
qToDelete.getFums().size();
}
});
s.delete(qToDelete);
boolean ok = false;
try {
s.getTransaction().commit();
} catch (LazyInitializationException e) {
ok = true;
s.getTransaction().rollback();
} catch (TransactionException te) {
if (te.getCause() instanceof LazyInitializationException) {
ok = true;
}
s.getTransaction().rollback();
} finally {
s.close();
}
assertTrue("lazy collection should have blown in the before trans completion", ok);
s = openSession();
s.beginTransaction();
q = (Qux) s.load(Qux.class, q.getKey());
s.delete(q);
s.getTransaction().commit();
s.close();
}
use of org.hibernate.action.spi.BeforeTransactionCompletionProcess 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.action.spi.BeforeTransactionCompletionProcess in project dhis2-core by dhis2.
the class HibernateFlushListener method onFlush.
@Override
public void onFlush(FlushEvent event) throws HibernateException {
BeforeTransactionCompletionProcess beforeTransactionCompletionProcess = session -> knownTransactionsService.registerEvent(event);
event.getSession().getActionQueue().registerProcess(beforeTransactionCompletionProcess);
}
Aggregations