Add instrumentation for hibernate 6 (#7773)
This commit is contained in:
parent
f074b93110
commit
d847bfcad5
|
@ -62,9 +62,9 @@ public class QueryInstrumentation implements TypeInstrumentation {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualField<Query, SessionInfo> criteriaVirtualField =
|
VirtualField<Query, SessionInfo> queryVirtualField =
|
||||||
VirtualField.find(Query.class, SessionInfo.class);
|
VirtualField.find(Query.class, SessionInfo.class);
|
||||||
SessionInfo sessionInfo = criteriaVirtualField.get(query);
|
SessionInfo sessionInfo = queryVirtualField.get(query);
|
||||||
|
|
||||||
Context parentContext = Java8BytecodeBridge.currentContext();
|
Context parentContext = Java8BytecodeBridge.currentContext();
|
||||||
hibernateOperation =
|
hibernateOperation =
|
||||||
|
|
|
@ -10,6 +10,7 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.
|
||||||
import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getEntityName;
|
import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getEntityName;
|
||||||
import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getSessionMethodOperationName;
|
import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getSessionMethodOperationName;
|
||||||
import static io.opentelemetry.javaagent.instrumentation.hibernate.v3_3.Hibernate3Singletons.instrumenter;
|
import static io.opentelemetry.javaagent.instrumentation.hibernate.v3_3.Hibernate3Singletons.instrumenter;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.any;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
||||||
|
@ -50,6 +51,7 @@ public class SessionInstrumentation implements TypeInstrumentation {
|
||||||
// Session synchronous methods we want to instrument.
|
// Session synchronous methods we want to instrument.
|
||||||
transformer.applyAdviceToMethod(
|
transformer.applyAdviceToMethod(
|
||||||
isMethod()
|
isMethod()
|
||||||
|
.and(takesArgument(0, any()))
|
||||||
.and(
|
.and(
|
||||||
namedOneOf(
|
namedOneOf(
|
||||||
"save",
|
"save",
|
||||||
|
|
|
@ -62,9 +62,9 @@ public class QueryInstrumentation implements TypeInstrumentation {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualField<Query, SessionInfo> criteriaVirtualField =
|
VirtualField<Query, SessionInfo> queryVirtualField =
|
||||||
VirtualField.find(Query.class, SessionInfo.class);
|
VirtualField.find(Query.class, SessionInfo.class);
|
||||||
SessionInfo sessionInfo = criteriaVirtualField.get(query);
|
SessionInfo sessionInfo = queryVirtualField.get(query);
|
||||||
|
|
||||||
Context parentContext = Java8BytecodeBridge.currentContext();
|
Context parentContext = Java8BytecodeBridge.currentContext();
|
||||||
hibernateOperation =
|
hibernateOperation =
|
||||||
|
|
|
@ -8,7 +8,6 @@ package io.opentelemetry.javaagent.instrumentation.hibernate.v4_0;
|
||||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
@ -32,7 +31,7 @@ public class SessionFactoryInstrumentation implements TypeInstrumentation {
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return implementsInterface(
|
return implementsInterface(
|
||||||
named("org.hibernate.SessionFactory").or(named("org.hibernate.SessionBuilder")));
|
namedOneOf("org.hibernate.SessionFactory", "org.hibernate.SessionBuilder"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -10,6 +10,7 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.
|
||||||
import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getEntityName;
|
import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getEntityName;
|
||||||
import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getSessionMethodOperationName;
|
import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getSessionMethodOperationName;
|
||||||
import static io.opentelemetry.javaagent.instrumentation.hibernate.v4_0.Hibernate4Singletons.instrumenter;
|
import static io.opentelemetry.javaagent.instrumentation.hibernate.v4_0.Hibernate4Singletons.instrumenter;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.any;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
||||||
|
@ -51,6 +52,7 @@ public class SessionInstrumentation implements TypeInstrumentation {
|
||||||
// Session synchronous methods we want to instrument.
|
// Session synchronous methods we want to instrument.
|
||||||
transformer.applyAdviceToMethod(
|
transformer.applyAdviceToMethod(
|
||||||
isMethod()
|
isMethod()
|
||||||
|
.and(takesArgument(0, any()))
|
||||||
.and(
|
.and(
|
||||||
namedOneOf(
|
namedOneOf(
|
||||||
"save",
|
"save",
|
||||||
|
@ -68,7 +70,7 @@ public class SessionInstrumentation implements TypeInstrumentation {
|
||||||
// Handle the non-generic 'get' separately.
|
// Handle the non-generic 'get' separately.
|
||||||
transformer.applyAdviceToMethod(
|
transformer.applyAdviceToMethod(
|
||||||
isMethod()
|
isMethod()
|
||||||
.and(named("get").or(named("find")))
|
.and(namedOneOf("get", "find"))
|
||||||
.and(returns(Object.class))
|
.and(returns(Object.class))
|
||||||
.and(takesArgument(0, String.class).or(takesArgument(0, Class.class))),
|
.and(takesArgument(0, String.class).or(takesArgument(0, Class.class))),
|
||||||
SessionInstrumentation.class.getName() + "$SessionMethodAdvice");
|
SessionInstrumentation.class.getName() + "$SessionMethodAdvice");
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
plugins {
|
||||||
|
id("otel.javaagent-instrumentation")
|
||||||
|
}
|
||||||
|
|
||||||
|
muzzle {
|
||||||
|
pass {
|
||||||
|
group.set("org.hibernate")
|
||||||
|
module.set("hibernate-core")
|
||||||
|
versions.set("[6.0.0.Final,)")
|
||||||
|
assertInverse.set(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
library("org.hibernate:hibernate-core:6.0.0.Final")
|
||||||
|
|
||||||
|
implementation(project(":instrumentation:hibernate:hibernate-common:javaagent"))
|
||||||
|
|
||||||
|
testInstrumentation(project(":instrumentation:jdbc:javaagent"))
|
||||||
|
// Added to ensure cross compatibility:
|
||||||
|
testInstrumentation(project(":instrumentation:hibernate:hibernate-3.3:javaagent"))
|
||||||
|
testInstrumentation(project(":instrumentation:hibernate:hibernate-4.0:javaagent"))
|
||||||
|
testInstrumentation(project(":instrumentation:hibernate:hibernate-procedure-call-4.3:javaagent"))
|
||||||
|
|
||||||
|
testImplementation("com.h2database:h2:1.4.197")
|
||||||
|
testImplementation("javax.xml.bind:jaxb-api:2.2.11")
|
||||||
|
testImplementation("com.sun.xml.bind:jaxb-core:2.2.11")
|
||||||
|
testImplementation("com.sun.xml.bind:jaxb-impl:2.2.11")
|
||||||
|
testImplementation("javax.activation:activation:1.1.1")
|
||||||
|
testImplementation("org.hsqldb:hsqldb:2.0.0")
|
||||||
|
testImplementation("org.springframework.data:spring-data-jpa:3.0.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
otelJava {
|
||||||
|
minJavaVersionSupported.set(JavaVersion.VERSION_11)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<Test>().configureEach {
|
||||||
|
// TODO run tests both with and without experimental span attributes
|
||||||
|
jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true")
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.hibernate.v6_0;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
import org.hibernate.SharedSessionContract;
|
||||||
|
import org.hibernate.internal.SessionImpl;
|
||||||
|
import org.hibernate.internal.StatelessSessionImpl;
|
||||||
|
|
||||||
|
public final class EntityNameUtil {
|
||||||
|
|
||||||
|
private EntityNameUtil() {}
|
||||||
|
|
||||||
|
private static String bestGuessEntityName(SharedSessionContract session, Object entity) {
|
||||||
|
if (entity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session instanceof SessionImpl) {
|
||||||
|
return ((SessionImpl) session).bestGuessEntityName(entity);
|
||||||
|
} else if (session instanceof StatelessSessionImpl) {
|
||||||
|
return ((StatelessSessionImpl) session).bestGuessEntityName(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Function<Object, String> bestGuessEntityName(SharedSessionContract session) {
|
||||||
|
return (entity) -> bestGuessEntityName(session, entity);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.hibernate.v6_0;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateInstrumenterFactory;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation;
|
||||||
|
|
||||||
|
public class Hibernate6Singletons {
|
||||||
|
|
||||||
|
private static final Instrumenter<HibernateOperation, Void> INSTANCE =
|
||||||
|
HibernateInstrumenterFactory.createInstrumenter("io.opentelemetry.hibernate-6.0");
|
||||||
|
|
||||||
|
public static Instrumenter<HibernateOperation, Void> instrumenter() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Hibernate6Singletons() {}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.hibernate.v6_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||||
|
import java.util.List;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@AutoService(InstrumentationModule.class)
|
||||||
|
public class HibernateInstrumentationModule extends InstrumentationModule {
|
||||||
|
|
||||||
|
public HibernateInstrumentationModule() {
|
||||||
|
super("hibernate", "hibernate-6.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
|
||||||
|
return hasClassesNamed(
|
||||||
|
// not present before 6.0
|
||||||
|
"org.hibernate.query.Query");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TypeInstrumentation> typeInstrumentations() {
|
||||||
|
return asList(
|
||||||
|
new QueryInstrumentation(),
|
||||||
|
new SessionFactoryInstrumentation(),
|
||||||
|
new SessionInstrumentation(),
|
||||||
|
new TransactionInstrumentation());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.hibernate.v6_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||||
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getOperationNameForQuery;
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.hibernate.v6_0.Hibernate6Singletons.instrumenter;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
||||||
|
|
||||||
|
import io.opentelemetry.context.Context;
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import io.opentelemetry.instrumentation.api.util.VirtualField;
|
||||||
|
import io.opentelemetry.javaagent.bootstrap.CallDepth;
|
||||||
|
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.hibernate.query.CommonQueryContract;
|
||||||
|
import org.hibernate.query.Query;
|
||||||
|
import org.hibernate.query.spi.SqmQuery;
|
||||||
|
|
||||||
|
public class QueryInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
||||||
|
return hasClassesNamed("org.hibernate.query.CommonQueryContract");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return implementsInterface(named("org.hibernate.query.CommonQueryContract"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform(TypeTransformer transformer) {
|
||||||
|
transformer.applyAdviceToMethod(
|
||||||
|
isMethod()
|
||||||
|
.and(
|
||||||
|
namedOneOf(
|
||||||
|
"list",
|
||||||
|
"getResultList",
|
||||||
|
"stream",
|
||||||
|
"getResultStream",
|
||||||
|
"uniqueResult",
|
||||||
|
"getSingleResult",
|
||||||
|
"getSingleResultOrNull",
|
||||||
|
"uniqueResultOptional",
|
||||||
|
"executeUpdate",
|
||||||
|
"scroll")),
|
||||||
|
QueryInstrumentation.class.getName() + "$QueryMethodAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static class QueryMethodAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void startMethod(
|
||||||
|
@Advice.This CommonQueryContract query,
|
||||||
|
@Advice.Local("otelCallDepth") CallDepth callDepth,
|
||||||
|
@Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation,
|
||||||
|
@Advice.Local("otelContext") Context context,
|
||||||
|
@Advice.Local("otelScope") Scope scope) {
|
||||||
|
|
||||||
|
callDepth = CallDepth.forClass(HibernateOperation.class);
|
||||||
|
if (callDepth.getAndIncrement() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String queryString = null;
|
||||||
|
if (query instanceof Query) {
|
||||||
|
queryString = ((Query<?>) query).getQueryString();
|
||||||
|
}
|
||||||
|
if (query instanceof SqmQuery) {
|
||||||
|
try {
|
||||||
|
queryString = ((SqmQuery) query).getSqmStatement().toHqlString();
|
||||||
|
} catch (RuntimeException exception) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualField<CommonQueryContract, SessionInfo> queryVirtualField =
|
||||||
|
VirtualField.find(CommonQueryContract.class, SessionInfo.class);
|
||||||
|
SessionInfo sessionInfo = queryVirtualField.get(query);
|
||||||
|
|
||||||
|
Context parentContext = Java8BytecodeBridge.currentContext();
|
||||||
|
hibernateOperation =
|
||||||
|
new HibernateOperation(getOperationNameForQuery(queryString), sessionInfo);
|
||||||
|
if (!instrumenter().shouldStart(parentContext, hibernateOperation)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context = instrumenter().start(parentContext, hibernateOperation);
|
||||||
|
scope = context.makeCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void endMethod(
|
||||||
|
@Advice.Thrown Throwable throwable,
|
||||||
|
@Advice.Local("otelCallDepth") CallDepth callDepth,
|
||||||
|
@Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation,
|
||||||
|
@Advice.Local("otelContext") Context context,
|
||||||
|
@Advice.Local("otelScope") Scope scope) {
|
||||||
|
|
||||||
|
if (callDepth.decrementAndGet() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope != null) {
|
||||||
|
scope.close();
|
||||||
|
instrumenter().end(context, hibernateOperation, null, throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.hibernate.v6_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||||
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.api.util.VirtualField;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.hibernate.SharedSessionContract;
|
||||||
|
|
||||||
|
public class SessionFactoryInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
||||||
|
return hasClassesNamed("org.hibernate.SessionFactory", "org.hibernate.SessionBuilder");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return implementsInterface(
|
||||||
|
namedOneOf("org.hibernate.SessionFactory", "org.hibernate.SessionBuilder"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform(TypeTransformer transformer) {
|
||||||
|
transformer.applyAdviceToMethod(
|
||||||
|
isMethod()
|
||||||
|
.and(namedOneOf("openSession", "openStatelessSession"))
|
||||||
|
.and(takesArguments(0))
|
||||||
|
.and(returns(namedOneOf("org.hibernate.Session", "org.hibernate.StatelessSession"))),
|
||||||
|
SessionFactoryInstrumentation.class.getName() + "$SessionFactoryAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static class SessionFactoryAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
|
public static void openSession(@Advice.Return SharedSessionContract session) {
|
||||||
|
|
||||||
|
VirtualField<SharedSessionContract, SessionInfo> virtualField =
|
||||||
|
VirtualField.find(SharedSessionContract.class, SessionInfo.class);
|
||||||
|
virtualField.set(session, new SessionInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,195 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.hibernate.v6_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||||
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getEntityName;
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.hibernate.OperationNameUtil.getSessionMethodOperationName;
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.hibernate.v6_0.Hibernate6Singletons.instrumenter;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.any;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import io.opentelemetry.context.Context;
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import io.opentelemetry.instrumentation.api.util.VirtualField;
|
||||||
|
import io.opentelemetry.javaagent.bootstrap.CallDepth;
|
||||||
|
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo;
|
||||||
|
import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.hibernate.SharedSessionContract;
|
||||||
|
import org.hibernate.Transaction;
|
||||||
|
import org.hibernate.query.CommonQueryContract;
|
||||||
|
|
||||||
|
public class SessionInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
||||||
|
return hasClassesNamed("org.hibernate.SharedSessionContract");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return implementsInterface(named("org.hibernate.SharedSessionContract"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform(TypeTransformer transformer) {
|
||||||
|
|
||||||
|
// Session synchronous methods we want to instrument.
|
||||||
|
transformer.applyAdviceToMethod(
|
||||||
|
isMethod()
|
||||||
|
.and(takesArgument(0, any()))
|
||||||
|
.and(
|
||||||
|
namedOneOf(
|
||||||
|
"save",
|
||||||
|
"replicate",
|
||||||
|
"saveOrUpdate",
|
||||||
|
"update",
|
||||||
|
"merge",
|
||||||
|
"persist",
|
||||||
|
"lock",
|
||||||
|
"fireLock",
|
||||||
|
"refresh",
|
||||||
|
"insert",
|
||||||
|
"delete")),
|
||||||
|
SessionInstrumentation.class.getName() + "$SessionMethodAdvice");
|
||||||
|
// Handle the non-generic 'get' separately.
|
||||||
|
transformer.applyAdviceToMethod(
|
||||||
|
isMethod()
|
||||||
|
.and(namedOneOf("get", "find"))
|
||||||
|
.and(returns(Object.class))
|
||||||
|
.and(takesArgument(0, String.class).or(takesArgument(0, Class.class))),
|
||||||
|
SessionInstrumentation.class.getName() + "$SessionMethodAdvice");
|
||||||
|
|
||||||
|
// These methods return some object that we want to instrument, and so the Advice will pin the
|
||||||
|
// current SessionInfo to the returned object using a VirtualField.
|
||||||
|
transformer.applyAdviceToMethod(
|
||||||
|
isMethod()
|
||||||
|
.and(namedOneOf("beginTransaction", "getTransaction"))
|
||||||
|
.and(returns(named("org.hibernate.Transaction"))),
|
||||||
|
SessionInstrumentation.class.getName() + "$GetTransactionAdvice");
|
||||||
|
|
||||||
|
transformer.applyAdviceToMethod(
|
||||||
|
isMethod()
|
||||||
|
.and(returns(implementsInterface(named("org.hibernate.query.CommonQueryContract")))),
|
||||||
|
SessionInstrumentation.class.getName() + "$GetQueryAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static class SessionMethodAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void startMethod(
|
||||||
|
@Advice.This SharedSessionContract session,
|
||||||
|
@Advice.Origin("#m") String name,
|
||||||
|
@Advice.Origin("#d") String descriptor,
|
||||||
|
@Advice.Argument(0) Object arg0,
|
||||||
|
@Advice.Argument(value = 1, optional = true) Object arg1,
|
||||||
|
@Advice.Local("otelCallDepth") CallDepth callDepth,
|
||||||
|
@Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation,
|
||||||
|
@Advice.Local("otelContext") Context context,
|
||||||
|
@Advice.Local("otelScope") Scope scope) {
|
||||||
|
|
||||||
|
callDepth = CallDepth.forClass(HibernateOperation.class);
|
||||||
|
if (callDepth.getAndIncrement() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualField<SharedSessionContract, SessionInfo> virtualField =
|
||||||
|
VirtualField.find(SharedSessionContract.class, SessionInfo.class);
|
||||||
|
SessionInfo sessionInfo = virtualField.get(session);
|
||||||
|
|
||||||
|
Context parentContext = Java8BytecodeBridge.currentContext();
|
||||||
|
String entityName =
|
||||||
|
getEntityName(descriptor, arg0, arg1, EntityNameUtil.bestGuessEntityName(session));
|
||||||
|
hibernateOperation =
|
||||||
|
new HibernateOperation(getSessionMethodOperationName(name), entityName, sessionInfo);
|
||||||
|
if (!instrumenter().shouldStart(parentContext, hibernateOperation)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context = instrumenter().start(parentContext, hibernateOperation);
|
||||||
|
scope = context.makeCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void endMethod(
|
||||||
|
@Advice.Thrown Throwable throwable,
|
||||||
|
@Advice.Local("otelCallDepth") CallDepth callDepth,
|
||||||
|
@Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation,
|
||||||
|
@Advice.Local("otelContext") Context context,
|
||||||
|
@Advice.Local("otelScope") Scope scope) {
|
||||||
|
|
||||||
|
if (callDepth.decrementAndGet() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope != null) {
|
||||||
|
scope.close();
|
||||||
|
instrumenter().end(context, hibernateOperation, null, throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static class GetQueryAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
|
public static void getQuery(
|
||||||
|
@Advice.This SharedSessionContract session, @Advice.Return CommonQueryContract query) {
|
||||||
|
|
||||||
|
VirtualField<SharedSessionContract, SessionInfo> sessionVirtualField =
|
||||||
|
VirtualField.find(SharedSessionContract.class, SessionInfo.class);
|
||||||
|
VirtualField<CommonQueryContract, SessionInfo> queryVirtualField =
|
||||||
|
VirtualField.find(CommonQueryContract.class, SessionInfo.class);
|
||||||
|
|
||||||
|
queryVirtualField.set(query, sessionVirtualField.get(session));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static class GetTransactionAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
|
public static void getTransaction(
|
||||||
|
@Advice.This SharedSessionContract session, @Advice.Return Transaction transaction) {
|
||||||
|
|
||||||
|
VirtualField<SharedSessionContract, SessionInfo> sessionVirtualField =
|
||||||
|
VirtualField.find(SharedSessionContract.class, SessionInfo.class);
|
||||||
|
VirtualField<Transaction, SessionInfo> transactionVirtualField =
|
||||||
|
VirtualField.find(Transaction.class, SessionInfo.class);
|
||||||
|
|
||||||
|
transactionVirtualField.set(transaction, sessionVirtualField.get(session));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static class GetCriteriaAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
|
public static void getCriteria(
|
||||||
|
@Advice.This SharedSessionContract session, @Advice.Return CriteriaQuery<?> criteria) {
|
||||||
|
|
||||||
|
VirtualField<SharedSessionContract, SessionInfo> sessionVirtualField =
|
||||||
|
VirtualField.find(SharedSessionContract.class, SessionInfo.class);
|
||||||
|
VirtualField<CriteriaQuery<?>, SessionInfo> criteriaVirtualField =
|
||||||
|
VirtualField.find(CriteriaQuery.class, SessionInfo.class);
|
||||||
|
|
||||||
|
criteriaVirtualField.set(criteria, sessionVirtualField.get(session));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.hibernate.v6_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||||
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.hibernate.v6_0.Hibernate6Singletons.instrumenter;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
import io.opentelemetry.context.Context;
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import io.opentelemetry.instrumentation.api.util.VirtualField;
|
||||||
|
import io.opentelemetry.javaagent.bootstrap.CallDepth;
|
||||||
|
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.hibernate.HibernateOperation;
|
||||||
|
import io.opentelemetry.javaagent.instrumentation.hibernate.SessionInfo;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import org.hibernate.Transaction;
|
||||||
|
|
||||||
|
public class TransactionInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
||||||
|
return hasClassesNamed("org.hibernate.Transaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return implementsInterface(named("org.hibernate.Transaction"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform(TypeTransformer transformer) {
|
||||||
|
transformer.applyAdviceToMethod(
|
||||||
|
isMethod().and(named("commit")).and(takesArguments(0)),
|
||||||
|
TransactionInstrumentation.class.getName() + "$TransactionCommitAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static class TransactionCommitAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static void startCommit(
|
||||||
|
@Advice.This Transaction transaction,
|
||||||
|
@Advice.Local("otelCallDepth") CallDepth callDepth,
|
||||||
|
@Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation,
|
||||||
|
@Advice.Local("otelContext") Context context,
|
||||||
|
@Advice.Local("otelScope") Scope scope) {
|
||||||
|
|
||||||
|
callDepth = CallDepth.forClass(HibernateOperation.class);
|
||||||
|
if (callDepth.getAndIncrement() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualField<Transaction, SessionInfo> transactionVirtualField =
|
||||||
|
VirtualField.find(Transaction.class, SessionInfo.class);
|
||||||
|
SessionInfo sessionInfo = transactionVirtualField.get(transaction);
|
||||||
|
|
||||||
|
Context parentContext = Java8BytecodeBridge.currentContext();
|
||||||
|
hibernateOperation = new HibernateOperation("Transaction.commit", sessionInfo);
|
||||||
|
if (!instrumenter().shouldStart(parentContext, hibernateOperation)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context = instrumenter().start(parentContext, hibernateOperation);
|
||||||
|
scope = context.makeCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void endCommit(
|
||||||
|
@Advice.Thrown Throwable throwable,
|
||||||
|
@Advice.Local("otelCallDepth") CallDepth callDepth,
|
||||||
|
@Advice.Local("otelHibernateOperation") HibernateOperation hibernateOperation,
|
||||||
|
@Advice.Local("otelContext") Context context,
|
||||||
|
@Advice.Local("otelScope") Scope scope) {
|
||||||
|
|
||||||
|
if (callDepth.decrementAndGet() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope != null) {
|
||||||
|
scope.close();
|
||||||
|
instrumenter().end(context, hibernateOperation, null, throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
|
||||||
|
import org.hibernate.Session
|
||||||
|
import org.hibernate.SessionFactory
|
||||||
|
import org.hibernate.cfg.Configuration
|
||||||
|
import spock.lang.Shared
|
||||||
|
|
||||||
|
abstract class AbstractHibernateTest extends AgentInstrumentationSpecification {
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
protected SessionFactory sessionFactory
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
protected List<Value> prepopulated
|
||||||
|
|
||||||
|
def setupSpec() {
|
||||||
|
sessionFactory = new Configuration().configure().buildSessionFactory()
|
||||||
|
// Pre-populate the DB, so delete/update can be tested.
|
||||||
|
Session writer = sessionFactory.openSession()
|
||||||
|
writer.beginTransaction()
|
||||||
|
prepopulated = new ArrayList<>()
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
prepopulated.add(new Value("Hello :) " + i))
|
||||||
|
writer.save(prepopulated.get(i))
|
||||||
|
}
|
||||||
|
writer.getTransaction().commit()
|
||||||
|
writer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
def cleanupSpec() {
|
||||||
|
if (sessionFactory != null) {
|
||||||
|
sessionFactory.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||||
|
import jakarta.persistence.criteria.CriteriaBuilder
|
||||||
|
import jakarta.persistence.criteria.CriteriaQuery
|
||||||
|
import jakarta.persistence.criteria.Root
|
||||||
|
import org.hibernate.Session
|
||||||
|
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.CLIENT
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||||
|
|
||||||
|
class CriteriaTest extends AbstractHibernateTest {
|
||||||
|
|
||||||
|
def "test criteria query.#methodName"() {
|
||||||
|
setup:
|
||||||
|
runWithSpan("parent") {
|
||||||
|
Session session = sessionFactory.openSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder()
|
||||||
|
CriteriaQuery<Value> createQuery = criteriaBuilder.createQuery(Value)
|
||||||
|
Root<Value> root = createQuery.from(Value)
|
||||||
|
createQuery.select(root)
|
||||||
|
.where(criteriaBuilder.like(root.get("name"), "Hello"))
|
||||||
|
.orderBy(criteriaBuilder.desc(root.get("name")))
|
||||||
|
def query= session.createQuery(createQuery)
|
||||||
|
interaction.call(query)
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
expect:
|
||||||
|
def sessionId
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name "SELECT Value"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "SELECT db1.Value"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(1)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" ~/^select /
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "SELECT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
methodName | interaction
|
||||||
|
"getResultList" | { c -> c.getResultList() }
|
||||||
|
"uniqueResult" | { c -> c.uniqueResult() }
|
||||||
|
"getSingleResultOrNull" | { c -> c.getSingleResultOrNull() }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,233 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||||
|
import jakarta.persistence.EntityManager
|
||||||
|
import jakarta.persistence.EntityManagerFactory
|
||||||
|
import jakarta.persistence.EntityTransaction
|
||||||
|
import jakarta.persistence.LockModeType
|
||||||
|
import jakarta.persistence.Persistence
|
||||||
|
import jakarta.persistence.Query
|
||||||
|
import spock.lang.Shared
|
||||||
|
import spock.lang.Unroll
|
||||||
|
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.CLIENT
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||||
|
|
||||||
|
class EntityManagerTest extends AbstractHibernateTest {
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("test-pu")
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
def "test hibernate action #testName"() {
|
||||||
|
setup:
|
||||||
|
EntityManager entityManager = entityManagerFactory.createEntityManager()
|
||||||
|
EntityTransaction entityTransaction = entityManager.getTransaction()
|
||||||
|
entityTransaction.begin()
|
||||||
|
|
||||||
|
def entity = prepopulated.get(0)
|
||||||
|
if (attach) {
|
||||||
|
entity = runWithSpan("setup") {
|
||||||
|
entityManager.merge(prepopulated.get(0))
|
||||||
|
}
|
||||||
|
ignoreTracesAndClear(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
when:
|
||||||
|
runWithSpan("parent") {
|
||||||
|
try {
|
||||||
|
sessionMethodTest.call(entityManager, entity)
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We expected this, we should see the error field set on the span.
|
||||||
|
}
|
||||||
|
|
||||||
|
entityTransaction.commit()
|
||||||
|
entityManager.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
then:
|
||||||
|
boolean isPersistTest = "persist" == testName
|
||||||
|
def sessionId
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4 + (isPersistTest ? 1 : 0)) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name ~/Session.$methodName $resource/
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def offset = 0
|
||||||
|
if (isPersistTest) {
|
||||||
|
// persist test has an extra query for getting id of inserted element
|
||||||
|
offset = 1
|
||||||
|
span(2) {
|
||||||
|
name "SELECT db1.Value"
|
||||||
|
childOf span(1)
|
||||||
|
kind CLIENT
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" String
|
||||||
|
"$SemanticAttributes.DB_OPERATION" String
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!flushOnCommit) {
|
||||||
|
span(2 + offset) {
|
||||||
|
childOf span(1)
|
||||||
|
kind CLIENT
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" String
|
||||||
|
"$SemanticAttributes.DB_OPERATION" String
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3 + offset) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
span(2 + offset) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3 + offset) {
|
||||||
|
childOf span(2 + offset)
|
||||||
|
kind CLIENT
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" String
|
||||||
|
"$SemanticAttributes.DB_OPERATION" String
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
testName | methodName | resource | attach | flushOnCommit | sessionMethodTest
|
||||||
|
"lock" | "lock" | "Value" | true | false | { em, val ->
|
||||||
|
em.lock(val, LockModeType.PESSIMISTIC_READ)
|
||||||
|
}
|
||||||
|
"refresh" | "refresh" | "Value" | true | false | { em, val ->
|
||||||
|
em.refresh(val)
|
||||||
|
}
|
||||||
|
"find" | "(get|find)" | "Value" | false | false | { em, val ->
|
||||||
|
em.find(Value, val.getId())
|
||||||
|
}
|
||||||
|
"persist" | "persist" | "Value" | false | true | { em, val ->
|
||||||
|
em.persist(new Value("insert me"))
|
||||||
|
}
|
||||||
|
"merge" | "merge" | "Value" | true | true | { em, val ->
|
||||||
|
val.setName("New name")
|
||||||
|
em.merge(val)
|
||||||
|
}
|
||||||
|
"remove" | "delete" | "Value" | true | true | { em, val ->
|
||||||
|
em.remove(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
def "test attaches State to query created via #queryMethodName"() {
|
||||||
|
setup:
|
||||||
|
runWithSpan("parent") {
|
||||||
|
EntityManager entityManager = entityManagerFactory.createEntityManager()
|
||||||
|
EntityTransaction entityTransaction = entityManager.getTransaction()
|
||||||
|
entityTransaction.begin()
|
||||||
|
Query query = queryBuildMethod(entityManager)
|
||||||
|
query.getResultList()
|
||||||
|
entityTransaction.commit()
|
||||||
|
entityManager.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
expect:
|
||||||
|
def sessionId
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name resource
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "SELECT db1.Value"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(1)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" String
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "SELECT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
queryMethodName | resource | queryBuildMethod
|
||||||
|
"createQuery" | "SELECT Value" | { em -> em.createQuery("from Value") }
|
||||||
|
"getNamedQuery" | "SELECT Value" | { em -> em.createNamedQuery("TestNamedQuery") }
|
||||||
|
"createSQLQuery" | "SELECT Value" | { em -> em.createNativeQuery("SELECT * FROM Value") }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||||
|
import jakarta.persistence.ParameterMode
|
||||||
|
import org.hibernate.Session
|
||||||
|
import org.hibernate.SessionFactory
|
||||||
|
import org.hibernate.cfg.Configuration
|
||||||
|
import org.hibernate.exception.SQLGrammarException
|
||||||
|
import org.hibernate.procedure.ProcedureCall
|
||||||
|
import spock.lang.Shared
|
||||||
|
|
||||||
|
import java.sql.Connection
|
||||||
|
import java.sql.DriverManager
|
||||||
|
import java.sql.Statement
|
||||||
|
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.CLIENT
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||||
|
import static io.opentelemetry.api.trace.StatusCode.ERROR
|
||||||
|
|
||||||
|
class ProcedureCallTest extends AgentInstrumentationSpecification {
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
protected SessionFactory sessionFactory
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
protected List<Value> prepopulated
|
||||||
|
|
||||||
|
def setupSpec() {
|
||||||
|
sessionFactory = new Configuration().configure("procedure-call-hibernate.cfg.xml").buildSessionFactory()
|
||||||
|
// Pre-populate the DB, so delete/update can be tested.
|
||||||
|
Session writer = sessionFactory.openSession()
|
||||||
|
writer.beginTransaction()
|
||||||
|
prepopulated = new ArrayList<>()
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
prepopulated.add(new Value("Hello :) " + i))
|
||||||
|
writer.save(prepopulated.get(i))
|
||||||
|
}
|
||||||
|
writer.getTransaction().commit()
|
||||||
|
writer.close()
|
||||||
|
|
||||||
|
// Create a stored procedure.
|
||||||
|
Connection conn = DriverManager.getConnection("jdbc:hsqldb:mem:test", "sa", "1")
|
||||||
|
Statement stmt = conn.createStatement()
|
||||||
|
stmt.execute("CREATE PROCEDURE TEST_PROC() MODIFIES SQL DATA BEGIN ATOMIC INSERT INTO Value VALUES (420, 'fred'); END")
|
||||||
|
stmt.close()
|
||||||
|
conn.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
def cleanupSpec() {
|
||||||
|
if (sessionFactory != null) {
|
||||||
|
sessionFactory.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test ProcedureCall"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
runWithSpan("parent") {
|
||||||
|
Session session = sessionFactory.openSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
|
||||||
|
ProcedureCall call = session.createStoredProcedureCall("TEST_PROC")
|
||||||
|
call.getOutputs()
|
||||||
|
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
expect:
|
||||||
|
def sessionId
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name "ProcedureCall.getOutputs TEST_PROC"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "CALL test.TEST_PROC"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(1)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "hsqldb"
|
||||||
|
"$SemanticAttributes.DB_NAME" "test"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" "{call TEST_PROC()}"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "hsqldb:mem:"
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "CALL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
kind INTERNAL
|
||||||
|
name "Transaction.commit"
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test failing ProcedureCall"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
runWithSpan("parent") {
|
||||||
|
Session session = sessionFactory.openSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
|
||||||
|
ProcedureCall call = session.createStoredProcedureCall("TEST_PROC")
|
||||||
|
def parameterRegistration = call.registerParameter("nonexistent", Long, ParameterMode.IN)
|
||||||
|
call.setParameter(parameterRegistration, 420L)
|
||||||
|
try {
|
||||||
|
call.getOutputs()
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We expected this.
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
expect:
|
||||||
|
def sessionId
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 3) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name "ProcedureCall.getOutputs TEST_PROC"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
status ERROR
|
||||||
|
errorEvent(SQLGrammarException, "could not prepare statement")
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,603 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||||
|
import org.hibernate.LockMode
|
||||||
|
import org.hibernate.LockOptions
|
||||||
|
import org.hibernate.ReplicationMode
|
||||||
|
import org.hibernate.Session
|
||||||
|
import org.hibernate.UnknownEntityTypeException
|
||||||
|
import spock.lang.Shared
|
||||||
|
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.CLIENT
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||||
|
import static io.opentelemetry.api.trace.StatusCode.ERROR
|
||||||
|
|
||||||
|
class SessionTest extends AbstractHibernateTest {
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
private Closure sessionBuilder = { return sessionFactory.openSession() }
|
||||||
|
@Shared
|
||||||
|
private Closure statelessSessionBuilder = { return sessionFactory.openStatelessSession() }
|
||||||
|
|
||||||
|
|
||||||
|
def "test hibernate action #testName"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
// Test for each implementation of Session.
|
||||||
|
for (def buildSession : sessionImplementations) {
|
||||||
|
runWithSpan("parent") {
|
||||||
|
def session = buildSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
|
||||||
|
try {
|
||||||
|
sessionMethodTest.call(session, prepopulated.get(0))
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We expected this, we should see the error field set on the span.
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect:
|
||||||
|
def sessionId
|
||||||
|
assertTraces(sessionImplementations.size()) {
|
||||||
|
for (int i = 0; i < sessionImplementations.size(); i++) {
|
||||||
|
trace(i, 4) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name "Session.$methodName $resource"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
childOf span(1)
|
||||||
|
kind CLIENT
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" String
|
||||||
|
"$SemanticAttributes.DB_OPERATION" String
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
testName | methodName | resource | sessionImplementations | sessionMethodTest
|
||||||
|
"lock" | "lock" | "Value" | [sessionBuilder] | { sesh, val ->
|
||||||
|
sesh.lock(val, LockMode.READ)
|
||||||
|
}
|
||||||
|
"lock with entity name" | "lock" | "Value" | [sessionBuilder] | { sesh, val ->
|
||||||
|
sesh.lock("Value", val, LockMode.READ)
|
||||||
|
}
|
||||||
|
"lock with null name" | "lock" | "Value" | [sessionBuilder] | { sesh, val ->
|
||||||
|
sesh.lock(null, val, LockMode.READ)
|
||||||
|
}
|
||||||
|
"buildLockRequest" | "lock" | "Value" | [sessionBuilder] | { sesh, val ->
|
||||||
|
sesh.buildLockRequest(LockOptions.READ)
|
||||||
|
.lock(val)
|
||||||
|
}
|
||||||
|
"refresh" | "refresh" | "Value" | [sessionBuilder, statelessSessionBuilder] | { sesh, val ->
|
||||||
|
sesh.refresh(val)
|
||||||
|
}
|
||||||
|
"refresh with entity name" | "refresh" | "Value" | [sessionBuilder, statelessSessionBuilder] | { sesh, val ->
|
||||||
|
sesh.refresh("Value", val)
|
||||||
|
}
|
||||||
|
"get with entity name" | "get" | "Value" | [sessionBuilder, statelessSessionBuilder] | { sesh, val ->
|
||||||
|
sesh.get("Value", val.getId())
|
||||||
|
}
|
||||||
|
"get with entity class" | "get" | "Value" | [sessionBuilder, statelessSessionBuilder] | { sesh, val ->
|
||||||
|
sesh.get(Value, val.getId())
|
||||||
|
}
|
||||||
|
"insert" | "insert" | "Value" | [statelessSessionBuilder] | { sesh, val ->
|
||||||
|
sesh.insert(new Value("insert me"))
|
||||||
|
}
|
||||||
|
"insert with entity name" | "insert" | "Value" | [statelessSessionBuilder] | { sesh, val ->
|
||||||
|
sesh.insert("Value", new Value("insert me"))
|
||||||
|
}
|
||||||
|
"insert with null entity name" | "insert" | "Value" | [statelessSessionBuilder] | { sesh, val ->
|
||||||
|
sesh.insert(null, new Value("insert me"))
|
||||||
|
}
|
||||||
|
"update (StatelessSession)" | "update" | "Value" | [statelessSessionBuilder] | { sesh, val ->
|
||||||
|
val.setName("New name")
|
||||||
|
sesh.update(val)
|
||||||
|
}
|
||||||
|
"update with entity name (StatelessSession)" | "update" | "Value" | [statelessSessionBuilder] | { sesh, val ->
|
||||||
|
val.setName("New name")
|
||||||
|
sesh.update("Value", val)
|
||||||
|
}
|
||||||
|
"delete (Session)" | "delete" | "Value" | [statelessSessionBuilder] | { sesh, val ->
|
||||||
|
sesh.delete(val)
|
||||||
|
prepopulated.remove(val)
|
||||||
|
}
|
||||||
|
"delete with entity name (Session)" | "delete" | "Value" | [statelessSessionBuilder] | { sesh, val ->
|
||||||
|
sesh.delete("Value", val)
|
||||||
|
prepopulated.remove(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test hibernate replicate: #testName"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
// Test for each implementation of Session.
|
||||||
|
runWithSpan("parent") {
|
||||||
|
def session = sessionFactory.openSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
|
||||||
|
try {
|
||||||
|
sessionMethodTest.call(session, prepopulated.get(0))
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We expected this, we should see the error field set on the span.
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
expect:
|
||||||
|
def sessionId
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 5) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name "Session.$methodName $resource"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "SELECT db1.Value"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(1)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" ~/^select /
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "SELECT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(4) {
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(3)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" String
|
||||||
|
"$SemanticAttributes.DB_OPERATION" String
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
testName | methodName | resource | sessionMethodTest
|
||||||
|
"replicate" | "replicate" | "Value" | { sesh, val ->
|
||||||
|
Value replicated = new Value(val.getName() + " replicated")
|
||||||
|
replicated.setId(val.getId())
|
||||||
|
sesh.replicate(replicated, ReplicationMode.OVERWRITE)
|
||||||
|
}
|
||||||
|
"replicate by entityName" | "replicate" | "Value" | { sesh, val ->
|
||||||
|
Value replicated = new Value(val.getName() + " replicated")
|
||||||
|
replicated.setId(val.getId())
|
||||||
|
sesh.replicate("Value", replicated, ReplicationMode.OVERWRITE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test hibernate failed replicate"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
// Test for each implementation of Session.
|
||||||
|
runWithSpan("parent") {
|
||||||
|
def session = sessionFactory.openSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
|
||||||
|
try {
|
||||||
|
session.replicate(new Long(123) /* Not a valid entity */, ReplicationMode.OVERWRITE)
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We expected this, we should see the error field set on the span.
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
expect:
|
||||||
|
def sessionId
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 3) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name "Session.replicate java.lang.Long"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
status ERROR
|
||||||
|
errorEvent(UnknownEntityTypeException, "Unable to locate persister: java.lang.Long")
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def "test hibernate commit action #testName"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
runWithSpan("parent") {
|
||||||
|
def session = sessionBuilder()
|
||||||
|
session.beginTransaction()
|
||||||
|
|
||||||
|
try {
|
||||||
|
sessionMethodTest.call(session, prepopulated.get(0))
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We expected this, we should see the error field set on the span.
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
expect:
|
||||||
|
def sessionId
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name "Session.$methodName $resource"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(2)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" String
|
||||||
|
"$SemanticAttributes.DB_OPERATION" String
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
testName | methodName | resource | sessionMethodTest
|
||||||
|
"save" | "save" | "Value" | { sesh, val ->
|
||||||
|
sesh.save(new Value("Another value"))
|
||||||
|
}
|
||||||
|
"save with entity name" | "save" | "Value" | { sesh, val ->
|
||||||
|
sesh.save("Value", new Value("Another value"))
|
||||||
|
}
|
||||||
|
"saveOrUpdate save" | "saveOrUpdate" | "Value" | { sesh, val ->
|
||||||
|
sesh.saveOrUpdate(new Value("Value"))
|
||||||
|
}
|
||||||
|
"saveOrUpdate save with entity name" | "saveOrUpdate" | "Value" | { sesh, val ->
|
||||||
|
sesh.saveOrUpdate("Value", new Value("Value"))
|
||||||
|
}
|
||||||
|
"saveOrUpdate update with entity name" | "saveOrUpdate" | "Value" | { sesh, val ->
|
||||||
|
val.setName("New name")
|
||||||
|
sesh.saveOrUpdate("Value", val)
|
||||||
|
}
|
||||||
|
"merge" | "merge" | "Value" | { sesh, val ->
|
||||||
|
sesh.merge(new Value("merge me in"))
|
||||||
|
}
|
||||||
|
"merge with entity name" | "merge" | "Value" | { sesh, val ->
|
||||||
|
sesh.merge("Value", new Value("merge me in"))
|
||||||
|
}
|
||||||
|
"persist" | "persist" | "Value" | { sesh, val ->
|
||||||
|
sesh.persist(new Value("merge me in"))
|
||||||
|
}
|
||||||
|
"persist with entity name" | "persist" | "Value" | { sesh, val ->
|
||||||
|
sesh.persist("Value", new Value("merge me in"))
|
||||||
|
}
|
||||||
|
"persist with null entity name" | "persist" | "Value" | { sesh, val ->
|
||||||
|
sesh.persist(null, new Value("merge me in"))
|
||||||
|
}
|
||||||
|
"update (Session)" | "update" | "Value" | { sesh, val ->
|
||||||
|
val.setName("New name")
|
||||||
|
sesh.update(val)
|
||||||
|
}
|
||||||
|
"update by entityName (Session)" | "update" | "Value" | { sesh, val ->
|
||||||
|
val.setName("New name")
|
||||||
|
sesh.update("Value", val)
|
||||||
|
}
|
||||||
|
"delete (Session)" | "delete" | "Value" | { sesh, val ->
|
||||||
|
sesh.delete(val)
|
||||||
|
prepopulated.remove(val)
|
||||||
|
}
|
||||||
|
"delete by entityName (Session)" | "delete" | "Value" | { sesh, val ->
|
||||||
|
sesh.delete("Value", val)
|
||||||
|
prepopulated.remove(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test attaches State to query created via #queryMethodName"() {
|
||||||
|
setup:
|
||||||
|
runWithSpan("parent") {
|
||||||
|
Session session = sessionFactory.openSession()
|
||||||
|
session.beginTransaction()
|
||||||
|
def query = queryBuildMethod(session)
|
||||||
|
query.list()
|
||||||
|
session.getTransaction().commit()
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
expect:
|
||||||
|
def sessionId
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name resource
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(1)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" String
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "SELECT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
queryMethodName | resource | queryBuildMethod
|
||||||
|
"createQuery" | "SELECT Value" | { sess -> sess.createQuery("from Value") }
|
||||||
|
"getNamedQuery" | "SELECT Value" | { sess -> sess.getNamedQuery("TestNamedQuery") }
|
||||||
|
"createNativeQuery" | "SELECT Value" | { sess -> sess.createNativeQuery("SELECT * FROM Value") }
|
||||||
|
"createSelectionQuery" | "SELECT Value" | { sess -> sess.createSelectionQuery("from Value") }
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test hibernate overlapping Sessions"() {
|
||||||
|
setup:
|
||||||
|
|
||||||
|
runWithSpan("overlapping Sessions") {
|
||||||
|
def session1 = sessionFactory.openSession()
|
||||||
|
session1.beginTransaction()
|
||||||
|
def session2 = sessionFactory.openStatelessSession()
|
||||||
|
def session3 = sessionFactory.openSession()
|
||||||
|
|
||||||
|
def value1 = new Value("Value 1")
|
||||||
|
session1.save(value1)
|
||||||
|
session2.insert(new Value("Value 2"))
|
||||||
|
session3.save(new Value("Value 3"))
|
||||||
|
session1.delete(value1)
|
||||||
|
|
||||||
|
session2.close()
|
||||||
|
session1.getTransaction().commit()
|
||||||
|
session1.close()
|
||||||
|
session3.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
expect:
|
||||||
|
def sessionId1
|
||||||
|
def sessionId2
|
||||||
|
def sessionId3
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 9) {
|
||||||
|
span(0) {
|
||||||
|
name "overlapping Sessions"
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name "Session.save Value"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId1 = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "Session.insert Value"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId2 = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
name "INSERT db1.Value"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(2)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" ~/^insert /
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "INSERT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(4) {
|
||||||
|
name "Session.save Value"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId3 = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(5) {
|
||||||
|
name "Session.delete Value"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(6) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(7) {
|
||||||
|
name "INSERT db1.Value"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(6)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" ~/^insert /
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "INSERT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(8) {
|
||||||
|
name "DELETE db1.Value"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(6)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "h2"
|
||||||
|
"$SemanticAttributes.DB_NAME" "db1"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "h2:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" ~/^delete /
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "DELETE"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sessionId1 != sessionId2 != sessionId3
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
|
import org.hibernate.annotations.NamedQuery;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table
|
||||||
|
@NamedQuery(name = "TestNamedQuery", query = "from Value")
|
||||||
|
public class Value {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Value() {}
|
||||||
|
|
||||||
|
public Value(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(generator = "increment")
|
||||||
|
@GenericGenerator(name = "increment", strategy = "increment")
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String title) {
|
||||||
|
name = title;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
|
||||||
|
version="2.0">
|
||||||
|
|
||||||
|
<persistence-unit name="test-pu">
|
||||||
|
<class>Value</class>
|
||||||
|
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||||
|
<properties>
|
||||||
|
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
|
||||||
|
<property name="hibernate.hbm2ddl.auto" value="update"/>
|
||||||
|
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
|
||||||
|
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE"/>
|
||||||
|
<property name="javax.persistence.jdbc.user" value="sa"/>
|
||||||
|
<property name="javax.persistence.jdbc.password" value=""/>
|
||||||
|
</properties>
|
||||||
|
</persistence-unit>
|
||||||
|
</persistence>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<!DOCTYPE hibernate-configuration PUBLIC
|
||||||
|
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
|
||||||
|
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
|
||||||
|
|
||||||
|
<hibernate-configuration>
|
||||||
|
|
||||||
|
<session-factory>
|
||||||
|
|
||||||
|
<!-- Use in-memory DB for testing -->
|
||||||
|
<property name="connection.driver_class">org.h2.Driver</property>
|
||||||
|
<property name="connection.url">jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE</property>
|
||||||
|
<property name="connection.username">sa</property>
|
||||||
|
<property name="connection.password"/>
|
||||||
|
<property name="dialect">org.hibernate.dialect.H2Dialect</property>
|
||||||
|
|
||||||
|
<property name="connection.pool_size">3</property>
|
||||||
|
<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
|
||||||
|
<property name="show_sql">true</property>
|
||||||
|
|
||||||
|
<!-- Reset the DB each test -->
|
||||||
|
<property name="hbm2ddl.auto">create</property>
|
||||||
|
|
||||||
|
<!-- Objects -->
|
||||||
|
<mapping class="Value"/>
|
||||||
|
|
||||||
|
</session-factory>
|
||||||
|
|
||||||
|
</hibernate-configuration>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<!DOCTYPE hibernate-configuration PUBLIC
|
||||||
|
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
|
||||||
|
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
|
||||||
|
|
||||||
|
<hibernate-configuration>
|
||||||
|
|
||||||
|
<session-factory>
|
||||||
|
|
||||||
|
<property name="hibernate.archive.autodetection">class,hbm</property>
|
||||||
|
<property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
|
||||||
|
<property name="hibernate.show_sql">true</property>
|
||||||
|
<property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
|
||||||
|
<property name="hibernate.connection.username">sa</property>
|
||||||
|
<property name="hibernate.connection.password">1</property>
|
||||||
|
<property name="hibernate.connection.url">jdbc:hsqldb:mem:test</property>
|
||||||
|
<property name="hibernate.hbm2ddl.auto">create</property>
|
||||||
|
|
||||||
|
<!-- Objects -->
|
||||||
|
<mapping class="Value"/>
|
||||||
|
|
||||||
|
</session-factory>
|
||||||
|
|
||||||
|
</hibernate-configuration>
|
|
@ -0,0 +1,26 @@
|
||||||
|
plugins {
|
||||||
|
id("otel.javaagent-testing")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
library("org.hibernate:hibernate-core:6.0.0.Final")
|
||||||
|
|
||||||
|
testInstrumentation(project(":instrumentation:hibernate:hibernate-6.0:javaagent"))
|
||||||
|
testInstrumentation(project(":instrumentation:jdbc:javaagent"))
|
||||||
|
// Added to ensure cross compatibility:
|
||||||
|
testInstrumentation(project(":instrumentation:hibernate:hibernate-3.3:javaagent"))
|
||||||
|
testInstrumentation(project(":instrumentation:hibernate:hibernate-4.0:javaagent"))
|
||||||
|
testInstrumentation(project(":instrumentation:hibernate:hibernate-procedure-call-4.3:javaagent"))
|
||||||
|
|
||||||
|
testImplementation("org.hsqldb:hsqldb:2.0.0")
|
||||||
|
testImplementation("org.springframework.data:spring-data-jpa:3.0.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
otelJava {
|
||||||
|
minJavaVersionSupported.set(JavaVersion.VERSION_17)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<Test>().configureEach {
|
||||||
|
// TODO run tests both with and without experimental span attributes
|
||||||
|
jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true")
|
||||||
|
}
|
|
@ -0,0 +1,385 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||||
|
import org.hibernate.Version
|
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||||
|
import spock.lang.Shared
|
||||||
|
import spring.jpa.Customer
|
||||||
|
import spring.jpa.CustomerRepository
|
||||||
|
import spring.jpa.PersistenceConfig
|
||||||
|
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.CLIENT
|
||||||
|
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||||
|
|
||||||
|
class SpringJpaTest extends AgentInstrumentationSpecification {
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
def context = new AnnotationConfigApplicationContext(PersistenceConfig)
|
||||||
|
|
||||||
|
@Shared
|
||||||
|
def repo = context.getBean(CustomerRepository)
|
||||||
|
|
||||||
|
def "test CRUD"() {
|
||||||
|
setup:
|
||||||
|
def isHibernate4 = Version.getVersionString().startsWith("4.")
|
||||||
|
def customer = new Customer("Bob", "Anonymous")
|
||||||
|
|
||||||
|
expect:
|
||||||
|
customer.id == null
|
||||||
|
!runWithSpan("parent") {
|
||||||
|
repo.findAll().iterator().hasNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
def sessionId
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name "SELECT spring.jpa.Customer"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "SELECT test.Customer"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(1)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "hsqldb"
|
||||||
|
"$SemanticAttributes.DB_NAME" "test"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "hsqldb:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" ~/select ([^.]+)\.id([^,]*),([^.]+)\.firstName([^,]*),([^.]+)\.lastName(.*)from Customer(.*)/
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "SELECT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Customer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearExportedData()
|
||||||
|
|
||||||
|
when:
|
||||||
|
runWithSpan("parent") {
|
||||||
|
repo.save(customer)
|
||||||
|
}
|
||||||
|
def savedId = customer.id
|
||||||
|
|
||||||
|
then:
|
||||||
|
customer.id != null
|
||||||
|
def sessionId2
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 4 + (isHibernate4 ? 0 : 1)) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name "Session.persist spring.jpa.Customer"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId2 = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isHibernate4) {
|
||||||
|
span(2) {
|
||||||
|
name "CALL test"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(1)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "hsqldb"
|
||||||
|
"$SemanticAttributes.DB_NAME" "test"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" "call next value for Customer_SEQ"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "hsqldb:mem:"
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "CALL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(4) {
|
||||||
|
name "INSERT test.Customer"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(3)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "hsqldb"
|
||||||
|
"$SemanticAttributes.DB_NAME" "test"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "hsqldb:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" ~/insert into Customer \(.*\) values \(.*, \?, \?\)/
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "INSERT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Customer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
span(2) {
|
||||||
|
name "INSERT test.Customer"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(1)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "hsqldb"
|
||||||
|
"$SemanticAttributes.DB_NAME" "test"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "hsqldb:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" ~/insert into Customer \(.*\) values \(.*, \?, \?\)/
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "INSERT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Customer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearExportedData()
|
||||||
|
|
||||||
|
when:
|
||||||
|
customer.firstName = "Bill"
|
||||||
|
runWithSpan("parent") {
|
||||||
|
repo.save(customer)
|
||||||
|
}
|
||||||
|
|
||||||
|
then:
|
||||||
|
customer.id == savedId
|
||||||
|
def sessionId3
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 5) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name "Session.merge spring.jpa.Customer"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" {
|
||||||
|
sessionId3 = it
|
||||||
|
it instanceof String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "SELECT test.Customer"
|
||||||
|
kind CLIENT
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "hsqldb"
|
||||||
|
"$SemanticAttributes.DB_NAME" "test"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "hsqldb:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" ~/select ([^.]+)\.id([^,]*),([^.]+)\.firstName([^,]*),([^.]+)\.lastName (.*)from Customer (.*)where ([^.]+)\.id( ?)=( ?)\?/
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "SELECT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Customer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" sessionId3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(4) {
|
||||||
|
name "UPDATE test.Customer"
|
||||||
|
kind CLIENT
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "hsqldb"
|
||||||
|
"$SemanticAttributes.DB_NAME" "test"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "hsqldb:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" "update Customer set firstName=?, lastName=? where id=?"
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "UPDATE"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Customer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearExportedData()
|
||||||
|
|
||||||
|
when:
|
||||||
|
customer = runWithSpan("parent") {
|
||||||
|
repo.findByLastName("Anonymous")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
then:
|
||||||
|
customer.id == savedId
|
||||||
|
customer.firstName == "Bill"
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 3) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1) {
|
||||||
|
name { it == "SELECT spring.jpa.Customer" || it == "Hibernate Query" }
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "SELECT test.Customer"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(1)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "hsqldb"
|
||||||
|
"$SemanticAttributes.DB_NAME" "test"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "hsqldb:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" ~/select ([^.]+)\.id([^,]*),([^.]+)\.firstName([^,]*),([^.]+)\.lastName (.*)from Customer (.*)(where ([^.]+)\.lastName( ?)=( ?)\?|)/
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "SELECT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Customer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearExportedData()
|
||||||
|
|
||||||
|
when:
|
||||||
|
runWithSpan("parent") {
|
||||||
|
repo.delete(customer)
|
||||||
|
}
|
||||||
|
|
||||||
|
then:
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 6 + (isHibernate4 ? 0 : 1)) {
|
||||||
|
span(0) {
|
||||||
|
name "parent"
|
||||||
|
kind INTERNAL
|
||||||
|
hasNoParent()
|
||||||
|
attributes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def offset = 0
|
||||||
|
if (!isHibernate4) {
|
||||||
|
offset = 2
|
||||||
|
span(1) {
|
||||||
|
name ~/Session.(get|find) spring.jpa.Customer/
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2) {
|
||||||
|
name "SELECT test.Customer"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(1)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "hsqldb"
|
||||||
|
"$SemanticAttributes.DB_NAME" "test"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "hsqldb:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" ~/select ([^.]+)\.id([^,]*),([^.]+)\.firstName([^,]*),([^.]+)\.lastName (.*)from Customer (.*)where ([^.]+)\.id( ?)=( ?)\?/
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "SELECT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Customer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(1 + offset) {
|
||||||
|
name "Session.merge spring.jpa.Customer"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isHibernate4) {
|
||||||
|
offset = 1
|
||||||
|
span(2) {
|
||||||
|
name "SELECT test.Customer"
|
||||||
|
kind CLIENT
|
||||||
|
childOf span(1)
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "hsqldb"
|
||||||
|
"$SemanticAttributes.DB_NAME" "test"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "hsqldb:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" ~/select ([^.]+)\.id([^,]*),([^.]+)\.firstName([^,]*),([^.]+)\.lastName (.*)from Customer (.*)where ([^.]+)\.id( ?)=( ?)\?/
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "SELECT"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Customer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(2 + offset) {
|
||||||
|
name "Session.delete spring.jpa.Customer"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(3 + offset) {
|
||||||
|
name "Transaction.commit"
|
||||||
|
kind INTERNAL
|
||||||
|
childOf span(0)
|
||||||
|
attributes {
|
||||||
|
"hibernate.session_id" String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span(4 + offset) {
|
||||||
|
name "DELETE test.Customer"
|
||||||
|
kind CLIENT
|
||||||
|
attributes {
|
||||||
|
"$SemanticAttributes.DB_SYSTEM" "hsqldb"
|
||||||
|
"$SemanticAttributes.DB_NAME" "test"
|
||||||
|
"$SemanticAttributes.DB_USER" "sa"
|
||||||
|
"$SemanticAttributes.DB_CONNECTION_STRING" "hsqldb:mem:"
|
||||||
|
"$SemanticAttributes.DB_STATEMENT" "delete from Customer where id=?"
|
||||||
|
"$SemanticAttributes.DB_OPERATION" "DELETE"
|
||||||
|
"$SemanticAttributes.DB_SQL_TABLE" "Customer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spring.jpa;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Customer {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
protected Customer() {}
|
||||||
|
|
||||||
|
public Customer(String firstName, String lastName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof Customer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Customer other = (Customer) obj;
|
||||||
|
return Objects.equals(id, other.id)
|
||||||
|
&& Objects.equals(firstName, other.firstName)
|
||||||
|
&& Objects.equals(lastName, other.lastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id, firstName, lastName);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spring.jpa;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
|
public interface CustomerRepository extends CrudRepository<Customer, Long> {
|
||||||
|
|
||||||
|
List<Customer> findByLastName(String lastName);
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package spring.jpa;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
||||||
|
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||||
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
|
import org.springframework.orm.jpa.vendor.Database;
|
||||||
|
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
|
||||||
|
@EnableJpaRepositories(basePackages = "spring/jpa")
|
||||||
|
public class PersistenceConfig {
|
||||||
|
|
||||||
|
@Bean(name = "transactionManager")
|
||||||
|
public PlatformTransactionManager dbTransactionManager() {
|
||||||
|
JpaTransactionManager transactionManager = new JpaTransactionManager();
|
||||||
|
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
|
||||||
|
return transactionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
|
|
||||||
|
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
|
||||||
|
vendorAdapter.setDatabase(Database.HSQL);
|
||||||
|
vendorAdapter.setGenerateDdl(true);
|
||||||
|
|
||||||
|
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
|
||||||
|
em.setDataSource(dataSource());
|
||||||
|
em.setPackagesToScan("spring/jpa");
|
||||||
|
em.setJpaVendorAdapter(vendorAdapter);
|
||||||
|
em.setJpaProperties(additionalProperties());
|
||||||
|
|
||||||
|
return em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DataSource dataSource() {
|
||||||
|
DriverManagerDataSource dataSource = new DriverManagerDataSource();
|
||||||
|
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
|
||||||
|
dataSource.setUrl("jdbc:hsqldb:mem:test");
|
||||||
|
dataSource.setUsername("sa");
|
||||||
|
dataSource.setPassword("1");
|
||||||
|
return dataSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Properties additionalProperties() {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.setProperty("hibernate.show_sql", "true");
|
||||||
|
properties.setProperty("hibernate.hbm2ddl.auto", "create");
|
||||||
|
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,6 @@ import org.hibernate.SessionFactory
|
||||||
import org.hibernate.cfg.Configuration
|
import org.hibernate.cfg.Configuration
|
||||||
import org.hibernate.exception.SQLGrammarException
|
import org.hibernate.exception.SQLGrammarException
|
||||||
import org.hibernate.procedure.ProcedureCall
|
import org.hibernate.procedure.ProcedureCall
|
||||||
import org.junit.jupiter.api.Assumptions
|
|
||||||
import spock.lang.Shared
|
import spock.lang.Shared
|
||||||
|
|
||||||
import javax.persistence.ParameterMode
|
import javax.persistence.ParameterMode
|
||||||
|
@ -57,16 +56,6 @@ class ProcedureCallTest extends AgentInstrumentationSpecification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def callProcedure(ProcedureCall call) {
|
|
||||||
try {
|
|
||||||
call.getOutputs()
|
|
||||||
} catch (Exception exception) {
|
|
||||||
// ignore failures on hibernate 6 where this functionality has not been implemented yet
|
|
||||||
Assumptions.assumeFalse("org.hibernate.NotYetImplementedFor6Exception" == exception.getClass().getName())
|
|
||||||
throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def "test ProcedureCall"() {
|
def "test ProcedureCall"() {
|
||||||
setup:
|
setup:
|
||||||
|
|
||||||
|
@ -75,7 +64,7 @@ class ProcedureCallTest extends AgentInstrumentationSpecification {
|
||||||
session.beginTransaction()
|
session.beginTransaction()
|
||||||
|
|
||||||
ProcedureCall call = session.createStoredProcedureCall("TEST_PROC")
|
ProcedureCall call = session.createStoredProcedureCall("TEST_PROC")
|
||||||
callProcedure(call)
|
call.getOutputs()
|
||||||
|
|
||||||
session.getTransaction().commit()
|
session.getTransaction().commit()
|
||||||
session.close()
|
session.close()
|
||||||
|
@ -137,10 +126,9 @@ class ProcedureCallTest extends AgentInstrumentationSpecification {
|
||||||
|
|
||||||
ProcedureCall call = session.createStoredProcedureCall("TEST_PROC")
|
ProcedureCall call = session.createStoredProcedureCall("TEST_PROC")
|
||||||
def parameterRegistration = call.registerParameter("nonexistent", Long, ParameterMode.IN)
|
def parameterRegistration = call.registerParameter("nonexistent", Long, ParameterMode.IN)
|
||||||
Assumptions.assumeTrue(parameterRegistration.metaClass.getMetaMethod("bindValue", Object) != null)
|
|
||||||
parameterRegistration.bindValue(420L)
|
parameterRegistration.bindValue(420L)
|
||||||
try {
|
try {
|
||||||
callProcedure(call)
|
call.getOutputs()
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// We expected this.
|
// We expected this.
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,20 +7,6 @@
|
||||||
|
|
||||||
<session-factory>
|
<session-factory>
|
||||||
|
|
||||||
<!--<!– Use in-memory DB for testing –>-->
|
|
||||||
<!--<property name="connection.driver_class">org.apache.derby.jdbc.EmbeddedDriver</property>-->
|
|
||||||
<!--<property name="connection.url">jdbc:derby:test;create=true</property>-->
|
|
||||||
<!--<property name="connection.username">root</property>-->
|
|
||||||
<!--<property name="connection.password">root</property>-->
|
|
||||||
<!--<property name="dialect">org.hibernate.dialect.DerbyTenSevenDialect</property>-->
|
|
||||||
|
|
||||||
<!--<property name="connection.pool_size">3</property>-->
|
|
||||||
<!--<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>-->
|
|
||||||
<!--<property name="show_sql">true</property>-->
|
|
||||||
|
|
||||||
<!--<!– Reset the DB each test –>-->
|
|
||||||
<!--<property name="hbm2ddl.auto">create</property>-->
|
|
||||||
|
|
||||||
<property name="hibernate.archive.autodetection">class,hbm</property>
|
<property name="hibernate.archive.autodetection">class,hbm</property>
|
||||||
<property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
|
<property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
|
||||||
<property name="hibernate.show_sql">true</property>
|
<property name="hibernate.show_sql">true</property>
|
||||||
|
|
|
@ -232,6 +232,8 @@ hideFromDependabot(":instrumentation:guava-10.0:library")
|
||||||
hideFromDependabot(":instrumentation:gwt-2.0:javaagent")
|
hideFromDependabot(":instrumentation:gwt-2.0:javaagent")
|
||||||
hideFromDependabot(":instrumentation:hibernate:hibernate-3.3:javaagent")
|
hideFromDependabot(":instrumentation:hibernate:hibernate-3.3:javaagent")
|
||||||
hideFromDependabot(":instrumentation:hibernate:hibernate-4.0:javaagent")
|
hideFromDependabot(":instrumentation:hibernate:hibernate-4.0:javaagent")
|
||||||
|
hideFromDependabot(":instrumentation:hibernate:hibernate-6.0:javaagent")
|
||||||
|
hideFromDependabot(":instrumentation:hibernate:hibernate-6.0:spring-testing")
|
||||||
hideFromDependabot(":instrumentation:hibernate:hibernate-common:javaagent")
|
hideFromDependabot(":instrumentation:hibernate:hibernate-common:javaagent")
|
||||||
hideFromDependabot(":instrumentation:hibernate:hibernate-procedure-call-4.3:javaagent")
|
hideFromDependabot(":instrumentation:hibernate:hibernate-procedure-call-4.3:javaagent")
|
||||||
hideFromDependabot(":instrumentation:hikaricp-3.0:javaagent")
|
hideFromDependabot(":instrumentation:hikaricp-3.0:javaagent")
|
||||||
|
|
Loading…
Reference in New Issue