observer) {
LOCK.writeLock().lock();
try {
- GLOBAL_OPERATORS.remove(consumer);
+ GLOBAL_OPERATORS.remove(observer);
} finally {
LOCK.writeLock().unlock();
}
}
/**
- * Create a new interceptor that will be called for SQL statements processed by the current thread. The interceptor
- * must be closed when it is no longer needed. It is recommended to use a try-with-resources block to ensure that
- * the interceptor is closed.
+ * Create a new scoped interceptor that applies an operator to SQL statements processed by the current thread and
+ * any child threads.
*
- * @param operator the operator to call for each SQL statement.
- * @return the interceptor.
+ * This interceptor is scoped to the current thread context and propagates only to its child threads.
+ * It is isolated from sibling threads, meaning changes made to the interceptor set will not affect other threads
+ * that share the same parent scope.
+ *
+ * @param operator the operator to apply to each SQL statement.
+ * @return a {@link Carrier} that binds the interceptor to the current thread's scoped context.
*/
- public static SqlInterceptor create(@Nonnull UnaryOperator operator) {
- class SqlInterceptorImpl implements SqlInterceptor, UnaryOperator {
- @Override
- public Sql apply(Sql sql) {
- return operator.apply(sql);
- }
-
- @Override
- public void close() {
- LOCAL_OPERATORS.get().remove(this);
- }
- }
- var interceptor = new SqlInterceptorImpl();
- LOCAL_OPERATORS.get().addFirst(interceptor);
- return MonitoredResource.wrap(interceptor);
+ public static Carrier intercept(@Nonnull UnaryOperator operator) {
+ var operators = synchronizedList(new ArrayList<>(LOCAL_OPERATORS.orElse(List.of())));
+ operators.addFirst(operator);
+ return ScopedValue.where(LOCAL_OPERATORS, operators);
}
/**
- * Create a new interceptor that will be called for SQL statements processed by the current thread. The interceptor
- * must be closed when it is no longer needed. It is recommended to use a try-with-resources block to ensure that
- * the interceptor is closed.
+ * Create a new scoped interceptor that applies an operator to SQL statements processed by the current thread and
+ * any child threads.
+ *
+ * This interceptor is scoped to the current thread context and propagates only to its child threads.
+ * It is isolated from sibling threads, meaning changes made to the interceptor set will not affect other threads
+ * that share the same parent scope.
*
- * @param consumer the consumer to call for each SQL statement.
- * @return the interceptor.
+ * @param observer the observer to invoke with each SQL statement.
+ * @return a {@link Carrier} that binds the interceptor to the current thread's scoped context.
*/
- public static SqlInterceptor create(@Nonnull Consumer consumer) {
- class SqlInterceptorImpl implements SqlInterceptor, UnaryOperator {
- @Override
- public Sql apply(Sql sql) {
- consumer.accept(sql);
- return sql;
- }
-
- @Override
- public void close() {
- LOCAL_OPERATORS.get().remove(this);
- }
- }
- var interceptor = new SqlInterceptorImpl();
- LOCAL_OPERATORS.get().addFirst(interceptor);
- return MonitoredResource.wrap(interceptor);
+ public static Carrier intercept(@Nonnull Consumer observer) {
+ var operators = synchronizedList(new ArrayList<>(LOCAL_OPERATORS.orElse(List.of())));
+ operators.addFirst(sql -> {
+ observer.accept(sql);
+ return sql;
+ });
+ return ScopedValue.where(LOCAL_OPERATORS, operators);
}
/**
* Intercepts the specified SQL statement by calling all globally and locally registered interceptors.
*
* @param sql the SQL statement to intercept.
+ * @return the adjusted SQL statement.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
- static void intercept(@Nonnull Sql sql) {
+ static Sql intercept(@Nonnull Sql sql) {
Sql adjusted = sql;
- // The local operators are not protected by a lock, but that is fine since they are thread-local. They should
- // however not modify the local operators from the accept method.
- for (var operator : LOCAL_OPERATORS.get()) {
- adjusted = operator.apply(adjusted);
+ // The local operators are not protected by a lock, but that is fine since they are locally scoped. However,
+ // they must not modify the local operators from the accept/apply method.
+ try {
+ for (var operator : LOCAL_OPERATORS.orElse(List.of())) {
+ adjusted = operator.apply(adjusted);
+ }
+ } catch (ConcurrentModificationException e) {
+ throw new PersistenceException("Registering interceptors from within their execution scope is not allowed.");
}
LOCK.readLock().lock();
try {
@@ -179,5 +173,6 @@ static void intercept(@Nonnull Sql sql) {
} finally {
LOCK.readLock().unlock();
}
+ return adjusted;
}
}
diff --git a/storm/src/main/java/st/orm/template/impl/SqlTemplateImpl.java b/storm/src/main/java/st/orm/template/impl/SqlTemplateImpl.java
index 537446ba0..d921a2db5 100644
--- a/storm/src/main/java/st/orm/template/impl/SqlTemplateImpl.java
+++ b/storm/src/main/java/st/orm/template/impl/SqlTemplateImpl.java
@@ -1062,7 +1062,7 @@ interface DelayedResult {
}
if (!subquery) {
// Don't intercept subquery calls.
- SqlInterceptorManager.intercept(generated);
+ generated = SqlInterceptorManager.intercept(generated);
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(STR."Generated SQL:\n\{generated.statement()}");
} else if (LOGGER.isLoggable(Level.FINEST)) {
diff --git a/storm/src/test/java/st/orm/RepositoryPreparedStatementIntegrationTest.java b/storm/src/test/java/st/orm/RepositoryPreparedStatementIntegrationTest.java
index 1c013c492..ef8c151fd 100644
--- a/storm/src/test/java/st/orm/RepositoryPreparedStatementIntegrationTest.java
+++ b/storm/src/test/java/st/orm/RepositoryPreparedStatementIntegrationTest.java
@@ -35,7 +35,6 @@
import st.orm.repository.PetRepository;
import st.orm.template.Metamodel;
import st.orm.template.Sql;
-import st.orm.template.SqlInterceptor;
import st.orm.template.SqlTemplate.PositionalParameter;
import st.orm.template.SqlTemplateException;
import st.orm.template.impl.DefaultJoinType;
@@ -69,7 +68,7 @@
import static st.orm.template.Operator.IS_NULL;
import static st.orm.template.ResolveScope.INNER;
import static st.orm.template.ResolveScope.OUTER;
-import static st.orm.template.SqlInterceptor.consume;
+import static st.orm.template.SqlInterceptor.observe;
import static st.orm.template.TemplateFunction.template;
@ExtendWith(SpringExtension.class)
@@ -530,27 +529,27 @@ public void testSelectWithTwoPetsWithMultipleParameters() {
var orm = ORM(dataSource);
var owner = orm.entity(Owner.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
var visits = orm.entity(VisitWithTwoPets.class)
.select()
.where(it -> it.where(VisitWithTwoPets_.pet1.owner, EQUALS, owner)
.or(it.where(VisitWithTwoPets_.pet2.owner, EQUALS, owner))).getResultList();
assertEquals(2, sql.getPlain().parameters().size());
assertEquals(2, visits.size());
- }
+ });
}
@Test
public void testSelectWithTwoPetsWithMultipleParametersTemplate() {
var owner = ORM(dataSource).entity(Owner.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
var visits = ORM(dataSource).entity(VisitWithTwoPets.class)
.select()
.where(it -> it.where(RAW."\{VisitWithTwoPets_.pet1.owner} = \{owner.id()} OR \{VisitWithTwoPets_.pet2.owner} = \{owner.id()}")).getResultList();
assertEquals(2, sql.getPlain().parameters().size());
assertEquals(2, visits.size());
- }
+ });
}
@Test
@@ -565,31 +564,31 @@ public void testPetOwnerRecursion() {
public void testSelectWithTwoPetsOneRefWithoutPath() throws Exception {
var owner = ORM(dataSource).entity(Owner.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
var visits = ORM(dataSource).entity(VisitWithTwoPetsOneRef.class).select().where(it -> it.whereAny(owner)).getResultList();
assertEquals(1, sql.getPlain().parameters().size());
assertEquals(2, visits.size());
- }
+ });
}
@Test
public void testSelectWithTwoPetsOneRefWithoutPathTemplate() {
var owner = ORM(dataSource).entity(Owner.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
var visits = ORM(dataSource).entity(VisitWithTwoPetsOneRef.class)
.select()
.where(it -> it.where(RAW."\{PetOwnerRef.class}.owner_id = \{owner.id()}")).getResultList();
assertEquals(1, sql.getPlain().parameters().size());
assertEquals(2, visits.size());
- }
+ });
}
@Test
public void testSelectWithTwoPetsOneRefWithRootPathTemplateMetamodel() {
var owner = ORM(dataSource).entity(Owner.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
var list = ORM(dataSource).entity(VisitWithTwoPetsOneRef.class)
.select()
.where(it -> it.where(RAW."\{PetOwnerRef_.owner} = \{owner.id()}")).getResultList();
@@ -598,7 +597,7 @@ public void testSelectWithTwoPetsOneRefWithRootPathTemplateMetamodel() {
assertEquals(owner.id(), list.getFirst().pet1().owner().id());
//noinspection DataFlowIssue
assertEquals(owner.id(), list.getLast().pet1().owner().id());
- }
+ });
}
@Test
@@ -606,11 +605,11 @@ public void testSelectWithTwoPetsOneRefWithInvalidPathTemplateMetamodel() {
var e = assertThrows(PersistenceException.class, () -> {
var owner = ORM(dataSource).entity(Owner.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
ORM(dataSource).entity(VisitWithTwoPetsOneRef.class)
.select()
.where(it -> it.where(RAW."\{Pet_.owner} = \{owner.id()}")).getResultList();
- }
+ });
});
assertInstanceOf(SqlTemplateException.class, e.getCause());
}
@@ -620,11 +619,11 @@ public void testSelectWithTwoPetsOneRefWithInvalidPathMetamodel() {
var e = assertThrows(PersistenceException.class, () -> {
var owner = ORM(dataSource).entity(Owner.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
ORM(dataSource).entity(VisitWithTwoPetsOneRef.class)
.select()
.where(it -> it.whereAny(PetOwnerRef_.owner, owner)).getResultList();
- }
+ });
});
assertInstanceOf(SqlTemplateException.class, e.getCause());
}
@@ -633,7 +632,7 @@ public void testSelectWithTwoPetsOneRefWithInvalidPathMetamodel() {
public void testSelectWithTwoPetsOneRefWithRootPathMetamodelTemplate() {
var owner = ORM(dataSource).entity(Owner.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
var list = ORM(dataSource).entity(VisitWithTwoPetsOneRef.class)
.select()
.where(RAW."\{PetOwnerRef_.owner} = \{owner.id()}").getResultList();
@@ -642,7 +641,7 @@ public void testSelectWithTwoPetsOneRefWithRootPathMetamodelTemplate() {
assertEquals(owner.id(), list.getFirst().pet1().owner().id());
//noinspection DataFlowIssue
assertEquals(owner.id(), list.getLast().pet1().owner().id());
- }
+ });
}
@Test
@@ -650,11 +649,11 @@ public void testSelectWithTwoPetsOneRefWithInvalidPathMetamodelTemplate() {
var e = assertThrows(PersistenceException.class, () -> {
var owner = ORM(dataSource).entity(Owner.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
ORM(dataSource).entity(VisitWithTwoPetsOneRef.class)
.select()
.where(RAW."\{Pet_.owner} = \{owner.id()}").getResultList();
- }
+ });
});
assertInstanceOf(SqlTemplateException.class, e.getCause());
}
@@ -663,24 +662,24 @@ public void testSelectWithTwoPetsOneRefWithInvalidPathMetamodelTemplate() {
public void testSelectWithTwoPetsOneRefWithPath() {
var owner = ORM(dataSource).entity(Owner.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
var visits = ORM(dataSource).entity(VisitWithTwoPetsOneRef.class).select().where(VisitWithTwoPetsOneRef_.pet1.owner, EQUALS, owner).getResultList();
assertEquals(1, sql.getPlain().parameters().size());
assertEquals(2, visits.size());
- }
+ });
}
@Test
public void testSelectWithTwoPetsOneRefWithPathTemplate() {
var owner = ORM(dataSource).entity(Owner.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
var visits = ORM(dataSource).entity(VisitWithTwoPetsOneRef.class)
.select()
.where(it -> it.where(RAW."\{VisitWithTwoPetsOneRef_.pet1.owner} = \{owner.id()}")).getResultList();
assertEquals(1, sql.getPlain().parameters().size());
assertEquals(2, visits.size());
- }
+ });
}
@Test
@@ -706,11 +705,11 @@ public void testSelectWithTwoPetsOneRefPetWithoutPathTemplate() {
public void testSelectWithTwoPetsOneRefPetWithPath() {
var pet = ORM(dataSource).entity(PetOwnerRef.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
var visits = ORM(dataSource).entity(VisitWithTwoPetsOneRef.class).select().where(VisitWithTwoPetsOneRef_.pet1, EQUALS, pet).getResultList();
assertEquals(1, sql.getPlain().parameters().size());
assertEquals(2, visits.size());
- }
+ });
}
@Test
@@ -718,36 +717,36 @@ public void testSelectWithTwoPetsOneRefPetWithPathTemplateMetamodel() throws Exc
var ORM = ORM(dataSource);
var pet = ORM.entity(PetOwnerRef.class).select().append(RAW."LIMIT 1").getSingleResult();
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
var visits = ORM.entity(VisitWithTwoPetsOneRef.class)
.select()
.where(it -> it.where(RAW."\{VisitWithTwoPetsOneRef_.pet1} = \{pet.id()}")).getResultList();
assertEquals(1, sql.getPlain().parameters().size());
assertEquals(2, visits.size());
- }
+ });
}
@Test
public void testSelectWithTwoPetsOneRefOtherPetWithPath() {
var pet = ORM(dataSource).entity(PetOwnerRef.class).getById(1);
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
var visits = ORM(dataSource).entity(VisitWithTwoPetsOneRef.class).select().where(VisitWithTwoPetsOneRef_.pet2, EQUALS, pet).getResultList();
assertEquals(1, sql.getPlain().parameters().size());
assertEquals(2, visits.size());
- }
+ });
}
@Test
public void testSelectWithTwoPetsOneRefOtherPetWithPathTemplateMetamodel() {
AtomicReference sql = new AtomicReference<>();
- try (var _ = consume(sql::setPlain)) {
+ observe(sql::setPlain, () -> {
var visits = ORM(dataSource).entity(VisitWithTwoPetsOneRef.class)
.select()
.where(it -> it.where(RAW."\{VisitWithTwoPetsOneRef_.pet2} = \{1}")).getResultList();
assertEquals(1, sql.getPlain().parameters().size());
assertEquals(2, visits.size());
- }
+ });
}
@Test
@@ -1051,9 +1050,9 @@ public void selectOwnerForUpdate() {
WHERE o.id = ?
FOR UPDATE""";
var repo = ORM(dataSource).entity(Owner.class);
- try (var _ = SqlInterceptor.consume(sql -> assertEquals(expectedSql, sql.statement()))) {
+ observe(sql -> assertEquals(expectedSql, sql.statement()), () -> {
repo.select().forUpdate().where(1).getSingleResult();
- }
+ });
}
@Test
@@ -1182,7 +1181,7 @@ public void testWherePredicateSubqueryParameters() {
FROM owner o1
WHERE o1.id = ?
) AND 3 = ?""";
- try (var _ = SqlInterceptor.consume(sql -> {
+ observe(sql -> {
assertEquals(expectedSql, sql.statement());
assertTrue(sql.parameters().get(0) instanceof PositionalParameter(int position, Object dbValue)
&& position == 1 && Integer.valueOf(1).equals(dbValue));
@@ -1190,16 +1189,15 @@ public void testWherePredicateSubqueryParameters() {
&& position == 2 && Integer.valueOf(2).equals(dbValue));
assertTrue(sql.parameters().get(2) instanceof PositionalParameter(int position, Object dbValue)
&& position == 3 && Integer.valueOf(3).equals(dbValue));
- })) {
+ }, () -> {
var orm = ORM(dataSource);
orm.entity(Owner.class)
.select()
.where(it -> it.where(RAW."\{alias(Owner.class, INNER)}.id = \{1}")
.and(it.where(RAW."EXISTS (\{it.subquery(Owner.class).where(RAW."\{alias(Owner.class, INNER)}.id = \{2}")})"))
- .and(it.where(RAW."3 = \{3}"))
- )
+ .and(it.where(RAW."3 = \{3}")))
.getResultList();
- }
+ });
}
@Test
@@ -1215,7 +1213,7 @@ AND EXISTS (
WHERE o1.id = ?
)
AND 3 = ?""";
- try (var _ = SqlInterceptor.consume(sql -> {
+ observe(sql -> {
assertEquals(expectedSql, sql.statement());
assertTrue(sql.parameters().get(0) instanceof PositionalParameter(int position, Object dbValue)
&& position == 1 && Integer.valueOf(1).equals(dbValue));
@@ -1223,7 +1221,7 @@ AND EXISTS (
&& position == 2 && Integer.valueOf(2).equals(dbValue));
assertTrue(sql.parameters().get(2) instanceof PositionalParameter(int position, Object dbValue)
&& position == 3 && Integer.valueOf(3).equals(dbValue));
- })) {
+ }, () -> {
var orm = ORM(dataSource);
orm.entity(Owner.class)
.select()
@@ -1231,7 +1229,7 @@ AND EXISTS (
.append(RAW."AND EXISTS (\{orm.subquery(Owner.class).where(RAW."\{alias(Owner.class, INNER)}.id = \{2}")})")
.append(RAW."AND 3 = \{3}")
.getResultList();
- }
+ });
}
/**
diff --git a/storm/src/test/java/st/orm/TemplatePreparedStatementIntegrationTest.java b/storm/src/test/java/st/orm/TemplatePreparedStatementIntegrationTest.java
index 583d0ed20..1d887d775 100644
--- a/storm/src/test/java/st/orm/TemplatePreparedStatementIntegrationTest.java
+++ b/storm/src/test/java/st/orm/TemplatePreparedStatementIntegrationTest.java
@@ -61,7 +61,7 @@
import static st.orm.template.Operator.EQUALS;
import static st.orm.template.ResolveScope.INNER;
import static st.orm.template.ResolveScope.OUTER;
-import static st.orm.template.SqlInterceptor.consume;
+import static st.orm.template.SqlInterceptor.observe;
import static st.orm.template.TemplateFunction.template;
@ExtendWith(SpringExtension.class)
@@ -350,15 +350,16 @@ public void testDelete() {
DELETE x
FROM visit x
WHERE x.id = ?""";
- try (var _ = consume(sql -> assertEquals(expectedSql, sql.statement()));
- var _ = ORM(dataSource).query(RAW."""
- DELETE \{Visit.class}
- FROM \{from(Visit.class, "x", true)}
- WHERE \{where(Visit.builder().id(1).build())}""").prepare()) {
- Assertions.fail("Should not reach here");
- } catch (PersistenceException _) {
- // Not supported in H2.
- }
+ observe(sql -> assertEquals(expectedSql, sql.statement()), () -> {
+ try (var _ = ORM(dataSource).query(RAW."""
+ DELETE \{Visit.class}
+ FROM \{from(Visit.class, "x", true)}
+ WHERE \{where(Visit.builder().id(1).build())}""").prepare()) {
+ Assertions.fail("Should not reach here");
+ } catch (PersistenceException _) {
+ // Not supported in H2.
+ }
+ });
}
@Test
@@ -376,15 +377,16 @@ public void testDeleteWithType() {
DELETE v
FROM visit v
WHERE v.id = ?""";
- try (var _ = consume(sql -> assertEquals(expectedSql, sql.statement()));
- var _ = ORM(dataSource).query(RAW."""
- DELETE \{Visit.class}
- FROM \{Visit.class}
- WHERE \{where(Visit.builder().id(1).build())}""").prepare()) {
- Assertions.fail("Should not reach here");
- } catch (PersistenceException _) {
- // Delete statements with alias are supported by many databases, but not by H2.
- }
+ observe(sql -> assertEquals(expectedSql, sql.statement()), () -> {
+ try (var _ = ORM(dataSource).query(RAW."""
+ DELETE \{Visit.class}
+ FROM \{Visit.class}
+ WHERE \{where(Visit.builder().id(1).build())}""").prepare()) {
+ Assertions.fail("Should not reach here");
+ } catch (PersistenceException _) {
+ // Delete statements with alias are supported by many databases, but not by H2.
+ }
+ });
}
@Test
@@ -406,15 +408,16 @@ public void testDeleteWithTypeAndAlias() {
DELETE f
FROM visit f
WHERE f.id = ?""";
- try (var _ = consume(sql -> assertEquals(expectedSql, sql.statement()));
- var query = ORM(dataSource).query(RAW."""
- DELETE \{Visit.class}
- FROM \{from(Visit.class, "f", false)}
- WHERE \{where(Visit.builder().id(1).build())}""").prepare()) {
- Assertions.fail("Should not reach here");
- } catch (PersistenceException _) {
- // Delete statements with alias are supported by many databases, but not by H2.
- }
+ observe(sql -> assertEquals(expectedSql, sql.statement()), () -> {
+ try (var _ = ORM(dataSource).query(RAW."""
+ DELETE \{Visit.class}
+ FROM \{from(Visit.class, "f", false)}
+ WHERE \{where(Visit.builder().id(1).build())}""").prepare()) {
+ Assertions.fail("Should not reach here");
+ } catch (PersistenceException _) {
+ // Delete statements with alias are supported by many databases, but not by H2.
+ }
+ });
}
@Test
@@ -424,15 +427,16 @@ public void testDeleteWithAutoJoin() {
FROM visit v
INNER JOIN pet p ON v.pet_id = p.id
WHERE p.owner_id = ?""";
- try (var _ = consume(sql -> assertEquals(expectedSql, sql.statement()));
- var _ = ORM(dataSource).query(RAW."""
- DELETE \{Visit.class}
- FROM \{from(Visit.class, true)}
- WHERE \{where(Owner.builder().id(1).build())}""").prepare()) {
- Assertions.fail("Should not reach here");
- } catch (PersistenceException _) {
- // Not supported in H2.
- }
+ observe(sql -> assertEquals(expectedSql, sql.statement()), () -> {
+ try (var _ = ORM(dataSource).query(RAW."""
+ DELETE \{Visit.class}
+ FROM \{from(Visit.class, true)}
+ WHERE \{where(Owner.builder().id(1).build())}""").prepare()) {
+ Assertions.fail("Should not reach here");
+ } catch (PersistenceException _) {
+ // Not supported in H2.
+ }
+ });
}
@Test
@@ -836,19 +840,18 @@ public void testUpdateSetWhereWithAliasClash() {
INNER JOIN owner o ON p.owner_id = o.id
INNER JOIN city c ON o.city_id = c.id
WHERE p.id = ?""";
- try (var _ = SqlInterceptor.consume(sql -> {
- assertEquals(expectedSql, sql.statement());
+ observe(sql -> assertEquals(expectedSql, sql.statement()), () -> {
+ try (var query = ORM(dataSource).query(RAW."""
+ SELECT \{Pet.class}
+ FROM \{Pet.class}
+ INNER JOIN \{table(Owner.class, "o")} ON \{Pet.class}.owner_id = o.id
+ INNER JOIN \{table(City.class, "c")} ON o.city_id = c.id
+ WHERE \{where(Pet.builder().id(1).build())}""").prepare()){
+ var result = query.getSingleResult(Pet.class);
+ assertEquals("Leo", result.name());
+ assertEquals(0, result.type().id());
+ }
});
- var query = ORM(dataSource).query(RAW."""
- SELECT \{Pet.class}
- FROM \{Pet.class}
- INNER JOIN \{table(Owner.class, "o")} ON \{Pet.class}.owner_id = o.id
- INNER JOIN \{table(City.class, "c")} ON o.city_id = c.id
- WHERE \{where(Pet.builder().id(1).build())}""").prepare()) {
- var result = query.getSingleResult(Pet.class);
- assertEquals("Leo", result.name());
- assertEquals(0, result.type().id());
- }
}
@Test