diff --git a/hypersistence-utils-hibernate-60/src/main/java/io/hypersistence/utils/hibernate/query/JfrQueryLogger.java b/hypersistence-utils-hibernate-60/src/main/java/io/hypersistence/utils/hibernate/query/JfrQueryLogger.java new file mode 100644 index 000000000..354e3e2fe --- /dev/null +++ b/hypersistence-utils-hibernate-60/src/main/java/io/hypersistence/utils/hibernate/query/JfrQueryLogger.java @@ -0,0 +1,44 @@ +package io.hypersistence.utils.hibernate.query; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import org.hibernate.resource.jdbc.spi.StatementInspector; + +/** + * The {@link JfrQueryLogger} allows you to log a given SQL, including its stack trace, + * using JDK Flight Recorder. + *
+ * This only works with HotSpot based JVMs and requires at least Java 11.
+ *
+ * @author Philippe Marschall
+ * @since 3.6.0
+ */
+public final class JfrQueryLogger implements StatementInspector {
+
+ @Override
+ public String inspect(String sql) {
+ QueryEvent event = new QueryEvent();
+ event.setSql(sql);
+ event.commit();
+ return null;
+ }
+
+ @Category("Hibernate")
+ @Label("Query")
+ static class QueryEvent extends Event {
+
+ @Label("SQL")
+ @Description("The SQL query generated by Hibernate")
+ private String sql;
+
+ String getSql() {
+ return sql;
+ }
+
+ void setSql(String sql) {
+ this.sql = sql;
+ }
+ }
+}
diff --git a/hypersistence-utils-hibernate-60/src/test/java/io/hypersistence/utils/hibernate/query/JfrQueryLoggerTest.java b/hypersistence-utils-hibernate-60/src/test/java/io/hypersistence/utils/hibernate/query/JfrQueryLoggerTest.java
new file mode 100644
index 000000000..e1c8e31a6
--- /dev/null
+++ b/hypersistence-utils-hibernate-60/src/test/java/io/hypersistence/utils/hibernate/query/JfrQueryLoggerTest.java
@@ -0,0 +1,201 @@
+package io.hypersistence.utils.hibernate.query;
+
+import io.hypersistence.utils.hibernate.util.AbstractPostgreSQLIntegrationTest;
+import jakarta.persistence.*;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.Join;
+import jakarta.persistence.criteria.Root;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordingFile;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.lang.invoke.MethodHandles;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.LocalDate;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Philippe Marschall
+ */
+public class JfrQueryLoggerTest extends AbstractPostgreSQLIntegrationTest {
+
+ private static final Path RECORDING_LOCATION = Path.of(
+ "target",
+ MethodHandles.lookup().lookupClass().getSimpleName() + ".jfr"
+ );
+
+ private Recording recording;
+
+ @Override
+ protected Class>[] entities() {
+ return new Class>[]{
+ Post.class,
+ PostComment.class
+ };
+ }
+
+ @Override
+ protected void afterInit() {
+ try {
+ Files.deleteIfExists(RECORDING_LOCATION);
+ recording = new Recording();
+ recording.enable(JfrQueryLogger.QueryEvent.class);
+ recording.setMaxSize(128L * 1024L);
+ recording.setToDisk(true);
+ recording.setDestination(RECORDING_LOCATION);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ recording.start();
+ }
+
+ private void assertQueryLogged(int expectedCount) {
+ recording.close();
+ Set
+ * This only works with HotSpot based JVMs and requires at least Java 11.
+ *
+ * @author Philippe Marschall
+ * @since 3.6.0
+ */
+public final class JfrQueryLogger implements StatementInspector {
+
+ @Override
+ public String inspect(String sql) {
+ QueryEvent event = new QueryEvent();
+ event.setSql(sql);
+ event.commit();
+ return null;
+ }
+
+ @Category("Hibernate")
+ @Label("Query")
+ static class QueryEvent extends Event {
+
+ @Label("SQL")
+ @Description("The SQL query generated by Hibernate")
+ private String sql;
+
+ String getSql() {
+ return sql;
+ }
+
+ void setSql(String sql) {
+ this.sql = sql;
+ }
+ }
+}
diff --git a/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/query/JfrQueryLoggerTest.java b/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/query/JfrQueryLoggerTest.java
new file mode 100644
index 000000000..e1c8e31a6
--- /dev/null
+++ b/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/query/JfrQueryLoggerTest.java
@@ -0,0 +1,201 @@
+package io.hypersistence.utils.hibernate.query;
+
+import io.hypersistence.utils.hibernate.util.AbstractPostgreSQLIntegrationTest;
+import jakarta.persistence.*;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import jakarta.persistence.criteria.CriteriaQuery;
+import jakarta.persistence.criteria.Join;
+import jakarta.persistence.criteria.Root;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordingFile;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.lang.invoke.MethodHandles;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.LocalDate;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Philippe Marschall
+ */
+public class JfrQueryLoggerTest extends AbstractPostgreSQLIntegrationTest {
+
+ private static final Path RECORDING_LOCATION = Path.of(
+ "target",
+ MethodHandles.lookup().lookupClass().getSimpleName() + ".jfr"
+ );
+
+ private Recording recording;
+
+ @Override
+ protected Class>[] entities() {
+ return new Class>[]{
+ Post.class,
+ PostComment.class
+ };
+ }
+
+ @Override
+ protected void afterInit() {
+ try {
+ Files.deleteIfExists(RECORDING_LOCATION);
+ recording = new Recording();
+ recording.enable(JfrQueryLogger.QueryEvent.class);
+ recording.setMaxSize(128L * 1024L);
+ recording.setToDisk(true);
+ recording.setDestination(RECORDING_LOCATION);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ recording.start();
+ }
+
+ private void assertQueryLogged(int expectedCount) {
+ recording.close();
+ Set