From 8df8fd37be7841d893e6c58fee285986c3a732ef Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 21 Apr 2024 15:51:01 +0200 Subject: [PATCH] HHH-17984 StatelessSession statistics for collections Signed-off-by: Gavin King --- .../internal/StatelessSessionImpl.java | 57 ++++++++++++++----- .../StatelessSessionStatisticsTest.java | 39 ++++++++++++- 2 files changed, 78 insertions(+), 18 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java index cef7e0b6f26e..ff1c6a64094a 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java @@ -151,7 +151,13 @@ public Object insert(String entityName, Object entity) { } persister.setIdentifier( entity, id, this ); forEachOwnedCollection( entity, id, persister, - (descriptor, collection) -> descriptor.recreate( collection, id, this) ); + (descriptor, collection) -> { + descriptor.recreate( collection, id, this); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.recreateCollection( descriptor.getRole() ); + } + } ); firePostInsert(entity, id, state, persister); final StatisticsImplementor statistics = getFactory().getStatistics(); if ( statistics.isStatisticsEnabled() ) { @@ -178,7 +184,13 @@ public void delete(String entityName, Object entity) { getInterceptor() .onDelete( entity, id, persister.getPropertyNames(), persister.getPropertyTypes() ); forEachOwnedCollection( entity, id, persister, - (descriptor, collection) -> descriptor.remove(id, this) ); + (descriptor, collection) -> { + descriptor.remove( id, this ); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.removeCollection( descriptor.getRole() ); + } + } ); persister.getDeleteCoordinator().delete( entity, id, version, this ); firePostDelete(entity, id, persister); final StatisticsImplementor statistics = getFactory().getStatistics(); @@ -223,11 +235,16 @@ public void update(String entityName, Object entity) { getInterceptor() .onUpdate( entity, id, state, persister.getPropertyNames(), persister.getPropertyTypes() ); persister.getUpdateCoordinator().update( entity, id, null, state, oldVersion, null, null, false, this ); - // TODO: can we do better here? - forEachOwnedCollection( entity, id, persister, - (descriptor, collection) -> descriptor.remove(id, this) ); forEachOwnedCollection( entity, id, persister, - (descriptor, collection) -> descriptor.recreate( collection, id, this) ); + (descriptor, collection) -> { + // TODO: can we do better here? + descriptor.remove( id, this ); + descriptor.recreate( collection, id, this ); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.updateCollection( descriptor.getRole() ); + } + } ); firePostUpdate(entity, id, state, persister); final StatisticsImplementor statistics = getFactory().getStatistics(); if ( statistics.isStatisticsEnabled() ) { @@ -247,11 +264,17 @@ public void upsert(String entityName, Object entity) { .onUpsert( entity, id, state, persister.getPropertyNames(), persister.getPropertyTypes() ); final Object oldVersion = versionToUpsert( entity, persister, state ); persister.getMergeCoordinator().update( entity, id, null, state, oldVersion, null, null, false, this ); - // TODO: can we do better here? - forEachOwnedCollection( entity, id, persister, - (descriptor, collection) -> descriptor.remove(id, this) ); + // TODO: statistics for upsert! forEachOwnedCollection( entity, id, persister, - (descriptor, collection) -> descriptor.recreate( collection, id, this) ); + (descriptor, collection) -> { + // TODO: can we do better here? + descriptor.remove( id, this ); + descriptor.recreate( collection, id, this ); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.updateCollection( descriptor.getRole() ); + } + } ); firePostUpsert(entity, id, state, persister); } } @@ -562,11 +585,10 @@ public void initializeCollection(PersistentCollection collection, boolean wri if ( LOG.isTraceEnabled() ) { LOG.trace( "Collection initialized" ); } - //TODO: statistics! -// final StatisticsImplementor statistics = getFactory().getStatistics(); -// if ( statistics.isStatisticsEnabled() ) { -// statistics.fetchCollection( loadedPersister.getRole() ); -// } + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.fetchCollection( loadedPersister.getRole() ); + } } } @@ -710,6 +732,7 @@ else if ( isPersistentAttributeInterceptable( association ) ) { proxyInterceptor.setSession( this ); try { proxyInterceptor.forceInitialize( association, null ); + // TODO: statistics?? call statistics.fetchEntity() } finally { proxyInterceptor.unsetSession(); @@ -731,6 +754,10 @@ else if ( association instanceof PersistentCollection ) { collectionDescriptor.initialize( key, this ); handlePotentiallyEmptyCollection( persistentCollection, getPersistenceContextInternal(), key, collectionDescriptor ); + final StatisticsImplementor statistics = getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.fetchCollection( collectionDescriptor.getRole() ); + } } finally { persistentCollection.unsetSession( this ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionStatisticsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionStatisticsTest.java index 7b5fc831ad0e..5ed772d9ecc5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionStatisticsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/StatelessSessionStatisticsTest.java @@ -1,7 +1,9 @@ package org.hibernate.orm.test.stateless; import jakarta.persistence.Basic; +import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import org.hibernate.stat.spi.StatisticsImplementor; @@ -12,7 +14,12 @@ import org.hibernate.testing.orm.junit.Setting; import org.junit.jupiter.api.Test; +import java.util.ArrayList; +import java.util.List; + import static org.hibernate.cfg.StatisticsSettings.GENERATE_STATISTICS; +import static org.hibernate.graph.GraphSemantic.FETCH; +import static org.hibernate.graph.GraphSemantic.LOAD; import static org.junit.jupiter.api.Assertions.assertEquals; @SessionFactory @@ -28,25 +35,51 @@ void test(SessionFactoryScope scope) { assertEquals(0, statistics.getEntityLoadCount()); Person person = new Person(); person.name = "Gavin"; + person.handles.add("@1ovthafew"); scope.inStatelessTransaction(s -> s.insert(person)); assertEquals(1, statistics.getEntityInsertCount()); + assertEquals(1, statistics.getCollectionRecreateCount()); scope.inStatelessSession(s -> s.get(Person.class, person.id)); assertEquals(1, statistics.getEntityLoadCount()); + assertEquals(0, statistics.getEntityFetchCount()); + assertEquals(1, statistics.getCollectionLoadCount()); + assertEquals(0, statistics.getCollectionFetchCount()); person.name = "Gavin King"; scope.inStatelessTransaction(s -> s.update(person)); assertEquals(1, statistics.getEntityUpdateCount()); - scope.inStatelessSession(s -> s.get(Person.class, person.id)); + assertEquals(1, statistics.getCollectionUpdateCount()); + scope.inStatelessSession(s -> s.get(s.createEntityGraph(Person.class), LOAD, person.id)); assertEquals(2, statistics.getEntityLoadCount()); + assertEquals(2, statistics.getCollectionLoadCount()); + assertEquals(0, statistics.getCollectionFetchCount()); + scope.inStatelessSession(s -> s.get(s.createEntityGraph(Person.class), FETCH, person.id)); + assertEquals(3, statistics.getEntityLoadCount()); + assertEquals(2, statistics.getCollectionLoadCount()); + assertEquals(0, statistics.getCollectionFetchCount()); + scope.inStatelessSession(s -> s.fetch(s.get(s.createEntityGraph(Person.class), FETCH, person.id).handles)); + assertEquals(4, statistics.getEntityLoadCount()); + assertEquals(3, statistics.getCollectionLoadCount()); + assertEquals(1, statistics.getCollectionFetchCount()); + scope.inStatelessSession(s -> s.createQuery("from Person", Person.class).getSingleResult()); + assertEquals(5, statistics.getEntityLoadCount()); + assertEquals(4, statistics.getCollectionLoadCount()); + assertEquals(2, statistics.getCollectionFetchCount()); + person.handles.add("hello world"); + scope.inStatelessTransaction(s -> s.upsert(person)); + assertEquals(2, statistics.getCollectionUpdateCount()); scope.inStatelessTransaction(s -> s.delete(person)); assertEquals(1, statistics.getEntityDeleteCount()); - assertEquals(3, statistics.getTransactionCount()); + assertEquals(1, statistics.getCollectionRemoveCount()); + assertEquals(4, statistics.getTransactionCount()); } - @Entity(name="Entity") + @Entity(name="Person") static class Person { @Id @GeneratedValue long id; @Basic(optional = false) String name; + @ElementCollection(fetch = FetchType.EAGER) + List handles = new ArrayList<>(); } }