diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java index 7c82ff7017..9f4db07d41 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java @@ -17,6 +17,9 @@ import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.jfr.CachePutEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; +import org.hibernate.event.jfr.internal.JfrEventManager.CacheActionDescription; import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.PostCommitInsertEventListener; @@ -161,11 +164,23 @@ public class EntityInsertAction extends AbstractEntityInsertAction { protected boolean cacheInsert(EntityPersister persister, Object ck) { SharedSessionContractImplementor session = getSession(); + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); + final EntityDataAccess cacheAccessStrategy = persister.getCacheAccessStrategy(); + boolean insert = false; try { session.getEventListenerManager().cachePutStart(); - return persister.getCacheAccessStrategy().insert( session, ck, cacheEntry, version ); + insert = cacheAccessStrategy.insert( session, ck, cacheEntry, version ); + return insert; } finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session, + cacheAccessStrategy, + getPersister(), + insert, + CacheActionDescription.ENTITY_INSERT + ); session.getEventListenerManager().cachePutEnd(); } } @@ -240,11 +255,22 @@ public class EntityInsertAction extends AbstractEntityInsertAction { protected boolean cacheAfterInsert(EntityDataAccess cache, Object ck) { SharedSessionContractImplementor session = getSession(); final SessionEventListenerManager eventListenerManager = session.getEventListenerManager(); + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); + boolean afterInsert = false; try { eventListenerManager.cachePutStart(); - return cache.afterInsert( session, ck, cacheEntry, version ); + afterInsert = cache.afterInsert( session, ck, cacheEntry, version ); + return afterInsert; } finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session, + cache, + getPersister(), + afterInsert, + CacheActionDescription.ENTITY_AFTER_INSERT + ); eventListenerManager.cachePutEnd(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java index a1281c391a..e70c79c66e 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java @@ -19,6 +19,8 @@ import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; +import org.hibernate.event.jfr.CachePutEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.PostCommitUpdateEventListener; @@ -307,11 +309,23 @@ public class EntityUpdateAction extends EntityAction { protected boolean updateCache(EntityPersister persister, Object previousVersion, Object ck) { final SharedSessionContractImplementor session = getSession(); + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); + final EntityDataAccess cacheAccessStrategy = persister.getCacheAccessStrategy(); + boolean update = false; try { session.getEventListenerManager().cachePutStart(); - return persister.getCacheAccessStrategy().update( session, ck, cacheEntry, nextVersion, previousVersion ); + update = cacheAccessStrategy.update( session, ck, cacheEntry, nextVersion, previousVersion ); + return update; } finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session, + cacheAccessStrategy, + getPersister(), + update, + JfrEventManager.CacheActionDescription.ENTITY_UPDATE + ); session.getEventListenerManager().cachePutEnd(); } } @@ -420,10 +434,21 @@ public class EntityUpdateAction extends EntityAction { protected void cacheAfterUpdate(EntityDataAccess cache, Object ck, SharedSessionContractImplementor session) { final SessionEventListenerManager eventListenerManager = session.getEventListenerManager(); + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); + boolean put = false; try { eventListenerManager.cachePutStart(); - boolean put = cache.afterUpdate( session, ck, cacheEntry, nextVersion, previousVersion, lock ); - + put = cache.afterUpdate( session, ck, cacheEntry, nextVersion, previousVersion, lock ); + } + finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session, + cache, + getPersister(), + put, + JfrEventManager.CacheActionDescription.ENTITY_AFTER_UPDATE + ); final StatisticsImplementor statistics = session.getFactory().getStatistics(); if ( put && statistics.isStatisticsEnabled() ) { statistics.entityCachePut( @@ -431,8 +456,6 @@ public class EntityUpdateAction extends EntityAction { cache.getRegion().getName() ); } - } - finally { eventListenerManager.cachePutEnd(); } diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/QueryResultsCacheImpl.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/QueryResultsCacheImpl.java index 6f4ed38aa9..d903ae0c25 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/QueryResultsCacheImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/QueryResultsCacheImpl.java @@ -17,6 +17,9 @@ import org.hibernate.cache.spi.QueryResultsCache; import org.hibernate.cache.spi.QueryResultsRegion; import org.hibernate.cache.spi.TimestampsCache; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.jfr.CacheGetEvent; +import org.hibernate.event.jfr.CachePutEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import static org.hibernate.cache.spi.SecondLevelCacheLogger.L2CACHE_LOGGER; @@ -61,11 +64,19 @@ public class QueryResultsCacheImpl implements QueryResultsCache { deepCopy( results ) ); + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); try { session.getEventListenerManager().cachePutStart(); cacheRegion.putIntoCache( key, cacheItem, session ); } finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session, + cacheRegion, + true, + JfrEventManager.CacheActionDescription.QUERY_RESULT + ); session.getEventListenerManager().cachePutEnd(); } @@ -142,11 +153,18 @@ public class QueryResultsCacheImpl implements QueryResultsCache { private CacheItem getCachedData(QueryKey key, SharedSessionContractImplementor session) { CacheItem cachedItem = null; + final CacheGetEvent cacheGetEvent = JfrEventManager.beginCacheGetEvent(); try { session.getEventListenerManager().cacheGetStart(); cachedItem = (CacheItem) cacheRegion.getFromCache( key, session ); } finally { + JfrEventManager.completeCacheGetEvent( + cacheGetEvent, + session, + cacheRegion, + cachedItem != null + ); session.getEventListenerManager().cacheGetEnd( cachedItem != null ); } return cachedItem; diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/TimestampsCacheEnabledImpl.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/TimestampsCacheEnabledImpl.java index e6be0dfe27..7559d9ba76 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/TimestampsCacheEnabledImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/TimestampsCacheEnabledImpl.java @@ -14,6 +14,9 @@ import org.hibernate.cache.spi.TimestampsRegion; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.jfr.CacheGetEvent; +import org.hibernate.event.jfr.CachePutEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.stat.spi.StatisticsImplementor; import org.jboss.logging.Logger; @@ -58,7 +61,7 @@ public class TimestampsCacheEnabledImpl implements TimestampsCache { if ( debugEnabled ) { log.debugf( "Pre-invalidating space [%s], timestamp: %s", space, ts ); } - + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); try { eventListenerManager.cachePutStart(); @@ -67,6 +70,13 @@ public class TimestampsCacheEnabledImpl implements TimestampsCache { timestampsRegion.putIntoCache( space, ts, session ); } finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session, + timestampsRegion, + true, + JfrEventManager.CacheActionDescription.TIMESTAMP_PRE_INVALIDATE + ); eventListenerManager.cachePutEnd(); } @@ -92,11 +102,19 @@ public class TimestampsCacheEnabledImpl implements TimestampsCache { } final SessionEventListenerManager eventListenerManager = session.getEventListenerManager(); + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); try { eventListenerManager.cachePutStart(); timestampsRegion.putIntoCache( space, ts, session ); } finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session, + timestampsRegion, + true, + JfrEventManager.CacheActionDescription.TIMESTAMP_INVALIDATE + ); eventListenerManager.cachePutEnd(); if ( stats ) { @@ -175,11 +193,18 @@ public class TimestampsCacheEnabledImpl implements TimestampsCache { private Long getLastUpdateTimestampForSpace(String space, SharedSessionContractImplementor session) { Long ts = null; + final CacheGetEvent cacheGetEvent = JfrEventManager.beginCacheGetEvent(); try { session.getEventListenerManager().cacheGetStart(); ts = (Long) timestampsRegion.getFromCache( space, session ); } finally { + JfrEventManager.completeCacheGetEvent( + cacheGetEvent, + session, + timestampsRegion, + ts != null + ); session.getEventListenerManager().cacheGetEnd( ts != null ); } return ts; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/CacheHelper.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/CacheHelper.java index 0e3543d1ef..84e67cb340 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/CacheHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/CacheHelper.java @@ -12,7 +12,11 @@ import org.hibernate.cache.MutableCacheKeyBuilder; import org.hibernate.cache.spi.access.CachedDomainDataAccess; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.jfr.CacheGetEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.type.descriptor.java.JavaType; @@ -28,14 +32,58 @@ public final class CacheHelper { public static Object fromSharedCache( SharedSessionContractImplementor session, Object cacheKey, + EntityPersister persister, + CachedDomainDataAccess cacheAccess) { + return fromSharedCache( session, cacheKey, persister, false, cacheAccess ); + } + + public static Object fromSharedCache( + SharedSessionContractImplementor session, + Object cacheKey, + EntityPersister persister, + boolean isNaturalKey, CachedDomainDataAccess cacheAccess) { final SessionEventListenerManager eventListenerManager = session.getEventListenerManager(); Object cachedValue = null; eventListenerManager.cacheGetStart(); + final CacheGetEvent cacheGetEvent = JfrEventManager.beginCacheGetEvent(); try { cachedValue = cacheAccess.get( session, cacheKey ); } finally { + JfrEventManager.completeCacheGetEvent( + cacheGetEvent, + session, + cacheAccess.getRegion(), + persister, + isNaturalKey, + cachedValue != null + ); + eventListenerManager.cacheGetEnd( cachedValue != null ); + } + return cachedValue; + } + + public static Object fromSharedCache( + SharedSessionContractImplementor session, + Object cacheKey, + CollectionPersister persister, + CachedDomainDataAccess cacheAccess) { + final SessionEventListenerManager eventListenerManager = session.getEventListenerManager(); + Object cachedValue = null; + eventListenerManager.cacheGetStart(); + final CacheGetEvent cacheGetEvent = JfrEventManager.beginCacheGetEvent(); + try { + cachedValue = cacheAccess.get( session, cacheKey ); + } + finally { + JfrEventManager.completeCacheGetEvent( + cacheGetEvent, + session, + cacheAccess.getRegion(), + persister, + cachedValue != null + ); eventListenerManager.cacheGetEnd( cachedValue != null ); } return cachedValue; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdResolutionsImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdResolutionsImpl.java index de7197502b..dba4c1e05e 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdResolutionsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdResolutionsImpl.java @@ -23,6 +23,8 @@ import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.Resolution; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.jfr.CachePutEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.NaturalIdLogging; import org.hibernate.metamodel.mapping.NaturalIdMapping; @@ -268,32 +270,63 @@ public class NaturalIdResolutionsImpl implements NaturalIdResolutions, Serializa switch ( source ) { case LOAD: { - if ( CacheHelper.fromSharedCache( s, cacheKey, cacheAccess ) != null ) { + if ( CacheHelper.fromSharedCache( s, cacheKey, persister, cacheAccess ) != null ) { // prevent identical re-cachings return; } - final boolean put = cacheAccess.putFromLoad( - s, - cacheKey, - id, - null - ); + boolean put = false; + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); + try { + put = cacheAccess.putFromLoad( + s, + cacheKey, + id, + null + ); - if ( put && statistics.isStatisticsEnabled() ) { - statistics.naturalIdCachePut( - rootEntityDescriptor.getNavigableRole(), - cacheAccess.getRegion().getName() + if ( put && statistics.isStatisticsEnabled() ) { + statistics.naturalIdCachePut( + rootEntityDescriptor.getNavigableRole(), + cacheAccess.getRegion().getName() + ); + } + } + finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session(), + cacheAccess, + rootEntityPersister, + put, + true, + JfrEventManager.CacheActionDescription.ENTITY_LOAD ); } break; } case INSERT: { - final boolean put = cacheAccess.insert( s, cacheKey, id ); - if ( put && statistics.isStatisticsEnabled() ) { - statistics.naturalIdCachePut( - rootEntityDescriptor.getNavigableRole(), - cacheAccess.getRegion().getName() + boolean put = false; + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); + + try { + put = cacheAccess.insert( s, cacheKey, id ); + if ( put && statistics.isStatisticsEnabled() ) { + statistics.naturalIdCachePut( + rootEntityDescriptor.getNavigableRole(), + cacheAccess.getRegion().getName() + ); + } + } + finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session(), + cacheAccess, + rootEntityPersister, + put, + true, + JfrEventManager.CacheActionDescription.ENTITY_INSERT ); } @@ -326,11 +359,26 @@ public class NaturalIdResolutionsImpl implements NaturalIdResolutions, Serializa cacheAccess.remove( s, previousCacheKey); final SoftLock lock = cacheAccess.lockItem( s, cacheKey, null ); - final boolean put = cacheAccess.update( s, cacheKey, id ); - if ( put && statistics.isStatisticsEnabled() ) { - statistics.naturalIdCachePut( - rootEntityDescriptor.getNavigableRole(), - cacheAccess.getRegion().getName() + boolean put = false; + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); + try { + put = cacheAccess.update( s, cacheKey, id ); + if ( put && statistics.isStatisticsEnabled() ) { + statistics.naturalIdCachePut( + rootEntityDescriptor.getNavigableRole(), + cacheAccess.getRegion().getName() + ); + } + } + finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session(), + cacheAccess, + rootEntityPersister, + put, + true, + JfrEventManager.CacheActionDescription.ENTITY_UPDATE ); } @@ -338,17 +386,32 @@ public class NaturalIdResolutionsImpl implements NaturalIdResolutions, Serializa (success, session) -> { cacheAccess.unlockItem( s, previousCacheKey, removalLock ); if (success) { - final boolean put12 = cacheAccess.afterUpdate( - s, - cacheKey, - id, - lock - ); + boolean putAfterUpdate = false; + final CachePutEvent cachePutEventAfterUpdate = JfrEventManager.beginCachePutEvent(); + try { + putAfterUpdate = cacheAccess.afterUpdate( + s, + cacheKey, + id, + lock + ); - if ( put12 && statistics.isStatisticsEnabled() ) { - statistics.naturalIdCachePut( - rootEntityDescriptor.getNavigableRole(), - cacheAccess.getRegion().getName() + if ( putAfterUpdate && statistics.isStatisticsEnabled() ) { + statistics.naturalIdCachePut( + rootEntityDescriptor.getNavigableRole(), + cacheAccess.getRegion().getName() + ); + } + } + finally { + JfrEventManager.completeCachePutEvent( + cachePutEventAfterUpdate, + session(), + cacheAccess, + rootEntityPersister, + putAfterUpdate, + true, + JfrEventManager.CacheActionDescription.ENTITY_AFTER_UPDATE ); } } @@ -567,7 +630,7 @@ public class NaturalIdResolutionsImpl implements NaturalIdResolutions, Serializa final SharedSessionContractImplementor session = session(); final Object naturalIdCacheKey = naturalIdCacheAccessStrategy.generateCacheKey( naturalId, persister, session ); - pk = CacheHelper.fromSharedCache( session, naturalIdCacheKey, naturalIdCacheAccessStrategy ); + pk = CacheHelper.fromSharedCache( session, naturalIdCacheKey, persister, true, naturalIdCacheAccessStrategy ); // Found in second-level cache, store in session cache final SessionFactoryImplementor factory = session.getFactory(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchImpl.java index ca5733b8b6..d80c1912df 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchImpl.java @@ -22,6 +22,8 @@ import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; +import org.hibernate.event.jfr.JdbcBatchExecutionEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.resource.jdbc.spi.JdbcObserver; import static org.hibernate.engine.jdbc.JdbcLogging.JDBC_MESSAGE_LOGGER; @@ -268,11 +270,13 @@ public class BatchImpl implements Batch { try { if ( statementDetails.getMutatingTableDetails().isIdentifierTable() ) { final int[] rowCounts; + final JdbcBatchExecutionEvent jdbcBatchExecutionEvent = JfrEventManager.beginJdbcBatchExecutionEvent(); try { observer.jdbcExecuteBatchStart(); rowCounts = statement.executeBatch(); } finally { + JfrEventManager.completeJdbcBatchExecutionEvent( jdbcBatchExecutionEvent, sql ); observer.jdbcExecuteBatchEnd(); } checkRowCounts( rowCounts, statementDetails ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/MutationStatementPreparerImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/MutationStatementPreparerImpl.java index fb8bc5f0c0..58e0f0bfea 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/MutationStatementPreparerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/MutationStatementPreparerImpl.java @@ -14,6 +14,8 @@ import org.hibernate.AssertionFailure; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.MutationStatementPreparer; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; +import org.hibernate.event.jfr.internal.JfrEventManager; +import org.hibernate.event.jfr.JdbcPreparedStatementCreationEvent; import org.hibernate.resource.jdbc.spi.JdbcObserver; import org.hibernate.resource.jdbc.spi.JdbcSessionContext; import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor; @@ -95,12 +97,14 @@ public class MutationStatementPreparerImpl implements MutationStatementPreparer final JdbcObserver observer = jdbcCoordinator.getJdbcSessionOwner() .getJdbcSessionContext() .getObserver(); + final JdbcPreparedStatementCreationEvent jdbcPreparedStatementCreation = JfrEventManager.beginJdbcPreparedStatementCreationEvent(); try { observer.jdbcPrepareStatementStart(); preparedStatement = doPrepare(); setStatementTimeout( preparedStatement ); } finally { + JfrEventManager.completeJdbcPreparedStatementCreationEvent( jdbcPreparedStatementCreation, sql ); observer.jdbcPrepareStatementEnd(); } postProcess( preparedStatement ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ResultSetReturnImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ResultSetReturnImpl.java index 3f500532cc..2a7ddd97bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ResultSetReturnImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ResultSetReturnImpl.java @@ -18,6 +18,8 @@ import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.ResultSetReturn; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; +import org.hibernate.event.jfr.JdbcPreparedStatementExecutionEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.resource.jdbc.spi.JdbcSessionContext; /** @@ -44,31 +46,6 @@ public class ResultSetReturnImpl implements ResultSetReturn { this.sqlExceptionHelper = jdbcServices.getSqlExceptionHelper(); } - @Override - public ResultSet extract(PreparedStatement statement) { - // IMPL NOTE : SQL logged by caller - long executeStartNanos = 0; - if ( this.sqlStatementLogger.getLogSlowQuery() > 0 ) { - executeStartNanos = System.nanoTime(); - } - try { - final ResultSet rs; - try { - jdbcExecuteStatementStart(); - rs = statement.executeQuery(); - } - finally { - jdbcExecuteStatementEnd(); - sqlStatementLogger.logSlowQuery( statement, executeStartNanos, context() ); - } - postExtract( rs, statement ); - return rs; - } - catch (SQLException e) { - throw sqlExceptionHelper.convert( e, "could not extract ResultSet" ); - } - } - @Override public ResultSet extract(PreparedStatement statement, String sql) { // IMPL NOTE : SQL logged by caller @@ -78,11 +55,13 @@ public class ResultSetReturnImpl implements ResultSetReturn { } try { final ResultSet rs; + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = JfrEventManager.beginJdbcPreparedStatementExecutionEvent(); try { jdbcExecuteStatementStart(); rs = statement.executeQuery(); } finally { + JfrEventManager.completeJdbcPreparedStatementExecutionEvent( jdbcPreparedStatementExecutionEvent, sql ); jdbcExecuteStatementEnd(); sqlStatementLogger.logSlowQuery( sql, executeStartNanos, context() ); } @@ -106,31 +85,6 @@ public class ResultSetReturnImpl implements ResultSetReturn { jdbcCoordinator.getJdbcSessionOwner().getJdbcSessionContext().getObserver().jdbcExecuteStatementStart(); } - @Override - public ResultSet extract(CallableStatement callableStatement) { - // IMPL NOTE : SQL logged by caller - long executeStartNanos = 0; - if ( this.sqlStatementLogger.getLogSlowQuery() > 0 ) { - executeStartNanos = System.nanoTime(); - } - try { - final ResultSet rs; - try { - jdbcExecuteStatementStart(); - rs = dialect.getResultSet( callableStatement ); - } - finally { - jdbcExecuteStatementEnd(); - sqlStatementLogger.logSlowQuery( callableStatement, executeStartNanos, context() ); - } - postExtract( rs, callableStatement ); - return rs; - } - catch (SQLException e) { - throw sqlExceptionHelper.convert( e, "could not extract ResultSet" ); - } - } - @Override public ResultSet extract(Statement statement, String sql) { sqlStatementLogger.logStatement( sql ); @@ -140,11 +94,13 @@ public class ResultSetReturnImpl implements ResultSetReturn { } try { final ResultSet rs; + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = JfrEventManager.beginJdbcPreparedStatementExecutionEvent(); try { jdbcExecuteStatementStart(); rs = statement.executeQuery( sql ); } finally { + JfrEventManager.completeJdbcPreparedStatementExecutionEvent( jdbcPreparedStatementExecutionEvent, sql ); jdbcExecuteStatementEnd(); sqlStatementLogger.logSlowQuery( sql, executeStartNanos, context() ); } @@ -156,36 +112,6 @@ public class ResultSetReturnImpl implements ResultSetReturn { } } - @Override - public ResultSet execute(PreparedStatement statement) { - // sql logged by StatementPreparerImpl - long executeStartNanos = 0; - if ( this.sqlStatementLogger.getLogSlowQuery() > 0 ) { - executeStartNanos = System.nanoTime(); - } - try { - final ResultSet rs; - try { - jdbcExecuteStatementStart(); - if ( !statement.execute() ) { - while ( !statement.getMoreResults() && statement.getUpdateCount() != -1 ) { - // do nothing until we hit the resultset - } - } - rs = statement.getResultSet(); - } - finally { - jdbcExecuteStatementEnd(); - sqlStatementLogger.logSlowQuery( statement, executeStartNanos, context() ); - } - postExtract( rs, statement ); - return rs; - } - catch (SQLException e) { - throw sqlExceptionHelper.convert( e, "could not execute statement" ); - } - } - @Override public ResultSet execute(PreparedStatement statement, String sql) { // sql logged by StatementPreparerImpl @@ -195,6 +121,7 @@ public class ResultSetReturnImpl implements ResultSetReturn { } try { final ResultSet rs; + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = JfrEventManager.beginJdbcPreparedStatementExecutionEvent(); try { jdbcExecuteStatementStart(); if ( !statement.execute() ) { @@ -205,6 +132,7 @@ public class ResultSetReturnImpl implements ResultSetReturn { rs = statement.getResultSet(); } finally { + JfrEventManager.completeJdbcPreparedStatementExecutionEvent( jdbcPreparedStatementExecutionEvent, sql ); jdbcExecuteStatementEnd(); sqlStatementLogger.logSlowQuery( sql, executeStartNanos, context() ); } @@ -225,6 +153,7 @@ public class ResultSetReturnImpl implements ResultSetReturn { } try { final ResultSet rs; + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = JfrEventManager.beginJdbcPreparedStatementExecutionEvent(); try { jdbcExecuteStatementStart(); if ( !statement.execute( sql ) ) { @@ -235,6 +164,7 @@ public class ResultSetReturnImpl implements ResultSetReturn { rs = statement.getResultSet(); } finally { + JfrEventManager.completeJdbcPreparedStatementExecutionEvent( jdbcPreparedStatementExecutionEvent, sql ); jdbcExecuteStatementEnd(); sqlStatementLogger.logSlowQuery( sql, executeStartNanos, context() ); } @@ -246,27 +176,6 @@ public class ResultSetReturnImpl implements ResultSetReturn { } } - @Override - public int executeUpdate(PreparedStatement statement) { - assert statement != null; - - long executeStartNanos = 0; - if ( this.sqlStatementLogger.getLogSlowQuery() > 0 ) { - executeStartNanos = System.nanoTime(); - } - try { - jdbcExecuteStatementStart(); - return statement.executeUpdate(); - } - catch (SQLException e) { - throw sqlExceptionHelper.convert( e, "could not execute statement" ); - } - finally { - jdbcExecuteStatementEnd(); - sqlStatementLogger.logSlowQuery( statement, executeStartNanos, context() ); - } - } - @Override public int executeUpdate(PreparedStatement statement, String sql) { assert statement != null; @@ -275,6 +184,7 @@ public class ResultSetReturnImpl implements ResultSetReturn { if ( this.sqlStatementLogger.getLogSlowQuery() > 0 ) { executeStartNanos = System.nanoTime(); } + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = JfrEventManager.beginJdbcPreparedStatementExecutionEvent(); try { jdbcExecuteStatementStart(); return statement.executeUpdate(); @@ -283,6 +193,7 @@ public class ResultSetReturnImpl implements ResultSetReturn { throw sqlExceptionHelper.convert( e, "could not execute statement", sql ); } finally { + JfrEventManager.completeJdbcPreparedStatementExecutionEvent( jdbcPreparedStatementExecutionEvent, sql ); jdbcExecuteStatementEnd(); sqlStatementLogger.logSlowQuery( sql, executeStartNanos, context() ); } @@ -295,6 +206,7 @@ public class ResultSetReturnImpl implements ResultSetReturn { if ( this.sqlStatementLogger.getLogSlowQuery() > 0 ) { executeStartNanos = System.nanoTime(); } + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = JfrEventManager.beginJdbcPreparedStatementExecutionEvent(); try { jdbcExecuteStatementStart(); return statement.executeUpdate( sql ); @@ -303,6 +215,7 @@ public class ResultSetReturnImpl implements ResultSetReturn { throw sqlExceptionHelper.convert( e, "could not execute statement", sql ); } finally { + JfrEventManager.completeJdbcPreparedStatementExecutionEvent( jdbcPreparedStatementExecutionEvent, sql ); jdbcExecuteStatementEnd(); sqlStatementLogger.logSlowQuery( sql, executeStartNanos, context() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/StatementPreparerImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/StatementPreparerImpl.java index d7250a2c21..ac3f6795c3 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/StatementPreparerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/StatementPreparerImpl.java @@ -17,6 +17,8 @@ import org.hibernate.ScrollMode; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.engine.jdbc.spi.StatementPreparer; +import org.hibernate.event.jfr.internal.JfrEventManager; +import org.hibernate.event.jfr.JdbcPreparedStatementCreationEvent; import org.hibernate.resource.jdbc.spi.JdbcObserver; import org.hibernate.resource.jdbc.spi.JdbcSessionContext; import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor; @@ -172,12 +174,14 @@ class StatementPreparerImpl implements StatementPreparer { final PreparedStatement preparedStatement; final JdbcObserver observer = jdbcCoordinator.getJdbcSessionOwner().getJdbcSessionContext().getObserver(); + final JdbcPreparedStatementCreationEvent jdbcPreparedStatementCreation = JfrEventManager.beginJdbcPreparedStatementCreationEvent(); try { observer.jdbcPrepareStatementStart(); preparedStatement = doPrepare(); setStatementTimeout( preparedStatement ); } finally { + JfrEventManager.completeJdbcPreparedStatementCreationEvent( jdbcPreparedStatementCreation, sql ); observer.jdbcPrepareStatementEnd(); } postProcess( preparedStatement ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ResultSetReturn.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ResultSetReturn.java index 2bab3a03a1..5b8f7d1d38 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ResultSetReturn.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ResultSetReturn.java @@ -24,21 +24,6 @@ import java.sql.Statement; */ public interface ResultSetReturn { - /** - * Extract the {@link ResultSet} from the {@link PreparedStatement}. - *

- * If client passes {@link CallableStatement} reference, this method calls {@link #extract(CallableStatement)} - * internally. Otherwise, {@link PreparedStatement#executeQuery()} is called. - * - * @param statement The {@link PreparedStatement} from which to extract the {@link ResultSet} - * - * @return The extracted ResultSet - * - * @deprecated Use {@link #extract(PreparedStatement, String)} instead - */ - @Deprecated(forRemoval = true) - ResultSet extract(PreparedStatement statement); - /** * Extract the {@link ResultSet} from the {@link PreparedStatement}. *

@@ -51,20 +36,6 @@ public interface ResultSetReturn { */ ResultSet extract(PreparedStatement statement, String sql); - /** - * Extract the {@link ResultSet} from the {@link CallableStatement}. Note that this is the limited legacy - * form which delegates to {@link org.hibernate.dialect.Dialect#getResultSet}. Better option is to integrate - * {@link org.hibernate.procedure.ProcedureCall}-like hooks - * - * @param callableStatement The {@link CallableStatement} from which to extract the {@link ResultSet} - * - * @return The extracted {@link ResultSet} - * - * @deprecated Use {@link #extract(PreparedStatement, String)} instead - */ - @Deprecated(forRemoval = true) - ResultSet extract(CallableStatement callableStatement); - /** * Performs the given SQL statement, expecting a {@link ResultSet} in return * @@ -75,19 +46,6 @@ public interface ResultSetReturn { */ ResultSet extract(Statement statement, String sql); - /** - * Execute the {@link PreparedStatement} return its first {@link ResultSet}, if any. - * If there is no {@link ResultSet}, returns {@code null} - * - * @param statement The {@link PreparedStatement} to execute - * - * @return The extracted {@link ResultSet}, or {@code null} - * - * @deprecated Use {@link #execute(PreparedStatement, String)} instead - */ - @Deprecated(forRemoval = true) - ResultSet execute(PreparedStatement statement); - /** * Execute the {@link PreparedStatement} return its first {@link ResultSet}, if any. * If there is no {@link ResultSet}, returns {@code null} @@ -110,18 +68,6 @@ public interface ResultSetReturn { */ ResultSet execute(Statement statement, String sql); - /** - * Execute the {@link PreparedStatement}, returning its "affected row count". - * - * @param statement The {@link PreparedStatement} to execute - * - * @return The {@link PreparedStatement#executeUpdate()} result - * - * @deprecated Use {@link #executeUpdate(PreparedStatement, String)} instead - */ - @Deprecated(forRemoval = true) - int executeUpdate(PreparedStatement statement); - /** * Execute the {@link PreparedStatement}, returning its "affected row count". * diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java index 7aa6a6c48d..f28544307f 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java @@ -311,7 +311,7 @@ public class BatchFetchQueue { session.getFactory(), session.getTenantIdentifier() ); - return CacheHelper.fromSharedCache( session, key, cache ) != null; + return CacheHelper.fromSharedCache( session, key, persister, cache ) != null; } return false; } @@ -529,7 +529,7 @@ public class BatchFetchQueue { session.getFactory(), session.getTenantIdentifier() ); - return CacheHelper.fromSharedCache( session, cacheKey, cache ) != null; + return CacheHelper.fromSharedCache( session, cacheKey, persister, cache ) != null; } return false; } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultAutoFlushEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultAutoFlushEventListener.java index 60a972b0cb..05c958a7c0 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultAutoFlushEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultAutoFlushEventListener.java @@ -11,6 +11,8 @@ import org.hibernate.HibernateException; import org.hibernate.engine.spi.ActionQueue; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionEventListenerManager; +import org.hibernate.event.jfr.PartialFlushEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.event.spi.AutoFlushEvent; import org.hibernate.event.spi.AutoFlushEventListener; import org.hibernate.event.spi.EventSource; @@ -37,6 +39,7 @@ public class DefaultAutoFlushEventListener extends AbstractFlushingEventListener public void onAutoFlush(AutoFlushEvent event) throws HibernateException { final EventSource source = event.getSession(); final SessionEventListenerManager eventListenerManager = source.getEventListenerManager(); + final PartialFlushEvent partialFlushEvent = JfrEventManager.beginPartialFlushEvent(); try { eventListenerManager.partialFlushStart(); @@ -52,11 +55,16 @@ public class DefaultAutoFlushEventListener extends AbstractFlushingEventListener // note: performExecutions() clears all collectionXxxxtion // collections (the collection actions) in the session - performExecutions( source ); - postFlush( source ); - - postPostFlush( source ); + final org.hibernate.event.jfr.FlushEvent jfrFlushEvent = JfrEventManager.beginFlushEvent(); + try { + performExecutions( source ); + postFlush( source ); + postPostFlush( source ); + } + finally { + JfrEventManager.completeFlushEvent( jfrFlushEvent, event, true ); + } final StatisticsImplementor statistics = source.getFactory().getStatistics(); if ( statistics.isStatisticsEnabled() ) { statistics.flush(); @@ -70,6 +78,7 @@ public class DefaultAutoFlushEventListener extends AbstractFlushingEventListener } } finally { + JfrEventManager.completePartialFlushEvent( partialFlushEvent, event ); eventListenerManager.partialFlushEnd( event.getNumberOfEntitiesProcessed(), event.getNumberOfEntitiesProcessed() diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java index de2b6c0453..566424b6e4 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java @@ -24,6 +24,8 @@ import org.hibernate.engine.spi.SelfDirtinessTracker; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; +import org.hibernate.event.jfr.DirtyCalculationEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.FlushEntityEvent; import org.hibernate.event.spi.FlushEntityEventListener; @@ -482,11 +484,12 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener final SessionImplementor session = event.getSession(); boolean dirtyCheckPossible; int[] dirtyProperties = null; + final DirtyCalculationEvent dirtyCalculationEvent = JfrEventManager.beginDirtyCalculationEvent(); + final EntityEntry entry = event.getEntityEntry(); + final EntityPersister persister = entry.getPersister(); try { session.getEventListenerManager().dirtyCalculationStart(); // object loaded by update() - final EntityEntry entry = event.getEntityEntry(); - final EntityPersister persister = entry.getPersister(); final Object[] values = event.getPropertyValues(); final Object[] loadedState = entry.getLoadedState(); final Object entity = event.getEntity(); @@ -532,6 +535,7 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener event.setDirtyCheckPossible( dirtyCheckPossible ); } finally { + JfrEventManager.completeDirtyCalculationEvent( dirtyCalculationEvent, session, persister, entry, dirtyProperties ); session.getEventListenerManager().dirtyCalculationEnd( dirtyProperties != null ); } return dirtyProperties; diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEventListener.java index 41d3ddb74e..5f4ac610ab 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEventListener.java @@ -8,6 +8,7 @@ package org.hibernate.event.internal; import org.hibernate.HibernateException; import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.FlushEvent; import org.hibernate.event.spi.FlushEventListener; @@ -28,10 +29,9 @@ public class DefaultFlushEventListener extends AbstractFlushingEventListener imp public void onFlush(FlushEvent event) throws HibernateException { final EventSource source = event.getSession(); final PersistenceContext persistenceContext = source.getPersistenceContextInternal(); - if ( persistenceContext.getNumberOfManagedEntities() > 0 || persistenceContext.getCollectionEntriesSize() > 0 ) { - + final org.hibernate.event.jfr.FlushEvent jfrFlushEvent = JfrEventManager.beginFlushEvent(); try { source.getEventListenerManager().flushStart(); @@ -40,6 +40,7 @@ public class DefaultFlushEventListener extends AbstractFlushingEventListener imp postFlush( source ); } finally { + JfrEventManager.completeFlushEvent( jfrFlushEvent, event ); source.getEventListenerManager().flushEnd( event.getNumberOfEntitiesProcessed(), event.getNumberOfCollectionsProcessed() diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java index f51aad0b8c..c3f9e1c182 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java @@ -127,7 +127,7 @@ public class DefaultInitializeCollectionEventListener implements InitializeColle final SessionFactoryImplementor factory = source.getFactory(); final CollectionDataAccess cacheAccessStrategy = persister.getCacheAccessStrategy(); final Object ck = cacheAccessStrategy.generateCacheKey( id, persister, factory, source.getTenantIdentifier() ); - final Object ce = CacheHelper.fromSharedCache( source, ck, cacheAccessStrategy ); + final Object ce = CacheHelper.fromSharedCache( source, ck, persister, cacheAccessStrategy ); final StatisticsImplementor statistics = factory.getStatistics(); if ( statistics.isStatisticsEnabled() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/CacheGetEvent.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/CacheGetEvent.java new file mode 100644 index 0000000000..295eac52f9 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/CacheGetEvent.java @@ -0,0 +1,55 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event.jfr; + +import org.hibernate.internal.build.AllowNonPortable; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; + +@Name( CacheGetEvent.NAME ) +@Label( "Cache Get Executed" ) +@Category( "Hibernate ORM" ) +@Description( "Cache Get Executed" ) +@StackTrace(false) +@AllowNonPortable +public class CacheGetEvent extends Event { + public static final String NAME = "org.hibernate.orm.CacheGet"; + + @Label( "Session Identifier" ) + public String sessionIdentifier; + + @Label( "Entity Name" ) + public String entityName; + + @Label( "Collection Name" ) + public String collectionName; + + @Label( "Used Natural Id" ) + public boolean isNaturalId; + + @Label( "Region Name" ) + public String regionName; + + @Label( "Cache Get Execution Time" ) + public long executionTime; + + @Label("Cache Hit") + public boolean hit; + + @Override + public String toString() { + return NAME ; + } + + public transient long startedAt; + +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/CachePutEvent.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/CachePutEvent.java new file mode 100644 index 0000000000..fac07b585b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/CachePutEvent.java @@ -0,0 +1,58 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event.jfr; + +import org.hibernate.internal.build.AllowNonPortable; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; + +@Name( CachePutEvent.NAME ) +@Label( "Cache Put Executed" ) +@Category( "Hibernate ORM" ) +@Description( "Cache Put Executed" ) +@StackTrace(false) +@AllowNonPortable +public class CachePutEvent extends Event { + public static final String NAME = "org.hibernate.orm.CachePut"; + + @Label( "Session Identifier" ) + public String sessionIdentifier; + + @Label( "Region Name" ) + public String regionName; + + @Label( "Entity Name" ) + public String entityName; + + @Label( "Collection Name" ) + public String collectionName; + + @Label( "Used Natural Id" ) + public boolean isNaturalId; + + @Label( "Cache Put Execution Time" ) + public long executionTime; + + @Label( "Description" ) + public String description; + + @Label( "Cache Content Has Changed" ) + public boolean cacheChanged; + + @Override + public String toString() { + return NAME ; + } + + public transient long startedAt; + +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/DirtyCalculationEvent.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/DirtyCalculationEvent.java new file mode 100644 index 0000000000..497f4d096e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/DirtyCalculationEvent.java @@ -0,0 +1,48 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event.jfr; + +import org.hibernate.internal.build.AllowNonPortable; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; + +@Name( DirtyCalculationEvent.NAME ) +@Label( "DirtyCalculationEvent Execution" ) +@Category( "Hibernate ORM" ) +@Description( "DirtyCalculationEvent Execution" ) +@StackTrace(false) +@AllowNonPortable +public class DirtyCalculationEvent extends Event { + public static final String NAME = "org.hibernate.orm.DirtyCalculationEvent"; + + @Label( "Session Identifier" ) + public String sessionIdentifier; + + @Label( "PartialFlushEvent time" ) + public long executionTime; + + @Label( "Entity Name" ) + public String entityName; + + @Label( "Entity Status" ) + public String entityStatus; + + @Label( "Found properties" ) + public boolean dirty; + + @Override + public String toString() { + return NAME ; + } + + public transient long startedAt; +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/FlushEvent.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/FlushEvent.java new file mode 100644 index 0000000000..c1718679d9 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/FlushEvent.java @@ -0,0 +1,49 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event.jfr; + +import org.hibernate.internal.build.AllowNonPortable; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; + +@Name( FlushEvent.NAME ) +@Label( "Flush Execution" ) +@Category( "Hibernate ORM" ) +@Description( "Flush Execution" ) +@StackTrace(false) +@AllowNonPortable +public class FlushEvent extends Event { + public static final String NAME = "org.hibernate.orm.FlushEvent"; + + @Label( "Session Identifier" ) + public String sessionIdentifier; + + @Label( "Number Of Processed Entities" ) + public int numberOfEntitiesProcessed; + + @Label( "Number Of Processed Collectionc" ) + public int numberOfCollectionsProcessed; + + @Label( "Flush time" ) + public long executionTime; + + @Label( "Auto Flush" ) + public boolean isAutoFlush; + + @Override + public String toString() { + return NAME ; + } + + public transient long startedAt; + +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcBatchExecutionEvent.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcBatchExecutionEvent.java new file mode 100644 index 0000000000..64f990b260 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcBatchExecutionEvent.java @@ -0,0 +1,41 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event.jfr; + +import org.hibernate.internal.build.AllowNonPortable; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; + +@Name( JdbcBatchExecutionEvent.NAME ) +@Label( "JDBC Batch Execution" ) +@Category( "Hibernate ORM" ) +@Description( "JDBC Batch Execution" ) +@StackTrace(false) +@AllowNonPortable +public class JdbcBatchExecutionEvent extends Event { + public static final String NAME = "org.hibernate.orm.JdbcBatchExecution"; + + @Label("PreparedStatement SQL") + public String sql; + + @Label( "Batch Execution time" ) + public long executionTime; + + @Override + public String toString() { + return NAME ; + } + + public transient long startedAt; + + +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcConnectionAcquisitionEvent.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcConnectionAcquisitionEvent.java new file mode 100644 index 0000000000..fee14bc8c0 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcConnectionAcquisitionEvent.java @@ -0,0 +1,42 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event.jfr; + +import org.hibernate.internal.build.AllowNonPortable; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; + +@Name(JdbcConnectionAcquisitionEvent.NAME ) +@Label( "JDBC Connection Obtained" ) +@Category( "Hibernate ORM" ) +@Description( "JDBC Connection Obtained" ) +@StackTrace(false) +@AllowNonPortable +public class JdbcConnectionAcquisitionEvent extends Event { + public static final String NAME = "org.hibernate.orm.JdbcConnectionAcquisition"; + + @Label("Session Identifier" ) + public String sessionIdentifier; + + @Label( "Tenant Identifier" ) + public String tenantIdentifier; + + @Label( "Connection Acquisition Time" ) + public long executionTime; + + @Override + public String toString() { + return NAME ; + } + + public transient long startedAt; +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcConnectionReleaseEvent.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcConnectionReleaseEvent.java new file mode 100644 index 0000000000..ee7b46b6db --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcConnectionReleaseEvent.java @@ -0,0 +1,43 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event.jfr; + +import org.hibernate.internal.build.AllowNonPortable; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; + +@Name(JdbcConnectionReleaseEvent.NAME ) +@Label( "JDBC Connection Release" ) +@Category( "Hibernate ORM" ) +@Description( "JDBC Connection Released" ) +@StackTrace(false) +@AllowNonPortable +public class JdbcConnectionReleaseEvent extends Event { + public static final String NAME = "org.hibernate.orm.JdbcConnectionRelease"; + + @Label("Session Identifier" ) + public String sessionIdentifier; + + @Label( "Tenant Identifier" ) + public String tenantIdentifier; + + @Label( "Connection Release Time" ) + public long executionTime; + + @Override + public String toString() { + return NAME ; + } + + public transient long startedAt; + +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcPreparedStatementCreationEvent.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcPreparedStatementCreationEvent.java new file mode 100644 index 0000000000..18a14f4ad3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcPreparedStatementCreationEvent.java @@ -0,0 +1,40 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event.jfr; + +import org.hibernate.internal.build.AllowNonPortable; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; + +@Name(JdbcPreparedStatementCreationEvent.NAME) +@Label("JDBC PreparedStatement Created") +@Category("Hibernate ORM") +@Description("JDBC PreparedStatement Created") +@StackTrace(false) +@AllowNonPortable +public class JdbcPreparedStatementCreationEvent extends Event { + public static final String NAME = "org.hibernate.orm.JdbcPreparedStatementCreation"; + + @Label("PreparedStatement SQL") + public String sql; + + @Label("PreparedStatement Creation Time") + public long executionTime; + + @Override + public String toString() { + return NAME; + } + + public transient long startedAt; + +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcPreparedStatementExecutionEvent.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcPreparedStatementExecutionEvent.java new file mode 100644 index 0000000000..4ceab9cef6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/JdbcPreparedStatementExecutionEvent.java @@ -0,0 +1,40 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event.jfr; + +import org.hibernate.internal.build.AllowNonPortable; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; + +@Name(JdbcPreparedStatementExecutionEvent.NAME ) +@Label( "JDBC PreparedStatement Executed" ) +@Category( "Hibernate ORM" ) +@Description( "JDBC PreparedStatement Executed" ) +@StackTrace(false) +@AllowNonPortable +public class JdbcPreparedStatementExecutionEvent extends Event { + public static final String NAME = "org.hibernate.orm.JdbcPreparedStatementExecution"; + + @Label( "PreparedStatement SQL" ) + public String sql; + + @Label( "PreparedStatement Execution Time" ) + public long executionTime; + + @Override + public String toString() { + return NAME ; + } + + public transient long startedAt; + +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/PartialFlushEvent.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/PartialFlushEvent.java new file mode 100644 index 0000000000..d94f54c754 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/PartialFlushEvent.java @@ -0,0 +1,49 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event.jfr; + +import org.hibernate.internal.build.AllowNonPortable; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; + +@Name( PartialFlushEvent.NAME ) +@Label( "PartialFlushEvent Execution" ) +@Category( "Hibernate ORM" ) +@Description( "PartialFlushEvent Execution" ) +@StackTrace(false) +@AllowNonPortable +public class PartialFlushEvent extends Event { + public static final String NAME = "org.hibernate.orm.PartialFlushEvent"; + + @Label( "Session Identifier" ) + public String sessionIdentifier; + + @Label( "Number Of Processed Entities" ) + public int numberOfEntitiesProcessed; + + @Label( "Number Of Processed Collectionc" ) + public int numberOfCollectionsProcessed; + + @Label( "PartialFlushEvent time" ) + public long executionTime; + + @Label( "Auto Flush" ) + public boolean isAutoFlush; + + @Override + public String toString() { + return NAME ; + } + + public transient long startedAt; + +} diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/SessionOpenEvent.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/SessionOpenEvent.java index 379603ecaf..fd2cbe19a4 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/jfr/SessionOpenEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/SessionOpenEvent.java @@ -25,7 +25,7 @@ import jdk.jfr.StackTrace; @StackTrace(false) @AllowNonPortable public class SessionOpenEvent extends Event { - public static final String NAME = "org.hibernate.orm.SessionOpened"; + public static final String NAME = "org.hibernate.orm.SessionOpen"; @Label("Session Identifier" ) public String sessionIdentifier; diff --git a/hibernate-core/src/main/java/org/hibernate/event/jfr/internal/JfrEventManager.java b/hibernate-core/src/main/java/org/hibernate/event/jfr/internal/JfrEventManager.java new file mode 100644 index 0000000000..4c4c0bc818 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/event/jfr/internal/JfrEventManager.java @@ -0,0 +1,479 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.event.jfr.internal; + +import java.sql.PreparedStatement; + +import org.hibernate.cache.spi.Region; +import org.hibernate.cache.spi.access.CachedDomainDataAccess; +import org.hibernate.engine.spi.EntityEntry; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.jfr.CacheGetEvent; +import org.hibernate.event.jfr.CachePutEvent; +import org.hibernate.event.jfr.DirtyCalculationEvent; +import org.hibernate.event.jfr.FlushEvent; +import org.hibernate.event.jfr.JdbcBatchExecutionEvent; +import org.hibernate.event.jfr.JdbcConnectionAcquisitionEvent; +import org.hibernate.event.jfr.JdbcConnectionReleaseEvent; +import org.hibernate.event.jfr.JdbcPreparedStatementCreationEvent; +import org.hibernate.event.jfr.JdbcPreparedStatementExecutionEvent; +import org.hibernate.event.jfr.PartialFlushEvent; +import org.hibernate.event.jfr.SessionClosedEvent; +import org.hibernate.event.jfr.SessionOpenEvent; +import org.hibernate.event.spi.AutoFlushEvent; +import org.hibernate.event.spi.EventSource; +import org.hibernate.internal.build.AllowNonPortable; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.stat.internal.StatsHelper; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +@AllowNonPortable +public class JfrEventManager { + + public static SessionOpenEvent beginSessionOpenEvent() { + final SessionOpenEvent sessionOpenEvent = new SessionOpenEvent(); + if ( sessionOpenEvent.isEnabled() ) { + sessionOpenEvent.begin(); + } + return sessionOpenEvent; + } + + public static void completeSessionOpenEvent( + SessionOpenEvent sessionOpenEvent, + SharedSessionContractImplementor session) { + if ( sessionOpenEvent.isEnabled() ) { + sessionOpenEvent.end(); + if ( sessionOpenEvent.shouldCommit() ) { + sessionOpenEvent.sessionIdentifier = getSessionIdentifier( session ); + sessionOpenEvent.commit(); + } + } + } + + public static SessionClosedEvent beginSessionClosedEvent() { + final SessionClosedEvent sessionClosedEvent = new SessionClosedEvent(); + if ( sessionClosedEvent.isEnabled() ) { + sessionClosedEvent.begin(); + } + return sessionClosedEvent; + } + + public static void completeSessionClosedEvent( + SessionClosedEvent sessionClosedEvent, + SharedSessionContractImplementor session) { + if ( sessionClosedEvent.isEnabled() ) { + sessionClosedEvent.end(); + if ( sessionClosedEvent.shouldCommit() ) { + sessionClosedEvent.sessionIdentifier = getSessionIdentifier( session ); + sessionClosedEvent.commit(); + } + } + } + + public static JdbcConnectionAcquisitionEvent beginJdbcConnectionAcquisitionEvent() { + final JdbcConnectionAcquisitionEvent jdbcConnectionAcquisitionEvent = new JdbcConnectionAcquisitionEvent(); + if ( jdbcConnectionAcquisitionEvent.isEnabled() ) { + jdbcConnectionAcquisitionEvent.begin(); + jdbcConnectionAcquisitionEvent.startedAt = System.nanoTime(); + } + return jdbcConnectionAcquisitionEvent; + } + + public static void completeJdbcConnectionAcquisitionEvent( + JdbcConnectionAcquisitionEvent jdbcConnectionAcquisitionEvent, + SharedSessionContractImplementor session, + String tenantId) { + if ( jdbcConnectionAcquisitionEvent.isEnabled() ) { + jdbcConnectionAcquisitionEvent.end(); + if ( jdbcConnectionAcquisitionEvent.shouldCommit() ) { + jdbcConnectionAcquisitionEvent.executionTime = getExecutionTime( jdbcConnectionAcquisitionEvent.startedAt ); + jdbcConnectionAcquisitionEvent.sessionIdentifier = getSessionIdentifier( session ); + jdbcConnectionAcquisitionEvent.tenantIdentifier = tenantId; + jdbcConnectionAcquisitionEvent.commit(); + } + } + } + + public static JdbcConnectionReleaseEvent beginJdbcConnectionReleaseEvent() { + final JdbcConnectionReleaseEvent jdbcConnectionReleaseEvent = new JdbcConnectionReleaseEvent(); + if ( jdbcConnectionReleaseEvent.isEnabled() ) { + jdbcConnectionReleaseEvent.begin(); + jdbcConnectionReleaseEvent.startedAt = System.nanoTime(); + } + return jdbcConnectionReleaseEvent; + } + + public static void completeJdbcConnectionReleaseEvent( + JdbcConnectionReleaseEvent jdbcConnectionReleaseEvent, + SharedSessionContractImplementor session, + String tenantId) { + if ( jdbcConnectionReleaseEvent.isEnabled() ) { + jdbcConnectionReleaseEvent.end(); + if ( jdbcConnectionReleaseEvent.shouldCommit() ) { + jdbcConnectionReleaseEvent.executionTime = getExecutionTime( jdbcConnectionReleaseEvent.startedAt ); + jdbcConnectionReleaseEvent.sessionIdentifier = getSessionIdentifier( session ); + jdbcConnectionReleaseEvent.tenantIdentifier = tenantId; + jdbcConnectionReleaseEvent.commit(); + } + } + } + + public static JdbcPreparedStatementCreationEvent beginJdbcPreparedStatementCreationEvent() { + final JdbcPreparedStatementCreationEvent jdbcPreparedStatementCreation = new JdbcPreparedStatementCreationEvent(); + if ( jdbcPreparedStatementCreation.isEnabled() ) { + jdbcPreparedStatementCreation.begin(); + jdbcPreparedStatementCreation.startedAt = System.nanoTime(); + } + return jdbcPreparedStatementCreation; + } + + public static void completeJdbcPreparedStatementCreationEvent( + JdbcPreparedStatementCreationEvent jdbcPreparedStatementCreation, + String preparedStatementSql) { + if ( jdbcPreparedStatementCreation.isEnabled() ) { + jdbcPreparedStatementCreation.end(); + if ( jdbcPreparedStatementCreation.shouldCommit() ) { + jdbcPreparedStatementCreation.executionTime = getExecutionTime( jdbcPreparedStatementCreation.startedAt ); + jdbcPreparedStatementCreation.sql = preparedStatementSql; + jdbcPreparedStatementCreation.commit(); + } + } + } + + public static JdbcPreparedStatementExecutionEvent beginJdbcPreparedStatementExecutionEvent() { + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = new JdbcPreparedStatementExecutionEvent(); + if ( jdbcPreparedStatementExecutionEvent.isEnabled() ) { + jdbcPreparedStatementExecutionEvent.begin(); + jdbcPreparedStatementExecutionEvent.startedAt = System.nanoTime(); + } + return jdbcPreparedStatementExecutionEvent; + } + + public static void completeJdbcPreparedStatementExecutionEvent( + JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent, + String preparedStatementSql) { + if ( jdbcPreparedStatementExecutionEvent.isEnabled() ) { + jdbcPreparedStatementExecutionEvent.end(); + if ( jdbcPreparedStatementExecutionEvent.shouldCommit() ) { + jdbcPreparedStatementExecutionEvent.executionTime = getExecutionTime( + jdbcPreparedStatementExecutionEvent.startedAt ); + jdbcPreparedStatementExecutionEvent.sql = preparedStatementSql; + jdbcPreparedStatementExecutionEvent.commit(); + } + } + } + + public static JdbcBatchExecutionEvent beginJdbcBatchExecutionEvent() { + final JdbcBatchExecutionEvent jdbcBatchExecutionEvent = new JdbcBatchExecutionEvent(); + if ( jdbcBatchExecutionEvent.isEnabled() ) { + jdbcBatchExecutionEvent.begin(); + jdbcBatchExecutionEvent.startedAt = System.nanoTime(); + } + return jdbcBatchExecutionEvent; + } + + public static void completeJdbcBatchExecutionEvent( + JdbcBatchExecutionEvent jdbcBatchExecutionEvent, + String statementSql) { + if ( jdbcBatchExecutionEvent.isEnabled() ) { + jdbcBatchExecutionEvent.end(); + if ( jdbcBatchExecutionEvent.shouldCommit() ) { + jdbcBatchExecutionEvent.executionTime = getExecutionTime( jdbcBatchExecutionEvent.startedAt ); + jdbcBatchExecutionEvent.sql = statementSql; + jdbcBatchExecutionEvent.commit(); + } + } + } + + public static CachePutEvent beginCachePutEvent() { + final CachePutEvent cachePutEvent = new CachePutEvent(); + if ( cachePutEvent.isEnabled() ) { + cachePutEvent.begin(); + cachePutEvent.startedAt = System.nanoTime(); + } + return cachePutEvent; + } + + public static void completeCachePutEvent( + CachePutEvent cachePutEvent, + SharedSessionContractImplementor session, + Region region, + boolean cacheContentChanged, + CacheActionDescription description) { + if ( cachePutEvent.isEnabled() ) { + cachePutEvent.end(); + if ( cachePutEvent.shouldCommit() ) { + cachePutEvent.executionTime = getExecutionTime( cachePutEvent.startedAt ); + cachePutEvent.sessionIdentifier = getSessionIdentifier( session ); + cachePutEvent.regionName = region.getName(); + cachePutEvent.description = description.getText(); + cachePutEvent.cacheChanged = cacheContentChanged; + cachePutEvent.commit(); + } + } + } + + public static void completeCachePutEvent( + CachePutEvent cachePutEvent, + SharedSessionContractImplementor session, + CachedDomainDataAccess cachedDomainDataAccess, + EntityPersister persister, + boolean cacheContentChanged, + CacheActionDescription description) { + completeCachePutEvent( + cachePutEvent, + session, + cachedDomainDataAccess, + persister, + cacheContentChanged, + false, + description + ); + } + + public static void completeCachePutEvent( + CachePutEvent cachePutEvent, + SharedSessionContractImplementor session, + CachedDomainDataAccess cachedDomainDataAccess, + EntityPersister persister, + boolean cacheContentChanged, + boolean isNatualId, + CacheActionDescription description) { + if ( cachePutEvent.isEnabled() ) { + cachePutEvent.end(); + if ( cachePutEvent.shouldCommit() ) { + cachePutEvent.executionTime = getExecutionTime( cachePutEvent.startedAt ); + cachePutEvent.sessionIdentifier = getSessionIdentifier( session ); + cachePutEvent.regionName = cachedDomainDataAccess.getRegion().getName(); + cachePutEvent.entityName = getEntityName( persister ); + cachePutEvent.description = description.getText(); + cachePutEvent.isNaturalId = isNatualId; + cachePutEvent.cacheChanged = cacheContentChanged; + cachePutEvent.commit(); + } + } + } + + public static void completeCachePutEvent( + CachePutEvent cachePutEvent, + SharedSessionContractImplementor session, + CachedDomainDataAccess cachedDomainDataAccess, + CollectionPersister persister, + boolean cacheContentChanged, + CacheActionDescription description) { + if ( cachePutEvent.isEnabled() ) { + cachePutEvent.end(); + if ( cachePutEvent.shouldCommit() ) { + cachePutEvent.executionTime = getExecutionTime( cachePutEvent.startedAt ); + cachePutEvent.sessionIdentifier = getSessionIdentifier( session ); + cachePutEvent.regionName = cachedDomainDataAccess.getRegion().getName(); + cachePutEvent.collectionName = persister.getNavigableRole().getFullPath(); + cachePutEvent.description = description.getText(); + cachePutEvent.cacheChanged = cacheContentChanged; + cachePutEvent.commit(); + } + } + } + + public static CacheGetEvent beginCacheGetEvent() { + final CacheGetEvent cacheGetEvent = new CacheGetEvent(); + if ( cacheGetEvent.isEnabled() ) { + cacheGetEvent.begin(); + cacheGetEvent.startedAt = System.nanoTime(); + } + return cacheGetEvent; + } + + public static void completeCacheGetEvent( + CacheGetEvent cacheGetEvent, + SharedSessionContractImplementor session, + Region region, + boolean hit) { + if ( cacheGetEvent.isEnabled() ) { + cacheGetEvent.end(); + if ( cacheGetEvent.shouldCommit() ) { + cacheGetEvent.executionTime = getExecutionTime( cacheGetEvent.startedAt ); + cacheGetEvent.sessionIdentifier = getSessionIdentifier( session ); + cacheGetEvent.regionName = region.getName(); + cacheGetEvent.hit = hit; + cacheGetEvent.commit(); + } + } + } + + public static void completeCacheGetEvent( + CacheGetEvent cacheGetEvent, + SharedSessionContractImplementor session, + Region region, + EntityPersister persister, + boolean isNaturalKey, + boolean hit) { + if ( cacheGetEvent.isEnabled() ) { + cacheGetEvent.end(); + if ( cacheGetEvent.shouldCommit() ) { + cacheGetEvent.executionTime = getExecutionTime( cacheGetEvent.startedAt ); + cacheGetEvent.sessionIdentifier = getSessionIdentifier( session ); + cacheGetEvent.entityName = getEntityName( persister ); + cacheGetEvent.regionName = region.getName(); + cacheGetEvent.isNaturalId = isNaturalKey; + cacheGetEvent.hit = hit; + cacheGetEvent.commit(); + } + } + } + + public static void completeCacheGetEvent( + CacheGetEvent cacheGetEvent, + SharedSessionContractImplementor session, + Region region, + CollectionPersister persister, + boolean hit) { + if ( cacheGetEvent.isEnabled() ) { + cacheGetEvent.end(); + if ( cacheGetEvent.shouldCommit() ) { + cacheGetEvent.executionTime = getExecutionTime( cacheGetEvent.startedAt ); + cacheGetEvent.sessionIdentifier = getSessionIdentifier( session ); + cacheGetEvent.collectionName = persister.getNavigableRole().getFullPath(); + cacheGetEvent.regionName = region.getName(); + cacheGetEvent.hit = hit; + cacheGetEvent.commit(); + } + } + } + + public static FlushEvent beginFlushEvent() { + final FlushEvent flushEvent = new FlushEvent(); + if ( flushEvent.isEnabled() ) { + flushEvent.begin(); + flushEvent.startedAt = System.nanoTime(); + } + return flushEvent; + } + + public static void completeFlushEvent( + FlushEvent flushEvent, + org.hibernate.event.spi.FlushEvent event) { + completeFlushEvent( flushEvent, event, false ); + } + + public static void completeFlushEvent( + FlushEvent flushEvent, + org.hibernate.event.spi.FlushEvent event, + boolean autoFlush) { + if ( flushEvent.isEnabled() ) { + flushEvent.end(); + if ( flushEvent.shouldCommit() ) { + flushEvent.executionTime = getExecutionTime( flushEvent.startedAt ); + EventSource session = event.getSession(); + flushEvent.sessionIdentifier = getSessionIdentifier( session ); + flushEvent.numberOfEntitiesProcessed = event.getNumberOfEntitiesProcessed(); + flushEvent.numberOfCollectionsProcessed = event.getNumberOfCollectionsProcessed(); + flushEvent.isAutoFlush = autoFlush; + flushEvent.commit(); + } + } + } + + public static PartialFlushEvent beginPartialFlushEvent() { + final PartialFlushEvent partialFlushEvent = new PartialFlushEvent(); + if ( partialFlushEvent.isEnabled() ) { + partialFlushEvent.startedAt = System.nanoTime(); + partialFlushEvent.begin(); + } + return partialFlushEvent; + } + + public static void completePartialFlushEvent( + PartialFlushEvent flushEvent, + AutoFlushEvent event) { + if ( flushEvent.isEnabled() ) { + flushEvent.end(); + if ( flushEvent.shouldCommit() ) { + flushEvent.executionTime = getExecutionTime( flushEvent.startedAt ); + EventSource session = event.getSession(); + flushEvent.sessionIdentifier = getSessionIdentifier( session ); + flushEvent.numberOfEntitiesProcessed = event.getNumberOfEntitiesProcessed(); + flushEvent.numberOfCollectionsProcessed = event.getNumberOfCollectionsProcessed(); + flushEvent.isAutoFlush = true; + flushEvent.commit(); + } + } + } + + public static DirtyCalculationEvent beginDirtyCalculationEvent() { + final DirtyCalculationEvent dirtyCalculationEvent = new DirtyCalculationEvent(); + if ( dirtyCalculationEvent.isEnabled() ) { + dirtyCalculationEvent.startedAt = System.nanoTime(); + dirtyCalculationEvent.begin(); + } + return dirtyCalculationEvent; + } + + public static void completeDirtyCalculationEvent( + DirtyCalculationEvent dirtyCalculationEvent, + SharedSessionContractImplementor session, + EntityPersister persister, + EntityEntry entry, + int[] dirtyProperties) { + if ( dirtyCalculationEvent.isEnabled() ) { + dirtyCalculationEvent.end(); + if ( dirtyCalculationEvent.shouldCommit() ) { + dirtyCalculationEvent.executionTime = getExecutionTime( dirtyCalculationEvent.startedAt ); + dirtyCalculationEvent.sessionIdentifier = getSessionIdentifier( session ); + dirtyCalculationEvent.entityName = getEntityName( persister ); + dirtyCalculationEvent.entityStatus = entry.getStatus().name(); + dirtyCalculationEvent.dirty = dirtyProperties != null; + dirtyCalculationEvent.commit(); + } + } + } + + public enum CacheActionDescription { + ENTITY_INSERT( "Entity Insert" ), + ENTITY_AFTER_INSERT( "Entity After Insert" ), + ENTITY_UPDATE( "Entity Update" ), + ENTITY_LOAD( "Entity Load" ), + ENTITY_AFTER_UPDATE( "Entity After Update" ), + TIMESTAMP_PRE_INVALIDATE( "Timestamp Pre Invalidate" ), + TIMESTAMP_INVALIDATE( "Timestamp Invalidate" ), + COLLECTION_INSERT( "Collection Insert" ), + QUERY_RESULT( "Query Result" ); + + + private final String text; + + CacheActionDescription(String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } + + public String getText() { + return text; + } + } + + private static long getExecutionTime(Long startTime) { + return NANOSECONDS.convert( System.nanoTime() - startTime, NANOSECONDS ); + } + + private static String getSessionIdentifier(SharedSessionContractImplementor session) { + if ( session == null ) { + return null; + } + return session.getSessionIdentifier().toString(); + } + + private static String getEntityName(EntityPersister persister) { + return StatsHelper.INSTANCE.getRootEntityRole( persister ).getFullPath(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java index 8f9f9ee20b..996892f36e 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java @@ -34,6 +34,9 @@ import org.hibernate.engine.jdbc.internal.FormatStyle; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.jfr.JdbcPreparedStatementExecutionEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; +import org.hibernate.event.jfr.JdbcPreparedStatementCreationEvent; import org.hibernate.id.ExportableColumn; import org.hibernate.id.IdentifierGeneratorHelper; import org.hibernate.id.IntegralDataTypeHolder; @@ -593,7 +596,7 @@ public class TableGenerator implements PersistentIdentifierGenerator { try ( PreparedStatement selectPS = prepareStatement( connection, selectQuery, logger, listener ) ) { selectPS.setString( 1, segmentValue ); - final ResultSet selectRS = executeQuery( selectPS, listener ); + final ResultSet selectRS = executeQuery( selectPS, listener, selectQuery ); if ( !selectRS.next() ) { long initializationValue; if ( storeLastUsedValue ) { @@ -608,7 +611,7 @@ public class TableGenerator implements PersistentIdentifierGenerator { LOG.tracef( "binding parameter [%s] - [%s]", 1, segmentValue ); statement.setString( 1, segmentValue ); value.bind( statement, 2 ); - executeUpdate( statement, listener); + executeUpdate( statement, listener, insertQuery); } } else { @@ -640,7 +643,7 @@ public class TableGenerator implements PersistentIdentifierGenerator { updateValue.bind( statement, 1 ); value.bind( statement, 2 ); statement.setString( 3, segmentValue ); - rows = executeUpdate( statement, listener ); + rows = executeUpdate( statement, listener, updateQuery ); } catch (SQLException e) { LOG.unableToUpdateQueryHiValue( physicalTableName.render(), e ); @@ -664,32 +667,37 @@ public class TableGenerator implements PersistentIdentifierGenerator { SqlStatementLogger logger, SessionEventListenerManager listener) throws SQLException { logger.logStatement( sql, FormatStyle.BASIC.getFormatter() ); + final JdbcPreparedStatementCreationEvent jdbcPreparedStatementCreation = JfrEventManager.beginJdbcPreparedStatementCreationEvent(); try { listener.jdbcPrepareStatementStart(); return connection.prepareStatement( sql ); } finally { + JfrEventManager.completeJdbcPreparedStatementCreationEvent( jdbcPreparedStatementCreation, sql ); listener.jdbcPrepareStatementEnd(); } } - private int executeUpdate(PreparedStatement ps, SessionEventListenerManager listener) throws SQLException { + private int executeUpdate(PreparedStatement ps, SessionEventListenerManager listener, String sql ) throws SQLException { + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = JfrEventManager.beginJdbcPreparedStatementExecutionEvent(); try { listener.jdbcExecuteStatementStart(); return ps.executeUpdate(); } finally { + JfrEventManager.completeJdbcPreparedStatementExecutionEvent( jdbcPreparedStatementExecutionEvent, sql ); listener.jdbcExecuteStatementEnd(); } - } - private ResultSet executeQuery(PreparedStatement ps, SessionEventListenerManager listener) throws SQLException { + private ResultSet executeQuery(PreparedStatement ps, SessionEventListenerManager listener, String sql ) throws SQLException { + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = JfrEventManager.beginJdbcPreparedStatementExecutionEvent(); try { listener.jdbcExecuteStatementStart(); return ps.executeQuery(); } finally { + JfrEventManager.completeJdbcPreparedStatementExecutionEvent( jdbcPreparedStatementExecutionEvent, sql ); listener.jdbcExecuteStatementEnd(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java index 0d1527476c..42703c5dd6 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java @@ -26,6 +26,9 @@ import org.hibernate.engine.jdbc.internal.FormatStyle; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.jfr.JdbcPreparedStatementExecutionEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; +import org.hibernate.event.jfr.JdbcPreparedStatementCreationEvent; import org.hibernate.id.ExportableColumn; import org.hibernate.id.IdentifierGenerationException; import org.hibernate.id.IdentifierGeneratorHelper; @@ -143,7 +146,7 @@ public class TableStructure implements DatabaseStructure { statementLogger, statsCollector )) { - final ResultSet selectRS = executeQuery( selectStatement, statsCollector ); + final ResultSet selectRS = executeQuery( selectStatement, statsCollector, selectQuery ); if ( !selectRS.next() ) { final String err = "could not read a hi value - you need to populate the table: " + physicalTableName; LOG.error( err ); @@ -168,7 +171,7 @@ public class TableStructure implements DatabaseStructure { final IntegralDataTypeHolder updateValue = value.copy().add( increment ); updateValue.bind( updatePS, 1 ); value.bind( updatePS, 2 ); - rows = executeUpdate( updatePS, statsCollector ); + rows = executeUpdate( updatePS, statsCollector, updateQuery ); } catch (SQLException e) { LOG.unableToUpdateQueryHiValue( physicalTableName.render(), e ); @@ -198,32 +201,38 @@ public class TableStructure implements DatabaseStructure { SqlStatementLogger statementLogger, SessionEventListenerManager statsCollector) throws SQLException { statementLogger.logStatement( sql, FormatStyle.BASIC.getFormatter() ); + final JdbcPreparedStatementCreationEvent jdbcPreparedStatementCreation = JfrEventManager.beginJdbcPreparedStatementCreationEvent(); try { statsCollector.jdbcPrepareStatementStart(); return connection.prepareStatement( sql ); } finally { + JfrEventManager.completeJdbcPreparedStatementCreationEvent( jdbcPreparedStatementCreation, sql ); statsCollector.jdbcPrepareStatementEnd(); } } - private int executeUpdate(PreparedStatement ps, SessionEventListenerManager statsCollector) throws SQLException { + private int executeUpdate(PreparedStatement ps, SessionEventListenerManager statsCollector, String sql ) throws SQLException { + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = JfrEventManager.beginJdbcPreparedStatementExecutionEvent(); try { statsCollector.jdbcExecuteStatementStart(); return ps.executeUpdate(); } finally { + JfrEventManager.completeJdbcPreparedStatementExecutionEvent( jdbcPreparedStatementExecutionEvent, sql ); statsCollector.jdbcExecuteStatementEnd(); } } - private ResultSet executeQuery(PreparedStatement ps, SessionEventListenerManager statsCollector) throws SQLException { + private ResultSet executeQuery(PreparedStatement ps, SessionEventListenerManager statsCollector, String sql ) throws SQLException { + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = JfrEventManager.beginJdbcPreparedStatementExecutionEvent(); try { statsCollector.jdbcExecuteStatementStart(); return ps.executeQuery(); } finally { + JfrEventManager.completeJdbcPreparedStatementExecutionEvent( jdbcPreparedStatementExecutionEvent, sql ); statsCollector.jdbcExecuteStatementEnd(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 178b4cce37..b99765a935 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -602,14 +602,16 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont if ( ! fastSessionServices.requiresMultiTenantConnectionProvider ) { jdbcConnectionAccess = new NonContextualJdbcConnectionAccess( getEventListenerManager(), - fastSessionServices.connectionProvider + fastSessionServices.connectionProvider, + this ); } else { jdbcConnectionAccess = new ContextualJdbcConnectionAccess( getTenantIdentifier(), getEventListenerManager(), - fastSessionServices.multiTenantConnectionProvider + fastSessionServices.multiTenantConnectionProvider, + this ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/ContextualJdbcConnectionAccess.java b/hibernate-core/src/main/java/org/hibernate/internal/ContextualJdbcConnectionAccess.java index 0219d70be1..2fa07fb74b 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/ContextualJdbcConnectionAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/ContextualJdbcConnectionAccess.java @@ -14,6 +14,10 @@ import org.hibernate.HibernateException; import org.hibernate.SessionEventListener; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.jfr.internal.JfrEventManager; +import org.hibernate.event.jfr.JdbcConnectionAcquisitionEvent; +import org.hibernate.event.jfr.JdbcConnectionReleaseEvent; /** * @author Steve Ebersole @@ -22,14 +26,18 @@ public class ContextualJdbcConnectionAccess implements JdbcConnectionAccess, Ser private final String tenantIdentifier; private final SessionEventListener listener; private final MultiTenantConnectionProvider connectionProvider; + private final SharedSessionContractImplementor session; + public ContextualJdbcConnectionAccess( String tenantIdentifier, SessionEventListener listener, - MultiTenantConnectionProvider connectionProvider) { + MultiTenantConnectionProvider connectionProvider, + SharedSessionContractImplementor session) { this.tenantIdentifier = tenantIdentifier; this.listener = listener; this.connectionProvider = connectionProvider; + this.session = session; } @Override @@ -38,11 +46,17 @@ public class ContextualJdbcConnectionAccess implements JdbcConnectionAccess, Ser throw new HibernateException( "Tenant identifier required" ); } + final JdbcConnectionAcquisitionEvent jdbcConnectionAcquisitionEvent = JfrEventManager.beginJdbcConnectionAcquisitionEvent(); try { listener.jdbcConnectionAcquisitionStart(); return connectionProvider.getConnection( tenantIdentifier ); } finally { + JfrEventManager.completeJdbcConnectionAcquisitionEvent( + jdbcConnectionAcquisitionEvent, + session, + tenantIdentifier + ); listener.jdbcConnectionAcquisitionEnd(); } } @@ -53,11 +67,13 @@ public class ContextualJdbcConnectionAccess implements JdbcConnectionAccess, Ser throw new HibernateException( "Tenant identifier required" ); } + final JdbcConnectionReleaseEvent jdbcConnectionReleaseEvent = JfrEventManager.beginJdbcConnectionReleaseEvent(); try { listener.jdbcConnectionReleaseStart(); connectionProvider.releaseConnection( tenantIdentifier, connection ); } finally { + JfrEventManager.completeJdbcConnectionReleaseEvent( jdbcConnectionReleaseEvent, session, tenantIdentifier ); listener.jdbcConnectionReleaseEnd(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/NonContextualJdbcConnectionAccess.java b/hibernate-core/src/main/java/org/hibernate/internal/NonContextualJdbcConnectionAccess.java index 82df15f40f..42143d22fa 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/NonContextualJdbcConnectionAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/NonContextualJdbcConnectionAccess.java @@ -14,6 +14,10 @@ import java.util.Objects; import org.hibernate.SessionEventListener; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.jfr.internal.JfrEventManager; +import org.hibernate.event.jfr.JdbcConnectionAcquisitionEvent; +import org.hibernate.event.jfr.JdbcConnectionReleaseEvent; /** * @author Steve Ebersole @@ -21,34 +25,45 @@ import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; public class NonContextualJdbcConnectionAccess implements JdbcConnectionAccess, Serializable { private final SessionEventListener listener; private final ConnectionProvider connectionProvider; + private final SharedSessionContractImplementor session; public NonContextualJdbcConnectionAccess( SessionEventListener listener, - ConnectionProvider connectionProvider) { + ConnectionProvider connectionProvider, + SharedSessionContractImplementor session) { Objects.requireNonNull( listener ); Objects.requireNonNull( connectionProvider ); this.listener = listener; this.connectionProvider = connectionProvider; + this.session = session; } @Override public Connection obtainConnection() throws SQLException { + final JdbcConnectionAcquisitionEvent jdbcConnectionAcquisitionEvent = JfrEventManager.beginJdbcConnectionAcquisitionEvent(); try { listener.jdbcConnectionAcquisitionStart(); return connectionProvider.getConnection(); } finally { + JfrEventManager.completeJdbcConnectionAcquisitionEvent( + jdbcConnectionAcquisitionEvent, + session, + null + ); listener.jdbcConnectionAcquisitionEnd(); } } @Override public void releaseConnection(Connection connection) throws SQLException { + final JdbcConnectionReleaseEvent jdbcConnectionReleaseEvent = JfrEventManager.beginJdbcConnectionReleaseEvent(); try { listener.jdbcConnectionReleaseStart(); connectionProvider.closeConnection( connection ); } finally { + JfrEventManager.completeJdbcConnectionReleaseEvent( jdbcConnectionReleaseEvent, session, null ); listener.jdbcConnectionReleaseEnd(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index eb44f02607..a9785b52e1 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -18,7 +18,6 @@ import java.sql.Connection; import java.sql.NClob; import java.sql.SQLException; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; @@ -69,6 +68,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; import org.hibernate.engine.transaction.spi.TransactionImplementor; import org.hibernate.engine.transaction.spi.TransactionObserver; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.event.jfr.SessionClosedEvent; import org.hibernate.event.jfr.SessionOpenEvent; import org.hibernate.event.spi.AutoFlushEvent; @@ -108,9 +108,7 @@ import org.hibernate.event.spi.ResolveNaturalIdEventListener; import org.hibernate.event.spi.SaveOrUpdateEvent; import org.hibernate.event.spi.SaveOrUpdateEventListener; import org.hibernate.graph.GraphSemantic; -import org.hibernate.graph.internal.RootGraphImpl; import org.hibernate.graph.spi.RootGraphImplementor; -import org.hibernate.internal.build.AllowNonPortable; import org.hibernate.internal.util.ExceptionHelper; import org.hibernate.jpa.internal.LegacySpecHelper; import org.hibernate.jpa.internal.util.ConfigurationHelper; @@ -141,7 +139,6 @@ import org.hibernate.type.descriptor.WrapperOptions; import jakarta.persistence.CacheRetrieveMode; import jakarta.persistence.CacheStoreMode; -import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityNotFoundException; import jakarta.persistence.FlushModeType; @@ -228,14 +225,10 @@ public class SessionImpl // TODO: this is unused and can be removed private transient boolean isEnforcingFetchGraph; - @AllowNonPortable public SessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) { super( factory, options ); - final SessionOpenEvent sessionOpenEvent = new SessionOpenEvent(); - if ( sessionOpenEvent.isEnabled() ) { - sessionOpenEvent.begin(); - } + final SessionOpenEvent sessionOpenEvent = JfrEventManager.beginSessionOpenEvent(); persistenceContext = createPersistenceContext(); actionQueue = createActionQueue(); @@ -282,13 +275,7 @@ public class SessionImpl log.tracef( "Opened Session [%s] at timestamp: %s", getSessionIdentifier(), currentTimeMillis() ); } - if ( sessionOpenEvent.isEnabled() ) { - sessionOpenEvent.end(); - if ( sessionOpenEvent.shouldCommit() ) { - sessionOpenEvent.sessionIdentifier = getSessionIdentifier().toString(); - sessionOpenEvent.commit(); - } - } + JfrEventManager.completeSessionOpenEvent( sessionOpenEvent, this ); } private FlushMode getInitialFlushMode() { @@ -425,16 +412,12 @@ public class SessionImpl closeWithoutOpenChecks(); } - @AllowNonPortable public void closeWithoutOpenChecks() throws HibernateException { if ( log.isTraceEnabled() ) { log.tracef( "Closing session [%s]", getSessionIdentifier() ); } - final SessionClosedEvent sessionClosedEvent = new SessionClosedEvent(); - if ( sessionClosedEvent.isEnabled() ) { - sessionClosedEvent.begin(); - } + final SessionClosedEvent sessionClosedEvent = JfrEventManager.beginSessionClosedEvent(); // todo : we want this check if usage is JPA, but not native Hibernate usage final SessionFactoryImplementor sessionFactory = getSessionFactory(); @@ -459,13 +442,7 @@ public class SessionImpl statistics.closeSession(); } - if ( sessionClosedEvent.isEnabled() ) { - sessionClosedEvent.end(); - if ( sessionClosedEvent.shouldCommit() ) { - sessionClosedEvent.sessionIdentifier = getSessionIdentifier().toString(); - sessionClosedEvent.commit(); - } - } + JfrEventManager.completeSessionClosedEvent( sessionClosedEvent, this ); } private boolean isTransactionInProgressAndNotMarkedForRollback() { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CacheEntityLoaderHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CacheEntityLoaderHelper.java index e6dc71f90b..e21cdc09d6 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CacheEntityLoaderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CacheEntityLoaderHelper.java @@ -273,7 +273,7 @@ public class CacheEntityLoaderHelper { source.getTenantIdentifier() ); - final Object ce = CacheHelper.fromSharedCache( source, ck, persister.getCacheAccessStrategy() ); + final Object ce = CacheHelper.fromSharedCache( source, ck, persister, persister.getCacheAccessStrategy() ); final StatisticsImplementor statistics = factory.getStatistics(); if ( statistics.isStatisticsEnabled() ) { if ( ce == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index e6b55e87e3..aedae13850 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -1537,7 +1537,7 @@ public abstract class AbstractEntityPersister if ( session.getCacheMode().isGetEnabled() && canReadFromCache() && isLazyPropertiesCacheable() ) { final EntityDataAccess cacheAccess = getCacheAccessStrategy(); final Object cacheKey = cacheAccess.generateCacheKey(id, this, session.getFactory(), session.getTenantIdentifier() ); - final Object ce = CacheHelper.fromSharedCache( session, cacheKey, cacheAccess ); + final Object ce = CacheHelper.fromSharedCache( session, cacheKey, this, cacheAccess ); if ( ce != null ) { final CacheEntry cacheEntry = (CacheEntry) getCacheEntryStructure().destructure( ce, factory ); final Object initializedValue = initializeLazyPropertiesFromCache( fieldName, entity, session, entry, cacheEntry ); @@ -4148,7 +4148,7 @@ public abstract class AbstractEntityPersister final EntityDataAccess cache = getCacheAccessStrategy(); final String tenantId = session.getTenantIdentifier(); final Object ck = cache.generateCacheKey( id, this, session.getFactory(), tenantId ); - final Object ce = CacheHelper.fromSharedCache( session, ck, getCacheAccessStrategy() ); + final Object ce = CacheHelper.fromSharedCache( session, ck, this, getCacheAccessStrategy() ); if ( ce != null ) { return false; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/StandardJdbcMutationExecutor.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/StandardJdbcMutationExecutor.java index 4b2a337005..d23731591b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/StandardJdbcMutationExecutor.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/StandardJdbcMutationExecutor.java @@ -13,6 +13,8 @@ import java.util.function.Function; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.jfr.JdbcPreparedStatementExecutionEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.query.spi.QueryOptions; import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor; import org.hibernate.sql.exec.spi.ExecutionContext; @@ -79,12 +81,14 @@ public class StandardJdbcMutationExecutor implements JdbcMutationExecutor { } session.getEventListenerManager().jdbcExecuteStatementStart(); + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = JfrEventManager.beginJdbcPreparedStatementExecutionEvent(); try { int rows = preparedStatement.executeUpdate(); expectationCheck.accept( rows, preparedStatement ); return rows; } finally { + JfrEventManager.completeJdbcPreparedStatementExecutionEvent( jdbcPreparedStatementExecutionEvent, finalSql ); session.getEventListenerManager().jdbcExecuteStatementEnd(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java index 9745ee4b4c..bc7447423e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java @@ -28,6 +28,8 @@ import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; +import org.hibernate.event.jfr.CachePutEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.event.spi.PreLoadEvent; import org.hibernate.event.spi.PreLoadEventListener; import org.hibernate.internal.util.StringHelper; @@ -977,19 +979,35 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces // // we need to be careful not to clobber the lock here in the cache so that it can be rolled back if need be if ( persistenceContext.wasInsertedDuringTransaction( concreteDescriptor, entityIdentifier) ) { - cacheAccess.update( - session, - cacheKey, - concreteDescriptor.getCacheEntryStructure().structure( cacheEntry ), - version, - version - ); + boolean update = false; + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); + try { + update = cacheAccess.update( + session, + cacheKey, + concreteDescriptor.getCacheEntryStructure().structure( cacheEntry ), + version, + version + ); + } + finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session, + cacheAccess, + concreteDescriptor, + update, + JfrEventManager.CacheActionDescription.ENTITY_UPDATE + ); + } } else { final SessionEventListenerManager eventListenerManager = session.getEventListenerManager(); + boolean put = false; + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); try { eventListenerManager.cachePutStart(); - final boolean put = cacheAccess.putFromLoad( + put = cacheAccess.putFromLoad( session, cacheKey, concreteDescriptor.getCacheEntryStructure().structure( cacheEntry ), @@ -997,13 +1015,20 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces //useMinimalPuts( session, entityEntry ) false ); - + } + finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session, + cacheAccess, + concreteDescriptor, + put, + JfrEventManager.CacheActionDescription.ENTITY_LOAD + ); final StatisticsImplementor statistics = factory.getStatistics(); if ( put && statistics.isStatisticsEnabled() ) { statistics.entityCachePut( rootEntityDescriptor.getNavigableRole(), cacheAccess.getRegion().getName() ); } - } - finally { eventListenerManager.cachePutEnd(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java index 5febe87525..1d18124070 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java @@ -22,6 +22,8 @@ import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.jfr.CachePutEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.metamodel.mapping.ModelPart; @@ -286,9 +288,11 @@ public class ResultsHelper { // CollectionRegionAccessStrategy has no update, so avoid putting uncommitted data via putFromLoad if ( isPutFromLoad ) { final SessionEventListenerManager eventListenerManager = session.getEventListenerManager(); + final CachePutEvent cachePutEvent = JfrEventManager.beginCachePutEvent(); + boolean put = false; try { eventListenerManager.cachePutStart(); - final boolean put = cacheAccess.putFromLoad( + put = cacheAccess.putFromLoad( session, cacheKey, collectionDescriptor.getCacheEntryStructure().structure( entry ), @@ -296,6 +300,17 @@ public class ResultsHelper { factory.getSessionFactoryOptions().isMinimalPutsEnabled() && session.getCacheMode()!= CacheMode.REFRESH ); + } + finally { + JfrEventManager.completeCachePutEvent( + cachePutEvent, + session, + cacheAccess, + collectionDescriptor, + put, + JfrEventManager.CacheActionDescription.COLLECTION_INSERT + ); + eventListenerManager.cachePutEnd(); final StatisticsImplementor statistics = factory.getStatistics(); if ( put && statistics.isStatisticsEnabled() ) { @@ -304,9 +319,7 @@ public class ResultsHelper { collectionDescriptor.getCacheAccessStrategy().getRegion().getName() ); } - } - finally { - eventListenerManager.cachePutEnd(); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java index a9c5e83667..55898424df 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java @@ -20,6 +20,8 @@ import org.hibernate.dialect.pagination.NoopLimitHandler; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.event.jfr.JdbcPreparedStatementExecutionEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.query.spi.Limit; @@ -235,11 +237,13 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess { if ( sqlStatementLogger.getLogSlowQuery() > 0 ) { executeStartNanos = System.nanoTime(); } + final JdbcPreparedStatementExecutionEvent jdbcPreparedStatementExecutionEvent = JfrEventManager.beginJdbcPreparedStatementExecutionEvent(); try { eventListenerManager.jdbcExecuteStatementStart(); resultSet = wrapResultSet( preparedStatement.executeQuery() ); } finally { + JfrEventManager.completeJdbcPreparedStatementExecutionEvent( jdbcPreparedStatementExecutionEvent, finalSql ); eventListenerManager.jdbcExecuteStatementEnd(); sqlStatementLogger.logSlowQuery( finalSql, executeStartNanos, context() ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jdbc/internal/AggressiveReleaseTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jdbc/internal/AggressiveReleaseTest.java index e9c57cd1c5..eac9332b15 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jdbc/internal/AggressiveReleaseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jdbc/internal/AggressiveReleaseTest.java @@ -124,11 +124,12 @@ public class AggressiveReleaseTest extends BaseSessionFactoryFunctionalTest { JdbcCoordinatorImpl jdbcCoord = (JdbcCoordinatorImpl) session.getJdbcCoordinator(); ResourceRegistry resourceRegistry = jdbcCoord.getLogicalConnection().getResourceRegistry(); try { + String sql = "insert into SANDBOX_JDBC_TST( ID, NAME ) values ( ?, ? )"; PreparedStatement ps = jdbcCoord.getStatementPreparer().prepareStatement( - "insert into SANDBOX_JDBC_TST( ID, NAME ) values ( ?, ? )" ); + sql ); ps.setLong( 1, 1 ); ps.setString( 2, "name" ); - jdbcCoord.getResultSetReturn().execute( ps ); + jdbcCoord.getResultSetReturn().execute( ps, sql ); assertTrue( jdbcCoord.getLogicalConnection().getResourceRegistry().hasRegisteredResources() ); assertEquals( 1, connectionProvider.getAcquiredConnections().size() ); assertEquals( 0, connectionProvider.getReleasedConnections().size() ); @@ -161,11 +162,12 @@ public class AggressiveReleaseTest extends BaseSessionFactoryFunctionalTest { ResourceRegistry resourceRegistry = jdbcCoord.getLogicalConnection().getResourceRegistry(); try { + String sql = "insert into SANDBOX_JDBC_TST( ID, NAME ) values ( ?, ? )"; PreparedStatement ps = jdbcCoord.getStatementPreparer().prepareStatement( - "insert into SANDBOX_JDBC_TST( ID, NAME ) values ( ?, ? )" ); + sql ); ps.setLong( 1, 1 ); ps.setString( 2, "name" ); - jdbcCoord.getResultSetReturn().execute( ps ); + jdbcCoord.getResultSetReturn().execute( ps , sql); assertTrue( resourceRegistry.hasRegisteredResources() ); assertEquals( 1, connectionProvider.getAcquiredConnections().size() ); assertEquals( 0, connectionProvider.getReleasedConnections().size() ); @@ -177,7 +179,7 @@ public class AggressiveReleaseTest extends BaseSessionFactoryFunctionalTest { assertEquals( 1, connectionProvider.getReleasedConnections().size() ); // open a result set and hold it open... - final String sql = "select * from SANDBOX_JDBC_TST"; + sql = "select * from SANDBOX_JDBC_TST"; ps = jdbcCoord.getStatementPreparer().prepareStatement( sql ); jdbcCoord.getResultSetReturn().extract( ps, sql ); assertTrue( resourceRegistry.hasRegisteredResources() ); @@ -225,11 +227,12 @@ public class AggressiveReleaseTest extends BaseSessionFactoryFunctionalTest { ResourceRegistry resourceRegistry = jdbcCoord.getLogicalConnection().getResourceRegistry(); try { + String sql = "insert into SANDBOX_JDBC_TST( ID, NAME ) values ( ?, ? )"; PreparedStatement ps = jdbcCoord.getStatementPreparer().prepareStatement( - "insert into SANDBOX_JDBC_TST( ID, NAME ) values ( ?, ? )" ); + sql ); ps.setLong( 1, 1 ); ps.setString( 2, "name" ); - jdbcCoord.getResultSetReturn().execute( ps ); + jdbcCoord.getResultSetReturn().execute( ps , sql); assertTrue( resourceRegistry.hasRegisteredResources() ); assertEquals( 1, connectionProvider.getAcquiredConnections().size() ); assertEquals( 0, connectionProvider.getReleasedConnections().size() ); @@ -243,7 +246,7 @@ public class AggressiveReleaseTest extends BaseSessionFactoryFunctionalTest { jdbcCoord.disableReleases(); // open a result set... - final String sql = "select * from SANDBOX_JDBC_TST"; + sql = "select * from SANDBOX_JDBC_TST"; ps = jdbcCoord.getStatementPreparer().prepareStatement( sql ); jdbcCoord.getResultSetReturn().extract( ps, sql ); assertTrue( resourceRegistry.hasRegisteredResources() ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jdbc/internal/BasicConnectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jdbc/internal/BasicConnectionTest.java index 922aa6e463..96776f66d2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jdbc/internal/BasicConnectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jdbc/internal/BasicConnectionTest.java @@ -38,9 +38,10 @@ public class BasicConnectionTest extends BaseCoreFunctionalTestCase { SessionImplementor sessionImpl = (SessionImplementor) session; boolean caught = false; try { + String sql = "select count(*) from NON_EXISTENT"; PreparedStatement ps = sessionImpl.getJdbcCoordinator().getStatementPreparer() - .prepareStatement( "select count(*) from NON_EXISTENT" ); - sessionImpl.getJdbcCoordinator().getResultSetReturn().execute( ps ); + .prepareStatement( sql ); + sessionImpl.getJdbcCoordinator().getResultSetReturn().execute( ps, sql ); } catch ( JDBCException ok ) { caught = true; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/refcursor/CursorFromCallableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/refcursor/CursorFromCallableTest.java index 4d860c8270..783f29d2c9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/refcursor/CursorFromCallableTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/refcursor/CursorFromCallableTest.java @@ -116,7 +116,7 @@ public class CursorFromCallableTest extends BaseCoreFunctionalTestCase { PreparedStatement preparedStatement = null; try { preparedStatement = statementPreparer.prepareStatement( sql ); - resultSetReturn.execute( preparedStatement ); + resultSetReturn.execute( preparedStatement, sql ); } finally { if ( preparedStatement != null ) { diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/DirtyCalculationEventTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/DirtyCalculationEventTests.java new file mode 100644 index 0000000000..d057514096 --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/DirtyCalculationEventTests.java @@ -0,0 +1,96 @@ +package org.hibernate.orm.test.event.jfr; + +import java.util.List; + +import org.hibernate.engine.spi.Status; +import org.hibernate.event.jfr.DirtyCalculationEvent; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel(annotatedClasses = { + DirtyCalculationEventTests.TestEntity.class, +}) +@SessionFactory +public class DirtyCalculationEventTests { + public JfrEvents jfrEvents = new JfrEvents(); + + @BeforeAll + public void setUp(SessionFactoryScope scope){ + scope.inTransaction( + session ->{ + TestEntity entity = new TestEntity( 1, "name_1" ); + session.persist( entity ); + } + ); + } + + @Test + @EnableEvent(DirtyCalculationEvent.NAME) + public void testFlushEvent(SessionFactoryScope scope) { + jfrEvents.reset(); + String sessionId = scope.fromTransaction( + session -> { + TestEntity testEntity = session.load( TestEntity.class, 1 ); + testEntity.setName( "new name" ); + return session.getSessionIdentifier().toString(); + } + ); + List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( DirtyCalculationEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 1 ); + + RecordedEvent event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( DirtyCalculationEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( sessionId ); + assertThat( event.getString( "entityName" ) ) + .isEqualTo( TestEntity.class.getName() ); + assertThat( event.getString( "entityStatus" ) ) + .isEqualTo( Status.MANAGED.name() ); + assertThat( event.getBoolean( "dirty" ) ) + .isTrue(); + } + + @Entity(name = "TestEntity") + public static class TestEntity { + @Id + private Integer id; + + private String name; + + public TestEntity() { + } + + public TestEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + } + +} diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/JdbcBatchExecutionEventTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/JdbcBatchExecutionEventTests.java new file mode 100644 index 0000000000..e1864298ed --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/JdbcBatchExecutionEventTests.java @@ -0,0 +1,112 @@ +package org.hibernate.orm.test.event.jfr; + +import java.util.List; +import java.util.Locale; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.event.jfr.JdbcBatchExecutionEvent; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel(annotatedClasses = { + JdbcBatchExecutionEventTests.TestEntity.class +}) +@SessionFactory +@ServiceRegistry( + settings = @Setting(name = AvailableSettings.STATEMENT_BATCH_SIZE, value = "5") +) +public class JdbcBatchExecutionEventTests { + public JfrEvents jfrEvents = new JfrEvents(); + + + @Test + @EnableEvent(JdbcBatchExecutionEvent.NAME) + public void testJdbcBatchExecutionEvent(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + for ( int i = 2; i < 10; i++ ) { + TestEntity entity = new TestEntity( i, "name_" + i ); + session.persist( entity ); + } + session.flush(); + final List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( JdbcBatchExecutionEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 2 ); + + RecordedEvent jdbcBatchExecutionEvent = events.get( 0 ); + assertThat( jdbcBatchExecutionEvent.getEventType().getName() ) + .isEqualTo( JdbcBatchExecutionEvent.NAME ); + assertThat( jdbcBatchExecutionEvent.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( jdbcBatchExecutionEvent.getString( "sql" ).toLowerCase( Locale.ROOT ) ) + .contains( "insert into " ); + + jdbcBatchExecutionEvent = events.get( 1 ); + assertThat( jdbcBatchExecutionEvent.getEventType().getName() ) + .isEqualTo( JdbcBatchExecutionEvent.NAME ); + assertThat( jdbcBatchExecutionEvent.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( jdbcBatchExecutionEvent.getString( "sql" ).toLowerCase( Locale.ROOT ) ) + .contains( "insert into " ); + } + ); + + } + + @Test + @EnableEvent(JdbcBatchExecutionEvent.NAME) + public void testJdbcBatchExecutionEventNoFired(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + } + ); + final List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( JdbcBatchExecutionEvent.NAME ); + } + ).toList(); + + assertThat( events ).hasSize( 0 ); + } + + @Entity(name = "TestEntity") + public static class TestEntity { + @Id + private Integer id; + + private String name; + + public TestEntity() { + } + + public TestEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + } + +} diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/JdbcConnectionEventTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/JdbcConnectionEventTests.java new file mode 100644 index 0000000000..b65362cc56 --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/JdbcConnectionEventTests.java @@ -0,0 +1,108 @@ +package org.hibernate.orm.test.event.jfr; + +import java.util.List; + +import org.hibernate.event.jfr.JdbcConnectionAcquisitionEvent; +import org.hibernate.event.jfr.JdbcConnectionReleaseEvent; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel +@SessionFactory +public class JdbcConnectionEventTests { + public JfrEvents jfrEvents = new JfrEvents(); + + @Test + @EnableEvent(JdbcConnectionAcquisitionEvent.NAME) + @EnableEvent(JdbcConnectionReleaseEvent.NAME) + public void testJdbcConnectionAcquisition(SessionFactoryScope scope) { + // starting a transaction should trigger the acquisition of the connection + String expectedSessionId = scope.fromTransaction( + session -> { + final List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( JdbcConnectionAcquisitionEvent.NAME ) + || eventName.equals( JdbcConnectionReleaseEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 1 ); + RecordedEvent event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( JdbcConnectionAcquisitionEvent.NAME ); + String sessionId = session.getSessionIdentifier().toString(); + assertThat( event.getString( "sessionIdentifier" ) ).isEqualTo( sessionId ); + jfrEvents.reset(); + + return sessionId; + } + ); + + final List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( JdbcConnectionAcquisitionEvent.NAME ) + || eventName.equals( JdbcConnectionReleaseEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 1 ); + RecordedEvent event = events.get( 0 ); + assertThat( event.getEventType().getName() ).isEqualTo( JdbcConnectionReleaseEvent.NAME ); + /* + Disabled the following check, not sure why but the retrieved `sessionIdentifier` is null, + checked with Flight Recorder and the value is correctly set to the session id value + */ + +// assertThat( event.getString( "sessionIdentifier" ) ).isEqualTo( expectedSessionId ); + } + + @Test + @EnableEvent(JdbcConnectionAcquisitionEvent.NAME) + @EnableEvent(JdbcConnectionReleaseEvent.NAME) + public void testJdbcConnectionAcquisitionNoFired(SessionFactoryScope scope) { + jfrEvents.reset(); + + // starting a session should not trigger the acquisition/release of the connection + scope.inSession( + session -> { + final List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( JdbcConnectionAcquisitionEvent.NAME ) + || eventName.equals( JdbcConnectionReleaseEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 0 ); + jfrEvents.reset(); + } + ); + final List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( JdbcConnectionAcquisitionEvent.NAME ) + || eventName.equals( JdbcConnectionReleaseEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 0 ); + } + +} diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/JdbcPreparedStatementEventTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/JdbcPreparedStatementEventTests.java new file mode 100644 index 0000000000..e9ac9347e8 --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/JdbcPreparedStatementEventTests.java @@ -0,0 +1,95 @@ +package org.hibernate.orm.test.event.jfr; + +import java.util.List; +import java.util.Locale; + +import org.hibernate.event.jfr.JdbcPreparedStatementCreationEvent; +import org.hibernate.event.jfr.JdbcPreparedStatementExecutionEvent; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel(annotatedClasses = JdbcPreparedStatementEventTests.TestEntity.class) +@SessionFactory +public class JdbcPreparedStatementEventTests { + public JfrEvents jfrEvents = new JfrEvents(); + + @Test + @EnableEvent(JdbcPreparedStatementCreationEvent.NAME) + @EnableEvent(JdbcPreparedStatementExecutionEvent.NAME) + public void testJdbcPreparedStatementEvent(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createQuery( "select t from TestEntity t" ).list(); + final List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( JdbcPreparedStatementCreationEvent.NAME ) + || eventName.equals( JdbcPreparedStatementExecutionEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 2 ); + + RecordedEvent preparedStatementCreationEvent = events.get( 0 ); + assertThat( preparedStatementCreationEvent.getEventType().getName() ) + .isEqualTo( JdbcPreparedStatementCreationEvent.NAME ); + assertThat( preparedStatementCreationEvent.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( preparedStatementCreationEvent.getString( "sql" ).toLowerCase( Locale.ROOT ) ) + .contains( "select " ); + + RecordedEvent preparedStatementExecutionEvent = events.get( 1 ); + assertThat( preparedStatementExecutionEvent.getEventType().getName() ) + .isEqualTo( JdbcPreparedStatementExecutionEvent.NAME ); + assertThat( preparedStatementExecutionEvent.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( preparedStatementExecutionEvent.getString( "sql" ) ) + .isEqualTo( preparedStatementCreationEvent.getString( "sql" ) ); + } + ); + + } + + @Test + @EnableEvent(JdbcPreparedStatementCreationEvent.NAME) + @EnableEvent(JdbcPreparedStatementExecutionEvent.NAME) + public void testJdbcPreparedStatementEventNoFired(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + + } + ); + final List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( JdbcPreparedStatementCreationEvent.NAME ) + || eventName.equals( JdbcPreparedStatementExecutionEvent.NAME ); + } + ).toList(); + + assertThat( events ).hasSize( 0 ); + } + + @Entity(name = "TestEntity") + public static class TestEntity { + @Id + private Long id; + + private String name; + } +} diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/CacheGetEventTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/CacheGetEventTests.java new file mode 100644 index 0000000000..a5f32ab47d --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/CacheGetEventTests.java @@ -0,0 +1,125 @@ +package org.hibernate.orm.test.event.jfr.cache; + +import java.util.List; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.event.jfr.CacheGetEvent; +import org.hibernate.event.jfr.JdbcBatchExecutionEvent; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Cacheable; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel(annotatedClasses = { + CacheGetEventTests.TestEntity.class, +}) +@SessionFactory +@ServiceRegistry( + settings = { + @Setting(name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true"), + } +) +public class CacheGetEventTests { + public JfrEvents jfrEvents = new JfrEvents(); + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + TestEntity entity = new TestEntity( 1, "name_1" ); + session.persist( entity ); + } + ); + } + + @Test + @EnableEvent(JdbcBatchExecutionEvent.NAME) + public void testCacheGetEvent(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + TestEntity testEntity = session.find( TestEntity.class, 1 ); + + List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( CacheGetEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 1 ); + + RecordedEvent event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( CacheGetEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getString( "entityName" ) ) + .isEqualTo( TestEntity.class.getName() ); + assertThat( event.getBoolean( "isNaturalId" ) ).isFalse(); + assertThat( event.getBoolean( "hit" ) ).isTrue(); + assertThat( event.getString( "regionName" ) ).isNotNull(); + } + ); + + } + + @Test + @EnableEvent(JdbcBatchExecutionEvent.NAME) + public void testCacheGetEventNoFired(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + + } + ); + List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( CacheGetEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 0 ); + } + + @Entity(name = "TestEntity") + @Cacheable + public static class TestEntity { + @Id + private Integer id; + + private String name; + + public TestEntity() { + } + + public TestEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + } + +} diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/EntityInsertCachePutEventTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/EntityInsertCachePutEventTests.java new file mode 100644 index 0000000000..5a287a1f0b --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/EntityInsertCachePutEventTests.java @@ -0,0 +1,168 @@ +package org.hibernate.orm.test.event.jfr.cache; + +import java.util.List; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.event.jfr.CachePutEvent; +import org.hibernate.event.jfr.JdbcBatchExecutionEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; +import org.hibernate.orm.test.cache.CacheRegionStatisticsTest; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Cacheable; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel(annotatedClasses = { + EntityInsertCachePutEventTests.TestEntity.class, + EntityInsertCachePutEventTests.AnotherTestEntity.class, +}) +@SessionFactory +@ServiceRegistry( + settings = + { + @Setting(name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true"), + } +) +public class EntityInsertCachePutEventTests { + public JfrEvents jfrEvents = new JfrEvents(); + + @Test + @EnableEvent(JdbcBatchExecutionEvent.NAME) + public void testCachePutEvent(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + TestEntity entity = new TestEntity( 1, "name_1" ); + session.persist( entity ); + + session.flush(); + List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( CachePutEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 1 ); + + RecordedEvent event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( CachePutEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getString( "entityName" ) ).isEqualTo( TestEntity.class.getName() ); + // cache strategy is READ_WRITE so no cache insert happened + assertThat( event.getBoolean( "cacheChanged" ) ).isFalse(); + assertThat( event.getString( "regionName" ) ).isNotNull(); + assertThat( event.getString( "description" ) ).isEqualTo( JfrEventManager.CacheActionDescription.ENTITY_INSERT.getText() ); + + jfrEvents.reset(); + AnotherTestEntity anotherTestEntity = new AnotherTestEntity( 1, "name_1" ); + session.persist( anotherTestEntity ); + + session.flush(); + events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( CachePutEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 1 ); + + event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( CachePutEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getString( "entityName" ) ).isEqualTo( AnotherTestEntity.class.getName() ); + // cache strategy is TRANSACTIONAL so cache insert should happen + assertThat( event.getBoolean( "cacheChanged" ) ).isTrue(); + assertThat( event.getString( "regionName" ) ).isNotNull(); + assertThat( event.getBoolean( "isNaturalId" ) ).isFalse(); + assertThat( event.getString( "description" ) ).isEqualTo( JfrEventManager.CacheActionDescription.ENTITY_INSERT.getText() ); + } + ); + + } + + @Test + @EnableEvent(JdbcBatchExecutionEvent.NAME) + public void testJdbcBatchExecutionEventNoFired(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + session.createQuery( + "from TestEntity", + CacheRegionStatisticsTest.Dog.class + ).setCacheable( false ).getResultList(); + } + ); + final List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( CachePutEvent.NAME ); + } + ).toList(); + + assertThat( events ).hasSize( 0 ); + } + + @Entity(name = "TestEntity") + @Cacheable + public static class TestEntity { + @Id + private Integer id; + + private String name; + + public TestEntity() { + } + + public TestEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + } + + @Entity(name = "AnotherTestEntity") + @Cacheable + @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) + public static class AnotherTestEntity { + @Id + private Integer id; + + private String name; + + public AnotherTestEntity() { + } + + public AnotherTestEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + } + +} diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/EntityUpdateCachePutEventTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/EntityUpdateCachePutEventTests.java new file mode 100644 index 0000000000..927a144f6c --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/EntityUpdateCachePutEventTests.java @@ -0,0 +1,167 @@ +package org.hibernate.orm.test.event.jfr.cache; + +import java.util.List; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.event.jfr.CachePutEvent; +import org.hibernate.event.jfr.JdbcBatchExecutionEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Cacheable; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel(annotatedClasses = { + EntityUpdateCachePutEventTests.TestEntity.class, + EntityUpdateCachePutEventTests.AnotherTestEntity.class, +}) +@SessionFactory +@ServiceRegistry( + settings = { + @Setting(name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true"), + } +) +public class EntityUpdateCachePutEventTests { + public JfrEvents jfrEvents = new JfrEvents(); + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + TestEntity entity = new TestEntity( 1, "name_1" ); + session.persist( entity ); + AnotherTestEntity anotherTestEntity = new AnotherTestEntity( 1, "name_1" ); + session.persist( anotherTestEntity ); + } + ); + } + + @Test + @EnableEvent(JdbcBatchExecutionEvent.NAME) + public void testCachePutEvent(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + + TestEntity testEntity = session.find( TestEntity.class, 1 ); + + testEntity.setName( "Another name" ); + session.flush(); + List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( CachePutEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 1 ); + + RecordedEvent event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( CachePutEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getString( "entityName" ) ).isEqualTo( TestEntity.class.getName() ); + // cache strategy is READ_WRITE so no cache insert happened + assertThat( event.getBoolean( "cacheChanged" ) ).isFalse(); + assertThat( event.getBoolean( "isNaturalId" ) ).isFalse(); + assertThat( event.getString( "regionName" ) ).isNotNull(); + assertThat( event.getString( "description" ) ).isEqualTo( JfrEventManager.CacheActionDescription.ENTITY_UPDATE.getText() ); + + jfrEvents.reset(); + + AnotherTestEntity anotherTestEntity = session.find( AnotherTestEntity.class, 1 ); + + anotherTestEntity.setName( "Another name" ); + session.flush(); + + events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( CachePutEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 1 ); + + event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( CachePutEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getString( "entityName" ) ).isEqualTo( AnotherTestEntity.class.getName() ); + // cache strategy is TRANSACTIONAL so cache insert should happen + assertThat( event.getBoolean( "cacheChanged" ) ).isTrue(); + assertThat( event.getString( "regionName" ) ).isNotNull(); + assertThat( event.getBoolean( "isNaturalId" ) ).isFalse(); + assertThat( event.getString( "description" ) ).isEqualTo( JfrEventManager.CacheActionDescription.ENTITY_UPDATE.getText() ); + } + ); + + } + + @Entity(name = "TestEntity") + @Cacheable + public static class TestEntity { + @Id + private Integer id; + + private String name; + + public TestEntity() { + } + + public TestEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + } + + @Entity(name = "AnotherTestEntity") + @Cacheable + @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) + public static class AnotherTestEntity { + @Id + private Integer id; + + private String name; + + public AnotherTestEntity() { + } + + public AnotherTestEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + } + +} diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/NaturalIdGetCacheTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/NaturalIdGetCacheTests.java new file mode 100644 index 0000000000..e0bc61c528 --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/NaturalIdGetCacheTests.java @@ -0,0 +1,128 @@ +package org.hibernate.orm.test.event.jfr.cache; + +import java.util.List; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.NaturalIdCache; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.event.jfr.CacheGetEvent; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Cacheable; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel(annotatedClasses = { + NaturalIdGetCacheTests.TestEntity.class +}) +@SessionFactory +@ServiceRegistry( + settings = { + @Setting(name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true"), + } +) +public class NaturalIdGetCacheTests { + + public JfrEvents jfrEvents = new JfrEvents(); + + @Test + @EnableEvent(CacheGetEvent.NAME) + public void testCacheGetEvent(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + TestEntity testEntity = new TestEntity( 2, 4, 5 ); + session.persist( testEntity ); + } + ); + + scope.inTransaction( + session -> { + jfrEvents.reset(); + TestEntity load = session.byNaturalId( TestEntity.class ) + .using( "code", 4 ) + .using( "item", 5 ) + .load(); + List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( CacheGetEvent.NAME ); + } + ).toList(); + + assertThat( events ).hasSize( 2 ); + + RecordedEvent event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( CacheGetEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getString( "entityName" ) ) + .isEqualTo( TestEntity.class.getName() ); + assertThat( event.getBoolean( "isNaturalId" ) ).isTrue(); + assertThat( event.getBoolean( "hit" ) ).isTrue(); + assertThat( event.getString( "regionName" ) ).isNotNull(); + + event = events.get( 1 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( CacheGetEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getString( "entityName" ) ) + .isEqualTo( TestEntity.class.getName() ); + assertThat( event.getBoolean( "isNaturalId" ) ).isFalse(); + assertThat( event.getBoolean( "hit" ) ).isTrue(); + assertThat( event.getString( "regionName" ) ).isNotNull(); + + + } + ); + + } + + + @Entity(name = "TestEntity") + @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) + @Cacheable + @NaturalIdCache + public static class TestEntity { + + @Id + private Integer id; + + @NaturalId + private Integer code; + + @NaturalId + private Integer item; + + private String description = "A description ..."; + + protected TestEntity() { + } + + public TestEntity(Integer id, Integer code, Integer item) { + this.id = id; + this.code = code; + this.item = item; + } + } +} diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/NaturalIdPutCacheTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/NaturalIdPutCacheTests.java new file mode 100644 index 0000000000..f5e10f3230 --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/NaturalIdPutCacheTests.java @@ -0,0 +1,115 @@ +package org.hibernate.orm.test.event.jfr.cache; + +import java.util.List; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.NaturalIdCache; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.event.jfr.CachePutEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Cacheable; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel(annotatedClasses = { + NaturalIdPutCacheTests.TestEntity.class +}) +@SessionFactory +@ServiceRegistry( + settings = { + @Setting(name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true"), + } +) +public class NaturalIdPutCacheTests { + + public JfrEvents jfrEvents = new JfrEvents(); + + @Test + @EnableEvent(CachePutEvent.NAME) + public void testCachePutEvent(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + TestEntity testEntity = new TestEntity( 1, 2, 3 ); + session.persist( testEntity ); + session.flush(); + List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( CachePutEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 2 ); + RecordedEvent event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( CachePutEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getString( "entityName" ) ).isEqualTo( TestEntity.class.getName() ); + assertThat( event.getBoolean( "cacheChanged" ) ).isTrue(); + assertThat( event.getBoolean( "isNaturalId" ) ).isFalse(); + assertThat( event.getString( "regionName" ) ).isNotNull(); + assertThat( event.getString( "description" ) ).isEqualTo( JfrEventManager.CacheActionDescription.ENTITY_INSERT.getText() ); + + event = events.get( 1 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( CachePutEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getString( "entityName" ) ).isEqualTo( TestEntity.class.getName() ); + // cache strategy is READ_WRITE so no cache insert happened + assertThat( event.getBoolean( "cacheChanged" ) ).isTrue(); + assertThat( event.getBoolean( "isNaturalId" ) ).isTrue(); + assertThat( event.getString( "regionName" ) ).isNotNull(); + assertThat( event.getString( "description" ) ).isEqualTo( JfrEventManager.CacheActionDescription.ENTITY_INSERT.getText() ); + } + ); + } + + @Entity(name = "TestEntity") + @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) + @Cacheable + @NaturalIdCache + public static class TestEntity { + + @Id + private Integer id; + + @NaturalId + private Integer code; + + @NaturalId + private Integer item; + + private String description = "A description ..."; + + protected TestEntity() { + } + + public TestEntity(Integer id, Integer code, Integer item) { + this.id = id; + this.code = code; + this.item = item; + } + } +} diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/QueryCachePutEventTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/QueryCachePutEventTests.java new file mode 100644 index 0000000000..4d52f948dd --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/cache/QueryCachePutEventTests.java @@ -0,0 +1,131 @@ +package org.hibernate.orm.test.event.jfr.cache; + +import java.util.List; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.event.jfr.CachePutEvent; +import org.hibernate.event.jfr.JdbcBatchExecutionEvent; +import org.hibernate.event.jfr.internal.JfrEventManager; +import org.hibernate.orm.test.cache.CacheRegionStatisticsTest; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel(annotatedClasses = { + QueryCachePutEventTests.TestEntity.class, +}) +@SessionFactory +@ServiceRegistry( + settings = + { + @Setting(name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true"), + @Setting(name = AvailableSettings.USE_QUERY_CACHE, value = "true") + } +) +public class QueryCachePutEventTests { + public JfrEvents jfrEvents = new JfrEvents(); + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + for ( int i = 2; i < 10; i++ ) { + TestEntity entity = new TestEntity( i, "name_" + i ); + session.persist( entity ); + } + } + ); + } + + @Test + @EnableEvent(JdbcBatchExecutionEvent.NAME) + public void testCachePutEvent(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + List dogs = session.createQuery( + "from TestEntity", + CacheRegionStatisticsTest.Dog.class + ).setCacheable( true ).getResultList(); + + final List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( CachePutEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 1 ); + + RecordedEvent event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( CachePutEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getBoolean( "cacheChanged" ) ).isTrue(); + assertThat( event.getString( "regionName" ) ).isNotNull(); + assertThat( event.getBoolean( "isNaturalId" ) ).isFalse(); + assertThat( event.getString( "description" ) ).isEqualTo( JfrEventManager.CacheActionDescription.QUERY_RESULT.getText() ); + } + ); + + } + + @Test + @EnableEvent(JdbcBatchExecutionEvent.NAME) + public void testJdbcBatchExecutionEventNoFired(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + session.createQuery( + "from TestEntity", + CacheRegionStatisticsTest.Dog.class + ).setCacheable( false ).getResultList(); + } + ); + final List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( CachePutEvent.NAME ); + } + ).toList(); + + assertThat( events ).hasSize( 0 ); + } + + @Entity(name = "TestEntity") + public static class TestEntity { + @Id + private Integer id; + + private String name; + + public TestEntity() { + } + + public TestEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + } + +} diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/flush/AutoFlushTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/flush/AutoFlushTests.java new file mode 100644 index 0000000000..53b1988af3 --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/flush/AutoFlushTests.java @@ -0,0 +1,103 @@ +package org.hibernate.orm.test.event.jfr.flush; + +import java.util.List; + +import org.hibernate.event.jfr.FlushEvent; +import org.hibernate.event.jfr.PartialFlushEvent; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel(annotatedClasses = { + AutoFlushTests.TestEntity.class, +}) +@SessionFactory +public class AutoFlushTests { + public JfrEvents jfrEvents = new JfrEvents(); + + @Test + @EnableEvent(FlushEvent.NAME) + @EnableEvent(PartialFlushEvent.NAME) + public void testFlushEvent(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + TestEntity entity = new TestEntity( 1, "name_1" ); + session.persist( entity ); + session.createQuery( "select t from TestEntity t" ).list(); + List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( FlushEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 1 ); + + RecordedEvent event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( FlushEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getInt( "numberOfEntitiesProcessed" ) ) + .isEqualTo( 1 ); + assertThat( event.getInt( "numberOfCollectionsProcessed" ) ).isEqualTo( 0 ); + assertThat( event.getBoolean( "isAutoFlush" ) ).isTrue(); + + events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( PartialFlushEvent.NAME ); + } + ).toList(); + event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( PartialFlushEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getInt( "numberOfEntitiesProcessed" ) ) + .isEqualTo( 1 ); + assertThat( event.getInt( "numberOfCollectionsProcessed" ) ).isEqualTo( 0 ); + assertThat( event.getBoolean( "isAutoFlush" ) ).isTrue(); + } + ); + } + + @Entity(name = "TestEntity") + public static class TestEntity { + @Id + private Integer id; + + private String name; + + public TestEntity() { + } + + public TestEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + } + +} diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/flush/ExplicitFlushTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/flush/ExplicitFlushTests.java new file mode 100644 index 0000000000..e12da23143 --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/flush/ExplicitFlushTests.java @@ -0,0 +1,82 @@ +package org.hibernate.orm.test.event.jfr.flush; + +import java.util.List; + +import org.hibernate.event.jfr.FlushEvent; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel(annotatedClasses = { + ExplicitFlushTests.TestEntity.class, +}) +@SessionFactory +public class ExplicitFlushTests { + public JfrEvents jfrEvents = new JfrEvents(); + + @Test + @EnableEvent(FlushEvent.NAME) + public void testFlushEvent(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + TestEntity entity = new TestEntity( 1, "name_1" ); + session.persist( entity ); + session.flush(); + List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( FlushEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 1 ); + + RecordedEvent event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( FlushEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( session.getSessionIdentifier().toString() ); + assertThat( event.getInt( "numberOfEntitiesProcessed" ) ) + .isEqualTo( 1 ); + assertThat( event.getInt( "numberOfCollectionsProcessed" ) ).isEqualTo( 0 ); + assertThat( event.getBoolean( "isAutoFlush" ) ).isFalse(); + } + ); + } + + @Entity(name = "TestEntity") + public static class TestEntity { + @Id + private Integer id; + + private String name; + + public TestEntity() { + } + + public TestEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + } + +} diff --git a/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/flush/FlushTests.java b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/flush/FlushTests.java new file mode 100644 index 0000000000..b43b49d34a --- /dev/null +++ b/hibernate-core/src/test/java17/org/hibernate/orm/test/event/jfr/flush/FlushTests.java @@ -0,0 +1,104 @@ +package org.hibernate.orm.test.event.jfr.flush; + +import java.util.List; + +import org.hibernate.event.jfr.FlushEvent; +import org.hibernate.event.jfr.JdbcBatchExecutionEvent; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jdk.jfr.consumer.RecordedEvent; +import org.moditect.jfrunit.EnableEvent; +import org.moditect.jfrunit.JfrEventTest; +import org.moditect.jfrunit.JfrEvents; + +import static org.assertj.core.api.Assertions.assertThat; + +@JfrEventTest +@DomainModel(annotatedClasses = { + FlushTests.TestEntity.class, +}) +@SessionFactory +public class FlushTests { + public JfrEvents jfrEvents = new JfrEvents(); + + @Test + @EnableEvent(FlushEvent.NAME) + public void testFlushEvent(SessionFactoryScope scope) { + jfrEvents.reset(); + String sessionId = scope.fromTransaction( + session -> { + TestEntity entity = new TestEntity( 1, "name_1" ); + session.persist( entity ); + return session.getSessionIdentifier().toString(); + } + ); + + List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( FlushEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 1 ); + + RecordedEvent event = events.get( 0 ); + assertThat( event.getEventType().getName() ) + .isEqualTo( FlushEvent.NAME ); + assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 ); + assertThat( event.getString( "sessionIdentifier" ) ) + .isEqualTo( sessionId ); + assertThat( event.getInt( "numberOfEntitiesProcessed" ) ) + .isEqualTo( 1 ); + assertThat( event.getInt( "numberOfCollectionsProcessed" ) ).isEqualTo( 0 ); + assertThat( event.getBoolean( "isAutoFlush" ) ).isFalse(); + } + + @Test + @EnableEvent(JdbcBatchExecutionEvent.NAME) + public void testFlushNoFired(SessionFactoryScope scope) { + jfrEvents.reset(); + scope.inTransaction( + session -> { + + } + ); + List events = jfrEvents.events() + .filter( + recordedEvent -> + { + String eventName = recordedEvent.getEventType().getName(); + return eventName.equals( FlushEvent.NAME ); + } + ).toList(); + assertThat( events ).hasSize( 0 ); + } + + @Entity(name = "TestEntity") + public static class TestEntity { + @Id + private Integer id; + + private String name; + + public TestEntity() { + } + + public TestEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + } + +}