.
+ */
+package org.hibernate.stat;
+
+import io.micrometer.core.instrument.*;
+import io.micrometer.core.instrument.binder.MeterBinder;
+import io.micrometer.core.lang.NonNullApi;
+import io.micrometer.core.lang.NonNullFields;
+
+import org.hibernate.SessionFactory;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
+import org.hibernate.event.service.spi.EventListenerRegistry;
+import org.hibernate.event.spi.EventType;
+import org.hibernate.event.spi.PostLoadEvent;
+import org.hibernate.event.spi.PostLoadEventListener;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link MeterBinder} implementation that provides Hibernate query metrics. It exposes the
+ * same statistics as would be exposed when calling {@link Statistics#getQueryStatistics(String)}.
+ * Note that only SELECT queries are recorded in {@link QueryStatistics}.
+ *
+ * Be aware of the potential for high cardinality of unique Hibernate queries executed by your
+ * application when considering using this {@link MeterBinder}.
+ */
+@NonNullApi
+@NonNullFields
+public class HibernateQueryMetrics implements MeterBinder {
+
+ private static final String SESSION_FACTORY_TAG_NAME = "entityManagerFactory";
+
+ private final Iterable tags;
+
+ private final SessionFactory sessionFactory;
+
+ /**
+ * Create {@code HibernateQueryMetrics} and bind to the specified meter registry.
+ *
+ * @param registry meter registry to use
+ * @param sessionFactory session factory to use
+ * @param sessionFactoryName session factory name as a tag value
+ * @param tags additional tags
+ */
+ public static void monitor(
+ MeterRegistry registry,
+ SessionFactory sessionFactory,
+ String sessionFactoryName,
+ String... tags) {
+ monitor( registry, sessionFactory, sessionFactoryName, Tags.of( tags ) );
+ }
+
+ /**
+ * Create {@code HibernateQueryMetrics} and bind to the specified meter registry.
+ *
+ * @param registry meter registry to use
+ * @param sessionFactory session factory to use
+ * @param sessionFactoryName session factory name as a tag value
+ * @param tags additional tags
+ */
+ public static void monitor(
+ MeterRegistry registry,
+ SessionFactory sessionFactory,
+ String sessionFactoryName,
+ Iterable tags) {
+ new HibernateQueryMetrics( sessionFactory, sessionFactoryName, tags ).bindTo( registry );
+ }
+
+ /**
+ * Create a {@code HibernateQueryMetrics}.
+ *
+ * @param sessionFactory session factory to use
+ * @param sessionFactoryName session factory name as a tag value
+ * @param tags additional tags
+ */
+ public HibernateQueryMetrics(SessionFactory sessionFactory, String sessionFactoryName, Iterable tags) {
+ this.tags = Tags.concat( tags, SESSION_FACTORY_TAG_NAME, sessionFactoryName );
+ this.sessionFactory = sessionFactory;
+ }
+
+ @Override
+ public void bindTo(MeterRegistry meterRegistry) {
+ if ( sessionFactory instanceof SessionFactoryImplementor ) {
+ EventListenerRegistry eventListenerRegistry = ( (SessionFactoryImplementor) sessionFactory ).getServiceRegistry()
+ .getService( EventListenerRegistry.class );
+ MetricsEventHandler metricsEventHandler = new MetricsEventHandler( meterRegistry );
+ eventListenerRegistry.appendListeners( EventType.POST_LOAD, metricsEventHandler );
+ }
+ }
+
+ class MetricsEventHandler implements PostLoadEventListener {
+
+ private final MeterRegistry meterRegistry;
+
+ MetricsEventHandler(MeterRegistry meterRegistry) {
+ this.meterRegistry = meterRegistry;
+ }
+
+ @Override
+ public void onPostLoad(PostLoadEvent event) {
+ registerQueryMetric( event.getSession().getFactory().getStatistics() );
+ }
+
+ void registerQueryMetric(Statistics statistics) {
+ for ( String query : statistics.getQueries() ) {
+ QueryStatistics queryStatistics = statistics.getQueryStatistics( query );
+
+ FunctionCounter.builder(
+ "hibernate.query.cache.requests",
+ queryStatistics,
+ QueryStatistics::getCacheHitCount
+ )
+ .tags( tags )
+ .tags( "result", "hit", "query", query )
+ .description( "Number of query cache hits" )
+ .register( meterRegistry );
+
+ FunctionCounter.builder(
+ "hibernate.query.cache.requests",
+ queryStatistics,
+ QueryStatistics::getCacheMissCount
+ )
+ .tags( tags )
+ .tags( "result", "miss", "query", query )
+ .description( "Number of query cache misses" )
+ .register( meterRegistry );
+
+ FunctionCounter.builder(
+ "hibernate.query.cache.puts",
+ queryStatistics,
+ QueryStatistics::getCachePutCount
+ )
+ .tags( tags )
+ .tags( "query", query )
+ .description( "Number of cache puts for a query" )
+ .register( meterRegistry );
+
+ FunctionTimer.builder(
+ "hibernate.query.execution.total",
+ queryStatistics,
+ QueryStatistics::getExecutionCount,
+ QueryStatistics::getExecutionTotalTime,
+ TimeUnit.MILLISECONDS
+ )
+ .tags( tags )
+ .tags( "query", query )
+ .description( "Query executions" )
+ .register( meterRegistry );
+
+ TimeGauge.builder(
+ "hibernate.query.execution.max",
+ queryStatistics,
+ TimeUnit.MILLISECONDS,
+ QueryStatistics::getExecutionMaxTime
+ )
+ .tags( tags )
+ .tags( "query", query )
+ .description( "Query maximum execution time" )
+ .register( meterRegistry );
+
+ TimeGauge.builder(
+ "hibernate.query.execution.min",
+ queryStatistics,
+ TimeUnit.MILLISECONDS,
+ QueryStatistics::getExecutionMinTime
+ )
+ .tags( tags )
+ .tags( "query", query )
+ .description( "Query minimum execution time" )
+ .register( meterRegistry );
+
+ FunctionCounter.builder(
+ "hibernate.query.execution.rows",
+ queryStatistics,
+ QueryStatistics::getExecutionRowCount
+ )
+ .tags( tags )
+ .tags( "query", query )
+ .description( "Number of rows processed for a query" )
+ .register( meterRegistry );
+ }
+ }
+ }
+}
diff --git a/hibernate-micrometer/src/test/java/org/hibernate/test/stat/Account.java b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/Account.java
new file mode 100644
index 0000000000..0cc4ea1eb3
--- /dev/null
+++ b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/Account.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.test.stat;
+
+import javax.persistence.Basic;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import org.hibernate.annotations.NaturalId;
+
+@Entity
+@Table( name = "t_acct" )
+public class Account {
+ @EmbeddedId
+ private AccountId accountId;
+
+ @Basic( optional = false )
+ @NaturalId
+ private String shortCode;
+
+ protected Account() {
+ }
+
+ public Account(AccountId accountId, String shortCode) {
+ this.accountId = accountId;
+ this.shortCode = shortCode;
+ }
+
+ public AccountId getAccountId() {
+ return accountId;
+ }
+
+ public String getShortCode() {
+ return shortCode;
+ }
+}
diff --git a/hibernate-micrometer/src/test/java/org/hibernate/test/stat/AccountId.java b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/AccountId.java
new file mode 100644
index 0000000000..1112721949
--- /dev/null
+++ b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/AccountId.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.test.stat;
+
+import javax.persistence.Embeddable;
+
+@Embeddable
+public class AccountId implements java.io.Serializable {
+ private final int id;
+
+ protected AccountId() {
+ this.id = 0;
+ }
+
+ public AccountId(int id) {
+ this.id = id;
+ }
+ public int intValue() {
+ return id;
+ }
+ @Override
+ public int hashCode() {
+ return id;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AccountId other = (AccountId) obj;
+ if (other != null && id != other.id)
+ return false;
+ return true;
+ }
+}
+
diff --git a/hibernate-micrometer/src/test/java/org/hibernate/test/stat/MicrometerCacheStatisticsTest.java b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/MicrometerCacheStatisticsTest.java
new file mode 100644
index 0000000000..0430e3db28
--- /dev/null
+++ b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/MicrometerCacheStatisticsTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.test.stat;
+
+import java.util.List;
+import java.util.Map;
+import javax.persistence.Cacheable;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.hibernate.Session;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.hibernate.annotations.NaturalId;
+import org.hibernate.annotations.NaturalIdCache;
+import org.hibernate.boot.MetadataSources;
+import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
+import org.hibernate.cfg.AvailableSettings;
+import org.hibernate.stat.HibernateMetrics;
+
+import org.hibernate.testing.cache.CachingRegionFactory;
+import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import io.micrometer.core.instrument.Tags;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+
+/**
+ * @author Erin Schnabel
+ * @author Steve Ebersole
+ */
+public class MicrometerCacheStatisticsTest extends BaseNonConfigCoreFunctionalTestCase {
+
+ @Override
+ protected void applyMetadataSources(MetadataSources metadataSources) {
+ super.applyMetadataSources( metadataSources );
+ metadataSources.addAnnotatedClass( Person.class );
+ metadataSources.addAnnotatedClass( Account.class );
+ metadataSources.addAnnotatedClass( AccountId.class );
+ }
+
+ private static final String REGION = "TheRegion";
+ private static final String PREFIX = "test";
+
+ private SimpleMeterRegistry registry = new SimpleMeterRegistry();
+ private HibernateMetrics hibernateMetrics;
+
+ @Override
+ protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
+ super.configureStandardServiceRegistryBuilder( ssrb );
+ ssrb.applySetting( AvailableSettings.USE_SECOND_LEVEL_CACHE, true );
+ ssrb.applySetting( AvailableSettings.USE_QUERY_CACHE, true );
+ ssrb.applySetting( AvailableSettings.CACHE_REGION_FACTORY, new CachingRegionFactory() );
+ ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
+ }
+
+ @Override
+ protected void addSettings(Map settings) {
+ super.addSettings( settings );
+ settings.put( AvailableSettings.USE_SECOND_LEVEL_CACHE, "true" );
+ settings.put( AvailableSettings.USE_QUERY_CACHE, "true" );
+ settings.put( AvailableSettings.CACHE_REGION_FACTORY, new CachingRegionFactory() );
+
+ settings.put( AvailableSettings.GENERATE_STATISTICS, "true" );
+ settings.put( AvailableSettings.HBM2DDL_AUTO, "create-drop" );
+ settings.put( AvailableSettings.SESSION_FACTORY_NAME, "something" );
+ settings.put( AvailableSettings.SESSION_FACTORY_NAME_IS_JNDI, "false" );
+ }
+
+ @Before
+ public void setUpMetrics() {
+ hibernateMetrics = new HibernateMetrics(sessionFactory(),
+ sessionFactory().getName(),
+ Tags.empty() );
+ hibernateMetrics.bindTo( registry );
+ }
+
+ @After
+ public void cleanUpMetrics() {
+ registry.clear();
+ }
+
+ @Test
+ public void testMicrometerMetrics() {
+ Assert.assertNotNull(registry.get("hibernate.sessions.open").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.sessions.closed").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.transactions").tags("result", "success").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.transactions").tags("result", "failure").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.optimistic.failures").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.flushes").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.connections.obtained").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.statements").tags("status", "prepared").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.statements").tags("status", "closed").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.second.level.cache.requests").tags("result", "hit", "region", REGION));
+ Assert.assertNotNull(registry.get("hibernate.second.level.cache.requests").tags("result", "miss", "region", REGION));
+ Assert.assertNotNull(registry.get("hibernate.second.level.cache.puts").tags("region", REGION).functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.entities.deletes").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.entities.fetches").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.entities.inserts").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.entities.loads").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.entities.updates").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.collections.deletes").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.collections.fetches").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.collections.loads").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.collections.recreates").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.collections.updates").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.cache.natural.id.requests").tags("result", "hit").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.natural.id.requests").tags("result", "miss").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.natural.id.puts").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.query.natural.id.executions").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.query.natural.id.executions.max").timeGauge());
+
+ Assert.assertNotNull(registry.get("hibernate.query.executions").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.query.executions.max").timeGauge());
+
+ Assert.assertNotNull(registry.get("hibernate.cache.update.timestamps.requests").tags("result", "hit").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.update.timestamps.requests").tags("result", "miss").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.update.timestamps.puts").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.cache.query.requests").tags("result", "hit").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.query.requests").tags("result", "miss").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.query.puts").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.query.plan").tags("result", "hit").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.query.plan").tags("result", "miss").functionCounter());
+
+ // prepare some test data...
+ Session session = openSession();
+ session.beginTransaction();
+ Person person = new Person( 1, "testAcct");
+ session.save( person );
+ session.getTransaction().commit();
+ session.close();
+
+ Assert.assertEquals( 1, registry.get("hibernate.sessions.open").functionCounter().count(), 0 );
+ Assert.assertEquals( 1, registry.get("hibernate.sessions.closed").functionCounter().count(), 0 );
+ Assert.assertEquals( 1, registry.get("hibernate.entities.inserts").functionCounter().count(), 0 );
+ Assert.assertEquals( 1, registry.get("hibernate.transactions").tags("result", "success").functionCounter().count(), 0 );
+ Assert.assertEquals( 1, registry.get("hibernate.cache.natural.id.puts").functionCounter().count(), 0);
+ Assert.assertEquals(2, registry.get("hibernate.second.level.cache.puts").tags("region", REGION).functionCounter().count(), 0);
+
+ final String queryString = "select p from Person p";
+ inTransaction(
+ // Only way to generate query region (to be accessible via stats) is to execute the query
+ s -> s.createQuery( queryString ).setCacheable( true ).setCacheRegion( REGION ).list()
+ );
+
+ Assert.assertEquals( 2, registry.get("hibernate.sessions.open").functionCounter().count(), 0 );
+ Assert.assertEquals( 2, registry.get("hibernate.sessions.closed").functionCounter().count(), 0 );
+ Assert.assertEquals( 0, registry.get("hibernate.entities.deletes").functionCounter().count(), 0 );
+ Assert.assertEquals( 2, registry.get("hibernate.transactions").tags("result", "success").functionCounter().count(), 0 );
+ Assert.assertEquals( 1, registry.get("hibernate.cache.natural.id.puts").functionCounter().count(), 0);
+ Assert.assertEquals(3, registry.get("hibernate.second.level.cache.puts").tags("region", REGION).functionCounter().count(), 0);
+
+ // clean up
+ session = openSession();
+ session.beginTransaction();
+ session.delete( person );
+ session.getTransaction().commit();
+ session.close();
+
+ Assert.assertEquals( 3, registry.get("hibernate.sessions.open").functionCounter().count(), 0 );
+ Assert.assertEquals( 3, registry.get("hibernate.sessions.closed").functionCounter().count(), 0 );
+ Assert.assertEquals( 1, registry.get("hibernate.entities.deletes").functionCounter().count(), 0 );
+ Assert.assertEquals( 3, registry.get("hibernate.transactions").tags("result", "success").functionCounter().count(), 0 );
+ }
+
+ @Entity( name = "Person" )
+ @Table( name = "persons" )
+ @Cacheable
+ @Cache( region = REGION, usage = CacheConcurrencyStrategy.READ_WRITE )
+ @NaturalIdCache( region = REGION )
+ public static class Person {
+ @Id
+ public Integer id;
+
+ @NaturalId
+ public String name;
+
+ protected Person() {
+ }
+
+ public Person(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ @ElementCollection
+ @Cache( region = REGION, usage = CacheConcurrencyStrategy.READ_WRITE )
+ public List nickNames;
+ }
+}
diff --git a/hibernate-micrometer/src/test/java/org/hibernate/test/stat/MicrometerStatisticsTest.java b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/MicrometerStatisticsTest.java
new file mode 100644
index 0000000000..43965841e1
--- /dev/null
+++ b/hibernate-micrometer/src/test/java/org/hibernate/test/stat/MicrometerStatisticsTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.test.stat;
+
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.stat.HibernateMetrics;
+
+import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import io.micrometer.core.instrument.Tags;
+import io.micrometer.core.instrument.search.MeterNotFoundException;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Erin Schnabel
+ * @author Donnchadh O Donnabhain
+ */
+public class MicrometerStatisticsTest extends BaseCoreFunctionalTestCase {
+
+ @Override
+ protected Class>[] getAnnotatedClasses() {
+ return new Class>[] { Account.class, AccountId.class };
+ }
+
+ private SimpleMeterRegistry registry = new SimpleMeterRegistry();
+ private HibernateMetrics hibernateMetrics;
+
+ @Override
+ protected void configure(Configuration configuration) {
+ super.configure( configuration );
+
+ configuration.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "false" );
+ configuration.setProperty( Environment.USE_QUERY_CACHE, "false" );
+ configuration.setProperty( Environment.GENERATE_STATISTICS, "true" );
+ configuration.setProperty( Environment.SESSION_FACTORY_NAME, "something" );
+ configuration.setProperty( Environment.SESSION_FACTORY_NAME_IS_JNDI, "false" );
+ }
+
+ @Before
+ public void setUpMetrics() {
+ hibernateMetrics = new HibernateMetrics(sessionFactory(),
+ sessionFactory().getName(),
+ Tags.empty() );
+ hibernateMetrics.bindTo( registry );
+ }
+
+ @After
+ public void cleanUpMetrics() {
+ registry.clear();
+ }
+
+ @Test
+ public void testMicrometerMetrics() {
+ Assert.assertNotNull(registry.get("hibernate.sessions.open").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.sessions.closed").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.transactions").tags("result", "success").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.transactions").tags("result", "failure").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.optimistic.failures").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.flushes").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.connections.obtained").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.statements").tags("status", "prepared").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.statements").tags("status", "closed").functionCounter());
+
+ // Second level cache disabled
+ verifyMeterNotFoundException("hibernate.second.level.cache.requests");
+ verifyMeterNotFoundException("hibernate.second.level.cache.puts");
+
+ Assert.assertNotNull(registry.get("hibernate.entities.deletes").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.entities.fetches").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.entities.inserts").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.entities.loads").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.entities.updates").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.collections.deletes").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.collections.fetches").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.collections.loads").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.collections.recreates").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.collections.updates").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.cache.natural.id.requests").tags("result", "hit").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.natural.id.requests").tags("result", "miss").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.natural.id.puts").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.query.natural.id.executions").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.query.natural.id.executions.max").timeGauge());
+
+ Assert.assertNotNull(registry.get("hibernate.query.executions").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.query.executions.max").timeGauge());
+
+ Assert.assertNotNull(registry.get("hibernate.cache.update.timestamps.requests").tags("result", "hit").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.update.timestamps.requests").tags("result", "miss").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.update.timestamps.puts").functionCounter());
+
+ Assert.assertNotNull(registry.get("hibernate.cache.query.requests").tags("result", "hit").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.query.requests").tags("result", "miss").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.query.puts").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.query.plan").tags("result", "hit").functionCounter());
+ Assert.assertNotNull(registry.get("hibernate.cache.query.plan").tags("result", "miss").functionCounter());
+
+ // prepare some test data...
+ Session session = openSession();
+ session.beginTransaction();
+ Account account = new Account( new AccountId( 1), "testAcct");
+ session.save( account );
+ session.getTransaction().commit();
+ session.close();
+
+ Assert.assertEquals( 1, registry.get("hibernate.sessions.open").functionCounter().count(), 0 );
+ Assert.assertEquals( 1, registry.get("hibernate.sessions.closed").functionCounter().count(), 0 );
+ Assert.assertEquals( 1, registry.get("hibernate.entities.inserts").functionCounter().count(), 0 );
+ Assert.assertEquals( 1, registry.get("hibernate.transactions").tags("result", "success").functionCounter().count(), 0 );
+
+ // clean up
+ session = openSession();
+ session.beginTransaction();
+ session.delete( account );
+ session.getTransaction().commit();
+ session.close();
+
+ Assert.assertEquals( 2, registry.get("hibernate.sessions.open").functionCounter().count(), 0 );
+ Assert.assertEquals( 2, registry.get("hibernate.sessions.closed").functionCounter().count(), 0 );
+ Assert.assertEquals( 1, registry.get("hibernate.entities.deletes").functionCounter().count(), 0 );
+ Assert.assertEquals( 2, registry.get("hibernate.transactions").tags("result", "success").functionCounter().count(), 0 );
+ }
+
+ void verifyMeterNotFoundException(String name) {
+ try {
+ registry.get(name).meter();
+ Assert.fail(name + " should not have been found");
+ } catch(MeterNotFoundException mnfe) {
+ }
+ }
+}
diff --git a/hibernate-micrometer/src/test/resources/hibernate.properties b/hibernate-micrometer/src/test/resources/hibernate.properties
new file mode 100644
index 0000000000..de12583ef4
--- /dev/null
+++ b/hibernate-micrometer/src/test/resources/hibernate.properties
@@ -0,0 +1,27 @@
+#
+# 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 .
+#
+
+hibernate.dialect @db.dialect@
+hibernate.connection.driver_class @jdbc.driver@
+hibernate.connection.url @jdbc.url@
+hibernate.connection.username @jdbc.user@
+hibernate.connection.password @jdbc.pass@
+
+hibernate.connection.pool_size 5
+
+hibernate.show_sql false
+hibernate.format_sql true
+
+hibernate.max_fetch_depth 5
+
+hibernate.cache.region_prefix hibernate.test
+hibernate.cache.region.factory_class org.hibernate.testing.cache.CachingRegionFactory
+
+javax.persistence.validation.mode=NONE
+hibernate.service.allow_crawling=false
+hibernate.session.events.log=true
+hibernate.hql.bulk_id_strategy.global_temporary.drop_tables=true
\ No newline at end of file
diff --git a/hibernate-micrometer/src/test/resources/log4j.properties b/hibernate-micrometer/src/test/resources/log4j.properties
new file mode 100644
index 0000000000..4567cb1105
--- /dev/null
+++ b/hibernate-micrometer/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#
+# 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 .
+#
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
+
+log4j.rootLogger=info, stdout
+
+log4j.logger.org.hibernate.stat=trace
+
+log4j.logger.org.hibernate.tool.hbm2ddl=trace
+log4j.logger.org.hibernate.SQL=debug
+log4j.logger.org.hibernate.type.descriptor.sql.BasicBinder=trace
+log4j.logger.org.hibernate.type.descriptor.sql.BasicExtractor=trace
\ No newline at end of file
diff --git a/hibernate-proxool/hibernate-proxool.gradle b/hibernate-proxool/hibernate-proxool.gradle
index 8aa0fb69c6..56ace9a389 100644
--- a/hibernate-proxool/hibernate-proxool.gradle
+++ b/hibernate-proxool/hibernate-proxool.gradle
@@ -15,3 +15,15 @@ dependencies {
compile( libraries.proxool )
testCompile project( ':hibernate-testing' )
}
+
+test {
+ if ( gradle.ext.javaVersions.test.launcher.asInt() >= 9 ) {
+ // Proxool needs this to define classes for some reason. Stack trace:
+ // at org.logicalcobwebs.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:372)
+ // at org.logicalcobwebs.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:193)
+ // at org.logicalcobwebs.cglib.core.KeyFactory$Generator.create(KeyFactory.java:177)
+ // at org.logicalcobwebs.cglib.core.KeyFactory.create(KeyFactory.java:149)
+ // at org.logicalcobwebs.cglib.proxy.Enhancer.(Enhancer.java:96)
+ jvmArgs( ['--add-opens', 'java.base/java.lang=ALL-UNNAMED'] )
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index b2a10652a5..e776fba9dd 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -106,7 +106,7 @@ include 'hibernate-agroal'
include 'hibernate-jcache'
include 'hibernate-ehcache'
include 'hibernate-infinispan'
-
+include 'hibernate-micrometer'
include 'hibernate-graalvm'
// The plugin used to generate Java modules was compiled using JDK11.
diff --git a/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle b/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle
index 338c5891ea..e01636c6f0 100644
--- a/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle
+++ b/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle
@@ -134,3 +134,9 @@ else {
" Using the JDK that runs Gradle for Groovy compilation." )
}
+tasks.test {
+ if ( gradle.ext.javaVersions.test.launcher.asInt() >= 9 ) {
+ // Needs add-opens because Gradle uses illegal accesses to inject... mocks? Something like that.
+ jvmArgs( ['--add-opens', 'java.base/java.lang=ALL-UNNAMED'] )
+ }
+}