diff --git a/README.adoc b/README.adoc index b7ab70c77..fe7b07138 100644 --- a/README.adoc +++ b/README.adoc @@ -45,7 +45,7 @@ Maven:: st.orm storm - 1.3.2 + 1.3.3 compile ---- @@ -53,7 +53,7 @@ Gradle:: + [source,groovy] ---- -implementation 'st.orm:storm:1.3.2' +implementation 'st.orm:storm:1.3.3' ---- ==== @@ -846,7 +846,7 @@ Maven:: st.orm storm-oracle - 1.3.2 + 1.3.3 runtime ---- @@ -854,7 +854,7 @@ Gradle:: + [source,groovy] ---- -runtimeOnly 'st.orm:storm-oracle:1.3.2' +runtimeOnly 'st.orm:storm-oracle:1.3.3' ---- ==== @@ -876,7 +876,7 @@ Maven:: st.orm storm-metamodel-processor - 1.3.2 + 1.3.3 provided ---- @@ -884,7 +884,7 @@ Gradle:: + [source,groovy] ---- -annotationProcessor 'st.orm:storm-metamodel-processor:1.3.2' +annotationProcessor 'st.orm:storm-metamodel-processor:1.3.3' ---- ==== @@ -952,7 +952,7 @@ Maven:: st.orm storm-json - 1.3.2 + 1.3.3 compile ---- @@ -960,7 +960,7 @@ Gradle:: + [source,groovy] ---- -implementation 'st.orm:storm-json:1.3.2' +implementation 'st.orm:storm-json:1.3.3' ---- ==== @@ -1039,7 +1039,7 @@ Maven:: st.orm storm-spring - 1.3.2 + 1.3.3 compile ---- @@ -1047,7 +1047,7 @@ Gradle:: + [source,groovy] ---- -implementation 'st.orm:storm-spring:1.3.2' +implementation 'st.orm:storm-spring:1.3.3' ---- ==== diff --git a/pom.xml b/pom.xml index 7ecbb99cc..210986f0e 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ st.orm storm-framework - 1.3.2 + 1.3.3 pom Storm Framework A SQL Template and ORM framework, focusing on modernizing and simplifying database programming. diff --git a/storm-json/pom.xml b/storm-json/pom.xml index 17682de26..97b34d607 100644 --- a/storm-json/pom.xml +++ b/storm-json/pom.xml @@ -6,7 +6,7 @@ st.orm storm-framework - 1.3.2 + 1.3.3 ../pom.xml storm-json diff --git a/storm-json/src/test/java/st/orm/json/JsonIntegrationTest.java b/storm-json/src/test/java/st/orm/json/JsonIntegrationTest.java index a7a6449b9..3d5319280 100644 --- a/storm-json/src/test/java/st/orm/json/JsonIntegrationTest.java +++ b/storm-json/src/test/java/st/orm/json/JsonIntegrationTest.java @@ -34,7 +34,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static st.orm.Templates.ORM; import static st.orm.Templates.alias; -import static st.orm.template.SqlInterceptor.consume; +import static st.orm.template.SqlInterceptor.observe; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = IntegrationConfig.class) @@ -175,16 +175,18 @@ SELECT v.id, v.first_name, v.last_name, JSON_OBJECTAGG(s.id, s.name) INNER JOIN vet_specialty vs ON vs.vet_id = v.id INNER JOIN specialty s ON s.id = vs.specialty_id GROUP BY v.id"""; - try (var _ = consume(sql -> assertEquals(expectedSql, sql.statement()))) { - ORM(dataSource).selectFrom(Vet.class, SpecialtyNamesByVet.class, RAW.""" - \{Vet.class}, JSON_OBJECTAGG(\{Specialty.class})""") - .innerJoin(VetSpecialty.class).on(Vet.class) - .innerJoin(Specialty.class).on(VetSpecialty.class) - .append(RAW."GROUP BY \{Vet.class}.id") - .getResultList(); - } catch (PersistenceException _) { - // H2 Does not support JSON_OBJECTAGG. We only check the expected SQL. - } + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { + try { + ORM(dataSource).selectFrom(Vet.class, SpecialtyNamesByVet.class, RAW.""" + \{Vet.class}, JSON_OBJECTAGG(\{Specialty.class})""") + .innerJoin(VetSpecialty.class).on(Vet.class) + .innerJoin(Specialty.class).on(VetSpecialty.class) + .append(RAW."GROUP BY \{Vet.class}.id") + .getResultList(); + } catch (PersistenceException _) { + // H2 Does not support JSON_OBJECTAGG. We only check the expected SQL. + } + }); } // No need to specify the sub types here, as we're automatically registering the implementations of the sealed interface. diff --git a/storm-kotlin/pom.xml b/storm-kotlin/pom.xml index e0c5c48ea..4bc1dbf42 100644 --- a/storm-kotlin/pom.xml +++ b/storm-kotlin/pom.xml @@ -6,7 +6,7 @@ st.orm storm-framework - 1.3.2 + 1.3.3 ../pom.xml storm-kotlin diff --git a/storm-kotlin/src/main/java/st/orm/kotlin/template/impl/KORMTemplateImpl.java b/storm-kotlin/src/main/java/st/orm/kotlin/template/impl/KORMTemplateImpl.java index 6e7a611e6..30f81c7c0 100644 --- a/storm-kotlin/src/main/java/st/orm/kotlin/template/impl/KORMTemplateImpl.java +++ b/storm-kotlin/src/main/java/st/orm/kotlin/template/impl/KORMTemplateImpl.java @@ -35,7 +35,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.sql.SQLException; import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -44,7 +43,7 @@ import static java.lang.System.identityHashCode; import static java.lang.reflect.Proxy.newProxyInstance; import static java.util.Optional.empty; -import static st.orm.template.SqlInterceptor.consume; +import static st.orm.template.SqlInterceptor.observeThrowing; public final class KORMTemplateImpl extends KQueryTemplateImpl implements KORMTemplate { private final static ORMReflection REFLECTION = Providers.getORMReflection(); @@ -183,18 +182,20 @@ private static void getAllInterfaces(Class clazz, Set> interfacesFou private T wrapRepository(@Nonnull T repository) { return (T) newProxyInstance(repository.getClass().getClassLoader(), getAllInterfaces(repository.getClass()).toArray(new Class[0]), (_, method, args) -> { var lastSql = new AtomicReference(); - try (var _ = consume(lastSql::setPlain)) { - try { - return method.invoke(repository, args); - } catch (Exception | Error e) { - throw e; - } catch (Throwable e) { - throw new PersistenceException(e); - } + try { + return observeThrowing(lastSql::setPlain, () -> { + try { + return method.invoke(repository, args); + } catch (Exception | Error e) { + throw e; + } catch (Throwable e) { + throw new PersistenceException(e); + } + }); } catch (InvocationTargetException e) { try { throw e.getTargetException(); - } catch (SQLException | PersistenceException ex) { + } catch (Exception ex) { Sql sql = lastSql.getPlain(); if (sql != null && ex.getSuppressed().length == 0) { ex.addSuppressed(new SqlTemplateException(STR.""" diff --git a/storm-mariadb/pom.xml b/storm-mariadb/pom.xml index 51dde9501..d4f7fc92e 100644 --- a/storm-mariadb/pom.xml +++ b/storm-mariadb/pom.xml @@ -6,7 +6,7 @@ st.orm storm-framework - 1.3.2 + 1.3.3 ../pom.xml storm-mariadb diff --git a/storm-mariadb/src/test/java/st/orm/spi/mariadb/MariaDBEntityRepositoryTest.java b/storm-mariadb/src/test/java/st/orm/spi/mariadb/MariaDBEntityRepositoryTest.java index 9093608b1..a9c283358 100644 --- a/storm-mariadb/src/test/java/st/orm/spi/mariadb/MariaDBEntityRepositoryTest.java +++ b/storm-mariadb/src/test/java/st/orm/spi/mariadb/MariaDBEntityRepositoryTest.java @@ -36,6 +36,7 @@ import static org.springframework.test.util.AssertionErrors.assertNull; import static st.orm.template.Operator.EQUALS; import static st.orm.template.Operator.GREATER_THAN_OR_EQUAL; +import static st.orm.template.SqlInterceptor.observe; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = IntegrationConfig.class) @@ -93,9 +94,7 @@ public void testSelectLimit() { FROM owner o LIMIT 2"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().limit(2).getResultList(); assertEquals(2, entities.size()); assertEquals("Betty", entities.getFirst().firstName()); @@ -110,7 +109,7 @@ public void testSelectLimit() { assertEquals("Madison", entities.getLast().address().city()); assertEquals("6085551023", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -121,9 +120,9 @@ public void testSelectLimitOffset() { ORDER BY o.id LIMIT 2 OFFSET 1"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); - })) { + }, () -> { var entities = repo.select().orderBy(Metamodel.of(Owner.class, "id")).offset(1).limit(2).getResultList(); assertEquals(2, entities.size()); assertEquals("George", entities.getFirst().firstName()); @@ -138,7 +137,7 @@ public void testSelectLimitOffset() { assertEquals("McFarland", entities.getLast().address().city()); assertEquals("6085558763", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -149,9 +148,9 @@ public void testSelectOffset() { ORDER BY o.id LIMIT 18446744073709551615 OFFSET 1"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); - })) { + }, () -> { var entities = repo.select().orderBy(Metamodel.of(Owner.class, "id")).offset(1).getResultList(); assertEquals(9, entities.size()); assertEquals("George", entities.getFirst().firstName()); @@ -159,7 +158,7 @@ public void testSelectOffset() { assertEquals("110 W. Liberty St.", entities.getFirst().address().address()); assertEquals("Madison", entities.getFirst().address().city()); assertEquals("6085551023", entities.getFirst().telephone()); - } + }); } @Test @@ -169,7 +168,7 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -177,12 +176,12 @@ INSERT INTO vet (first_name, last_name) assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(Vet.builder().firstName("John").lastName("Doe").build()); assertTrue(entity.id() > 0); assertEquals("John", entity.firstName()); assertEquals("Doe", entity.lastName()); - } + }); } @Test @@ -192,7 +191,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) VALUES (?, ?, ?, ?, ?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -200,7 +199,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(Owner.builder().firstName("John").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build()); assertTrue(entity.id() > 0); assertEquals("John", entity.firstName()); @@ -209,7 +208,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sunnyvale", entity.address().city()); assertNull("telephone", entity.telephone()); assertEquals(0, entity.version()); - } + }); } @Test @@ -219,14 +218,14 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) VALUES (?, ?, ?, ?, ?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( Owner.builder().firstName("John").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build(), Owner.builder().firstName("Jane").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build() @@ -244,7 +243,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sunnyvale", entities.getLast().address().city()); assertNull("telephone", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -254,14 +253,14 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( Vet.builder().firstName("John").lastName("Doe").build(), Vet.builder().firstName("Jane").lastName("Doe").build() @@ -271,7 +270,7 @@ INSERT INTO vet (first_name, last_name) assertEquals("Doe", entities.getFirst().lastName()); assertEquals("Jane", entities.getLast().firstName()); assertEquals("Doe", entities.getLast().lastName()); - } + }); } @Test @@ -283,7 +282,7 @@ public void testUpdateAndFetchInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -296,7 +295,7 @@ public void testUpdateAndFetchInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { var update = repo.updateAndFetch(entity.toBuilder().lastName("Smith").build()); assertEquals("Betty", update.firstName()); assertEquals("Smith", update.lastName()); @@ -304,7 +303,7 @@ public void testUpdateAndFetchInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -316,14 +315,14 @@ public void testUpdateAndFetchInlineVersionBatch() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entities = repo.findAllById(List.of(1, 2)); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertTrue(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var updates = repo.updateAndFetch( entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList() ).stream().sorted(Comparator.comparingInt(Entity::id)).toList(); @@ -340,7 +339,7 @@ public void testUpdateAndFetchInlineVersionBatch() { assertEquals("Madison", updates.getLast().address().city()); assertEquals("6085551023", updates.getLast().telephone()); assertEquals(1, updates.getLast().version()); - } + }); } @Test @@ -351,14 +350,14 @@ public void testUpdateAndFetchBatch() { WHERE id = ?"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( Vet.builder().id(1).firstName("John").lastName("Doe").build(), Vet.builder().id(2).firstName("Jane").lastName("Doe").build() @@ -368,7 +367,7 @@ public void testUpdateAndFetchBatch() { assertEquals("Doe", entities.getFirst().lastName()); assertEquals("Jane", entities.getLast().firstName()); assertEquals("Doe", entities.getLast().lastName()); - } + }); } @Test @@ -379,21 +378,19 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id), first_name = VALUES(first_name), last_name = VALUES(last_name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(Vet.builder().firstName("John").lastName("Doe").build()); - } - var entity = repo.select().where(Metamodel.of(Vet.class, "firstName"), EQUALS, "John").getSingleResult(); - repo.upsert(entity.toBuilder().lastName("Smith").build()); - var updated = repo.select().where(Metamodel.of(Vet.class, "firstName"), EQUALS, "John").getSingleResult(); - assertEquals(entity.id(), updated.id()); - assertEquals("John", updated.firstName()); - assertEquals("Smith", updated.lastName()); + }, () -> repo.upsert(Vet.builder().firstName("John").lastName("Doe").build())); + var entity = repo.select().where(Metamodel.of(Vet.class, "firstName"), EQUALS, "John").getSingleResult(); + repo.upsert(entity.toBuilder().lastName("Smith").build()); + var updated = repo.select().where(Metamodel.of(Vet.class, "firstName"), EQUALS, "John").getSingleResult(); + assertEquals(entity.id(), updated.id()); + assertEquals("John", updated.firstName()); + assertEquals("Smith", updated.lastName()); } @Test @@ -404,16 +401,14 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id), first_name = VALUES(first_name), last_name = VALUES(last_name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); - })) { - repo.upsert(List.of( - Vet.builder().firstName("John").lastName("Doe").build(), - Vet.builder().firstName("Jane").lastName("Doe").build())); - } + }, () -> repo.upsert(List.of( + Vet.builder().firstName("John").lastName("Doe").build(), + Vet.builder().firstName("Jane").lastName("Doe").build()))); var entities = repo.select().where(Metamodel.of(Vet.class, "lastName"), EQUALS, "Doe").getResultList(); repo.upsert(entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList()); var updated = repo.select().where(Metamodel.of(Vet.class, "lastName"), EQUALS, "Smith").getResultList(); @@ -432,7 +427,7 @@ public void testUpsertInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -445,7 +440,7 @@ public void testUpsertInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { repo.upsert(entity.toBuilder().lastName("Smith").build()); var update = repo.getById(1); assertEquals("Betty", update.firstName()); @@ -454,7 +449,7 @@ public void testUpsertInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -466,7 +461,7 @@ public void testUpsertAndFetchInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -479,7 +474,7 @@ public void testUpsertAndFetchInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { var update = repo.upsertAndFetch(entity.toBuilder().lastName("Smith").build()); assertEquals("Betty", update.firstName()); assertEquals("Smith", update.lastName()); @@ -487,7 +482,7 @@ public void testUpsertAndFetchInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -500,7 +495,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -511,7 +506,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sun Prairie", sql.parameters().get(3).dbValue()); assertEquals("6085551749", sql.parameters().get(4).dbValue()); } - })) { + }, () -> { var insert = repo.upsertAndFetch(entity.toBuilder() .id(0) // Default value. .lastName("Smith").build()); @@ -522,7 +517,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sun Prairie", insert.address().city()); assertEquals("6085551749", insert.telephone()); assertEquals(0, insert.version()); - } + }); } @Test @@ -534,14 +529,14 @@ public void testUpsertInlineVersionBatch() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entities = repo.findAllById(List.of(1, 2)); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertTrue(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { repo.upsert( entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList() ); @@ -559,7 +554,7 @@ public void testUpsertInlineVersionBatch() { assertEquals("Madison", updates.getLast().address().city()); assertEquals("6085551023", updates.getLast().telephone()); assertEquals(1, updates.getLast().version()); - } + }); } @Builder(toBuilder = true) @@ -577,15 +572,13 @@ INSERT INTO pet_type (name, description) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id), name = VALUES(name), description = VALUES(description)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(PetType.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertEquals("dragon", sql.parameters().get(0).dbValue()); assertEquals("description", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(PetType.builder().name("dragon").description("description").build()); - } + }, () -> repo.upsert(PetType.builder().name("dragon").description("description").build())); var entity = repo.select().where(Metamodel.of(PetType.class, "name"), EQUALS, "dragon").getSingleResult(); assertEquals("description", entity.description()); repo.upsert(PetType.builder().name("dragon").description(null).build()); @@ -606,15 +599,13 @@ INSERT INTO specialty (id, name) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = VALUES(id), name = VALUES(name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertEquals(4, sql.parameters().get(0).dbValue()); assertEquals("anaesthetics", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(Specialty.builder().id(4).name("anaesthetics").build()); - } + }, () -> repo.upsert(Specialty.builder().id(4).name("anaesthetics").build())); var entity = repo.select().where(Metamodel.of(Specialty.class, "name"), EQUALS, "anaesthetics").getSingleResult(); repo.upsert(entity.toBuilder().name("anaesthetist").build()); var updated = repo.select().where(Metamodel.of(Specialty.class, "name"), EQUALS, "anaesthetist").getSingleResult(); @@ -630,7 +621,7 @@ INSERT INTO specialty (id, name) ON DUPLICATE KEY UPDATE id = VALUES(id), name = VALUES(name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -638,12 +629,12 @@ INSERT INTO specialty (id, name) assertEquals(4, sql.parameters().get(0).dbValue()); assertEquals("anaesthetics", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.upsertAndFetch(Specialty.builder().id(4).name("anaesthetics").build()); var updated = repo.upsertAndFetch(entity.toBuilder().name("anaesthetist").build()); assertEquals(entity.id(), updated.id()); assertEquals("anaesthetist", updated.name()); - } + }); } @Test @@ -653,16 +644,14 @@ INSERT INTO specialty (id, name) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = VALUES(id), name = VALUES(name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); - })) { - repo.upsert(List.of( - Specialty.builder().id(4).name("anaesthetics").build(), - Specialty.builder().id(5).name("nurse").build())); - } + }, () -> repo.upsert(List.of( + Specialty.builder().id(4).name("anaesthetics").build(), + Specialty.builder().id(5).name("nurse").build()))); var entities = repo.select().where(Metamodel.of(Specialty.class, "id"), GREATER_THAN_OR_EQUAL, 4).getResultList(); repo.upsert(entities.stream().map(e -> e.toBuilder().name(STR."\{e.name()}s").build()).toList()); var updated = repo.select().where(Metamodel.of(Specialty.class, "id"), GREATER_THAN_OR_EQUAL, 4).getResultList(); @@ -678,21 +667,21 @@ INSERT INTO specialty (id, name) ON DUPLICATE KEY UPDATE id = VALUES(id), name = VALUES(name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( Specialty.builder().id(4).name("anaesthetics").build(), Specialty.builder().id(5).name("nurse").build())); var updated = repo.upsertAndFetch(entities.stream().map(e -> e.toBuilder().name(STR."\{e.name()}s").build()).toList()); assertEquals(2, updated.size()); assertTrue(updated.stream().allMatch(entity -> entity.name().endsWith("s"))); - } + }); } @Builder(toBuilder = true) @@ -719,7 +708,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -727,11 +716,11 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(1, sql.parameters().get(0).dbValue()); assertEquals(2, sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).build()); assertEquals(1, entity.id().vetId()); assertEquals(2, entity.id().specialtyId()); - } + }); } @Test @@ -741,14 +730,14 @@ INSERT INTO vet_specialty (vet_id, specialty_id) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(6).specialtyId(3).build()).build() @@ -758,7 +747,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(2, entities.getFirst().id().specialtyId()); assertEquals(6, entities.getLast().id().vetId()); assertEquals(3, entities.getLast().id().specialtyId()); - } + }); } @Test @@ -769,14 +758,14 @@ INSERT INTO vet_specialty (vet_id, specialty_id) ON DUPLICATE KEY UPDATE vet_id = VALUES(vet_id), specialty_id = VALUES(specialty_id)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).vet(Vet.builder().id(1).build()).specialty(Specialty.builder().id(2).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(6).specialtyId(3).build()).vet(Vet.builder().id(6).build()).specialty(Specialty.builder().id(3).build()).build() @@ -786,7 +775,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(2, entities.getFirst().id().specialtyId()); assertEquals(6, entities.getLast().id().vetId()); assertEquals(3, entities.getLast().id().specialtyId()); - } + }); } @Test @@ -797,14 +786,14 @@ INSERT INTO vet_specialty (vet_id, specialty_id) ON DUPLICATE KEY UPDATE vet_id = VALUES(vet_id), specialty_id = VALUES(specialty_id)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(2).specialtyId(1).build()).vet(Vet.builder().id(1).build()).specialty(Specialty.builder().id(1).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(3).specialtyId(2).build()).vet(Vet.builder().id(3).build()).specialty(Specialty.builder().id(2).build()).build() @@ -814,6 +803,6 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(1, entities.getFirst().id().specialtyId()); assertEquals(3, entities.getLast().id().vetId()); assertEquals(2, entities.getLast().id().specialtyId()); - } + }); } } diff --git a/storm-metamodel-processor/pom.xml b/storm-metamodel-processor/pom.xml index 876968d18..feb639e30 100644 --- a/storm-metamodel-processor/pom.xml +++ b/storm-metamodel-processor/pom.xml @@ -5,7 +5,7 @@ st.orm storm-framework - 1.3.2 + 1.3.3 ../pom.xml storm-metamodel-processor diff --git a/storm-mssqlserver/pom.xml b/storm-mssqlserver/pom.xml index 7a80b9b97..42da544e4 100644 --- a/storm-mssqlserver/pom.xml +++ b/storm-mssqlserver/pom.xml @@ -6,7 +6,7 @@ st.orm storm-framework - 1.3.2 + 1.3.3 ../pom.xml storm-mssqlserver diff --git a/storm-mssqlserver/src/main/java/st/orm/spi/mssqlserver/MSSQLServerEntityRepositoryImpl.java b/storm-mssqlserver/src/main/java/st/orm/spi/mssqlserver/MSSQLServerEntityRepositoryImpl.java index a53b92a66..236c3fa70 100644 --- a/storm-mssqlserver/src/main/java/st/orm/spi/mssqlserver/MSSQLServerEntityRepositoryImpl.java +++ b/storm-mssqlserver/src/main/java/st/orm/spi/mssqlserver/MSSQLServerEntityRepositoryImpl.java @@ -221,14 +221,14 @@ public void upsert(@Nonnull E entity) { } validateUpsert(entity); var versionAware = new AtomicBoolean(); - try (var _ = intercept(sql -> sql.versionAware(versionAware.getPlain()))) { + intercept(sql -> sql.versionAware(versionAware.getPlain()), () -> { // Note: SQL Server’s MERGE syntax does not require a FROM DUAL clause. var query = ormTemplate.query(flatten(RAW.""" - MERGE INTO \{model.type()} t - USING (\{mergeSelect(entity)}) src - ON (\{mergeOn()})\{mergeUpdate(versionAware)}\{mergeInsert()};""")); - query.executeUpdate(); - } + MERGE INTO \{model.type()} t + USING (\{mergeSelect(entity)}) src + ON (\{mergeOn()})\{mergeUpdate(versionAware)}\{mergeInsert()};""")); + query.executeUpdate(); + }); } /** @@ -396,12 +396,12 @@ protected PreparedQuery prepareInsertQuery() { protected PreparedQuery prepareUpsertQuery() { var bindVars = ormTemplate.createBindVars(); var versionAware = new AtomicBoolean(); - try (var _ = intercept(sql -> sql.versionAware(versionAware.getPlain()))) { - return ormTemplate.query(flatten(RAW.""" + return intercept(sql -> sql.versionAware(versionAware.getPlain()), () -> + ormTemplate.query(flatten(RAW.""" MERGE INTO \{model.type()} t USING (\{mergeSelect(bindVars)}) src - ON (\{mergeOn()})\{mergeUpdate(versionAware)}\{mergeInsert()};""")).prepare(); - } + ON (\{mergeOn()})\{mergeUpdate(versionAware)}\{mergeInsert()};""") + ).prepare()); } protected void insertAndFetchIds(@Nonnull List batch, diff --git a/storm-mssqlserver/src/test/java/st/orm/spi/mssqlserver/MSSQLServerEntityRepositoryTest.java b/storm-mssqlserver/src/test/java/st/orm/spi/mssqlserver/MSSQLServerEntityRepositoryTest.java index e5bdde995..916928746 100644 --- a/storm-mssqlserver/src/test/java/st/orm/spi/mssqlserver/MSSQLServerEntityRepositoryTest.java +++ b/storm-mssqlserver/src/test/java/st/orm/spi/mssqlserver/MSSQLServerEntityRepositoryTest.java @@ -40,6 +40,7 @@ import static org.springframework.test.util.AssertionErrors.assertNull; import static st.orm.template.Operator.EQUALS; import static st.orm.template.Operator.GREATER_THAN_OR_EQUAL; +import static st.orm.template.SqlInterceptor.observe; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = IntegrationConfig.class) @@ -95,9 +96,7 @@ public void testSelectLimit() { SELECT TOP 2 o.id, o.first_name, o.last_name, o.address, o.city, o.telephone, o.version FROM owner o"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().limit(2).getResultList(); assertEquals(2, entities.size()); assertEquals("Betty", entities.getFirst().firstName()); @@ -112,7 +111,7 @@ public void testSelectLimit() { assertEquals("Madison", entities.getLast().address().city()); assertEquals("6085551023", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -123,9 +122,7 @@ public void testSelectLimitOffset() { ORDER BY o.id OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().orderBy(Metamodel.of(Owner.class, "id")).offset(1).limit(2).getResultList(); assertEquals(2, entities.size()); assertEquals("George", entities.getFirst().firstName()); @@ -140,7 +137,7 @@ public void testSelectLimitOffset() { assertEquals("McFarland", entities.getLast().address().city()); assertEquals("6085558763", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -151,9 +148,7 @@ public void testSelectOffset() { ORDER BY o.id OFFSET 1 ROWS"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().orderBy(Metamodel.of(Owner.class, "id")).offset(1).getResultList(); assertEquals(9, entities.size()); assertEquals("George", entities.getFirst().firstName()); @@ -161,7 +156,7 @@ public void testSelectOffset() { assertEquals("110 W. Liberty St.", entities.getFirst().address().address()); assertEquals("Madison", entities.getFirst().address().city()); assertEquals("6085551023", entities.getFirst().telephone()); - } + }); } @Test @@ -171,7 +166,7 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -179,12 +174,12 @@ INSERT INTO vet (first_name, last_name) assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(Vet.builder().firstName("John").lastName("Doe").build()); assertTrue(entity.id() > 0); assertEquals("John", entity.firstName()); assertEquals("Doe", entity.lastName()); - } + }); } @Test @@ -194,7 +189,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) VALUES (?, ?, ?, ?, ?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -202,7 +197,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(Owner.builder().firstName("John").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build()); assertTrue(entity.id() > 0); assertEquals("John", entity.firstName()); @@ -211,7 +206,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sunnyvale", entity.address().city()); assertNull("telephone", entity.telephone()); assertEquals(0, entity.version()); - } + }); } @Test @@ -221,14 +216,14 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) VALUES (?, ?, ?, ?, ?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertFalse(sql.bindVariables().isPresent()); // Using actual values in case of Sql Server as batch mode is not supported in combination with generated keys. } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( Owner.builder().firstName("John").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build(), Owner.builder().firstName("Jane").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build() @@ -246,7 +241,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sunnyvale", entities.getLast().address().city()); assertNull("telephone", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -256,14 +251,14 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertFalse(sql.bindVariables().isPresent()); // Using actual values in case of Sql Server as batch mode is not supported in combination with generated keys. } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( Vet.builder().firstName("John").lastName("Doe").build(), Vet.builder().firstName("Jane").lastName("Doe").build() @@ -273,7 +268,7 @@ INSERT INTO vet (first_name, last_name) assertEquals("Doe", entities.getFirst().lastName()); assertEquals("Jane", entities.getLast().firstName()); assertEquals("Doe", entities.getLast().lastName()); - } + }); } @Test @@ -285,7 +280,7 @@ public void testUpdateAndFetchInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -298,7 +293,7 @@ public void testUpdateAndFetchInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { var update = repo.updateAndFetch(entity.toBuilder().lastName("Smith").build()); assertEquals("Betty", update.firstName()); assertEquals("Smith", update.lastName()); @@ -306,7 +301,7 @@ public void testUpdateAndFetchInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -318,14 +313,14 @@ public void testUpdateAndFetchInlineVersionBatch() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entities = repo.findAllById(List.of(1, 2)); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertTrue(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var updates = repo.updateAndFetch( entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList() ).stream().sorted(Comparator.comparingInt(Entity::id)).toList(); @@ -342,7 +337,7 @@ public void testUpdateAndFetchInlineVersionBatch() { assertEquals("Madison", updates.getLast().address().city()); assertEquals("6085551023", updates.getLast().telephone()); assertEquals(1, updates.getLast().version()); - } + }); } @Test @@ -353,14 +348,14 @@ public void testUpdateAndFetchBatch() { WHERE id = ?"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( Vet.builder().id(1).firstName("John").lastName("Doe").build(), Vet.builder().id(2).firstName("Jane").lastName("Doe").build() @@ -370,7 +365,7 @@ public void testUpdateAndFetchBatch() { assertEquals("Doe", entities.getFirst().lastName()); assertEquals("Jane", entities.getLast().firstName()); assertEquals("Doe", entities.getLast().lastName()); - } + }); } @Test @@ -380,15 +375,13 @@ public void testUpsert() { INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(Vet.builder().firstName("John").lastName("Doe").build()); - } + }, () -> repo.upsert(Vet.builder().firstName("John").lastName("Doe").build())); var entity = repo.select().where(Metamodel.of(Vet.class, "firstName"), EQUALS, "John").getSingleResult(); repo.upsert(entity.toBuilder().lastName("Smith").build()); var updated = repo.select().where(Metamodel.of(Vet.class, "firstName"), EQUALS, "John").getSingleResult(); @@ -404,16 +397,14 @@ public void testUpsertBatch() { INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); - })) { - repo.upsert(List.of( - Vet.builder().firstName("John").lastName("Doe").build(), - Vet.builder().firstName("Jane").lastName("Doe").build())); - } + }, () -> repo.upsert(List.of( + Vet.builder().firstName("John").lastName("Doe").build(), + Vet.builder().firstName("Jane").lastName("Doe").build()))); var entities = repo.select().where(Metamodel.of(Vet.class, "lastName"), EQUALS, "Doe").getResultList(); repo.upsert(entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList()); var updated = repo.select().where(Metamodel.of(Vet.class, "lastName"), EQUALS, "Smith").getResultList(); @@ -432,7 +423,7 @@ public void testUpsertInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -445,7 +436,7 @@ public void testUpsertInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { repo.upsert(entity.toBuilder().lastName("Smith").build()); var update = repo.getById(1); assertEquals("Betty", update.firstName()); @@ -454,7 +445,7 @@ public void testUpsertInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -466,7 +457,7 @@ public void testUpsertAndFetchInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -479,7 +470,7 @@ public void testUpsertAndFetchInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { var update = repo.upsertAndFetch(entity.toBuilder().lastName("Smith").build()); assertEquals("Betty", update.firstName()); assertEquals("Smith", update.lastName()); @@ -487,7 +478,7 @@ public void testUpsertAndFetchInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -499,7 +490,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -510,7 +501,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sun Prairie", sql.parameters().get(3).dbValue()); assertEquals("6085551749", sql.parameters().get(4).dbValue()); } - })) { + }, () -> { var insert = repo.upsertAndFetch(entity.toBuilder() .id(0) // Default value. .lastName("Smith").build()); @@ -521,7 +512,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sun Prairie", insert.address().city()); assertEquals("6085551749", insert.telephone()); assertEquals(0, insert.version()); - } + }); } @Test @@ -533,14 +524,14 @@ public void testUpsertInlineVersionBatch() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entities = repo.findAllById(List.of(1, 2)); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertTrue(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { repo.upsert( entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList() ); @@ -558,7 +549,7 @@ public void testUpsertInlineVersionBatch() { assertEquals("Madison", updates.getLast().address().city()); assertEquals("6085551023", updates.getLast().telephone()); assertEquals(1, updates.getLast().version()); - } + }); } @Builder(toBuilder = true) @@ -576,15 +567,13 @@ public void testUpsertUniqueKey() { INSERT INTO pet_type (name, description) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(PetType.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertEquals("dragon", sql.parameters().get(0).dbValue()); assertEquals("description", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(PetType.builder().name("dragon").description("description").build()); - } + }, () -> repo.upsert(PetType.builder().name("dragon").description("description").build())); var entity = repo.select().where(Metamodel.of(PetType.class, "name"), EQUALS, "dragon").getSingleResult(); assertEquals("description", entity.description()); var e = assertThrows(PersistenceException.class, () -> repo.upsert(PetType.builder().name("dragon").description("description").build())); @@ -609,15 +598,13 @@ public void testUpsertNonAutoGenerated() { INSERT (id, name) VALUES (src.id, src.name);"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertEquals(4, sql.parameters().get(0).dbValue()); assertEquals("anaesthetics", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(Specialty.builder().id(4).name("anaesthetics").build()); - } + }, () -> repo.upsert(Specialty.builder().id(4).name("anaesthetics").build())); var entity = repo.select().where(Metamodel.of(Specialty.class, "name"), EQUALS, "anaesthetics").getSingleResult(); repo.upsert(entity.toBuilder().name("anaesthetist").build()); var updated = repo.select().where(Metamodel.of(Specialty.class, "name"), EQUALS, "anaesthetist").getSingleResult(); @@ -638,7 +625,7 @@ public void testUpsertAndFetchNonAutoGenerated() { VALUES (src.id, src.name);"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -646,12 +633,12 @@ public void testUpsertAndFetchNonAutoGenerated() { assertEquals(4, sql.parameters().get(0).dbValue()); assertEquals("anaesthetics", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.upsertAndFetch(Specialty.builder().id(4).name("anaesthetics").build()); var updated = repo.upsertAndFetch(entity.toBuilder().name("anaesthetist").build()); assertEquals(entity.id(), updated.id()); assertEquals("anaesthetist", updated.name()); - } + }); } @Test @@ -666,16 +653,14 @@ public void testUpsertNonAutoGeneratedBatch() { INSERT (id, name) VALUES (src.id, src.name);"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); - })) { - repo.upsert(List.of( - Specialty.builder().id(4).name("anaesthetics").build(), - Specialty.builder().id(5).name("nurse").build())); - } + }, () -> repo.upsert(List.of( + Specialty.builder().id(4).name("anaesthetics").build(), + Specialty.builder().id(5).name("nurse").build()))); var entities = repo.select().where(Metamodel.of(Specialty.class, "id"), GREATER_THAN_OR_EQUAL, 4).getResultList(); repo.upsert(entities.stream().map(e -> e.toBuilder().name(STR."\{e.name()}s").build()).toList()); var updated = repo.select().where(Metamodel.of(Specialty.class, "id"), GREATER_THAN_OR_EQUAL, 4).getResultList(); @@ -696,21 +681,21 @@ public void testUpsertAndFetchNonAutoGeneratedBatch() { VALUES (src.id, src.name);"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( Specialty.builder().id(4).name("anaesthetics").build(), Specialty.builder().id(5).name("nurse").build())); var updated = repo.upsertAndFetch(entities.stream().map(e -> e.toBuilder().name(STR."\{e.name()}s").build()).toList()); assertEquals(2, updated.size()); assertTrue(updated.stream().allMatch(entity -> entity.name().endsWith("s"))); - } + }); } @Builder(toBuilder = true) @@ -737,7 +722,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -745,11 +730,11 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(1, sql.parameters().get(0).dbValue()); assertEquals(2, sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).build()); assertEquals(1, entity.id().vetId()); assertEquals(2, entity.id().specialtyId()); - } + }); } @Test @@ -759,14 +744,14 @@ INSERT INTO vet_specialty (vet_id, specialty_id) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertFalse(sql.bindVariables().isPresent()); // Using actual values in case of Sql Server as batch mode is not supported in combination with generated keys. } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(6).specialtyId(3).build()).build() @@ -776,7 +761,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(2, entities.getFirst().id().specialtyId()); assertEquals(6, entities.getLast().id().vetId()); assertEquals(3, entities.getLast().id().specialtyId()); - } + }); } @Test @@ -790,14 +775,14 @@ public void testUpsertAndFetchBatchNewCompoundPk() { VALUES (src.vet_id, src.specialty_id);"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).vet(Vet.builder().id(1).build()).specialty(Specialty.builder().id(2).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(6).specialtyId(3).build()).vet(Vet.builder().id(6).build()).specialty(Specialty.builder().id(3).build()).build() @@ -807,7 +792,7 @@ public void testUpsertAndFetchBatchNewCompoundPk() { assertEquals(2, entities.getFirst().id().specialtyId()); assertEquals(6, entities.getLast().id().vetId()); assertEquals(3, entities.getLast().id().specialtyId()); - } + }); } @Test @@ -821,14 +806,14 @@ public void testUpsertAndFetchBatchExistingCompoundPk() { VALUES (src.vet_id, src.specialty_id);"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(2).specialtyId(1).build()).vet(Vet.builder().id(1).build()).specialty(Specialty.builder().id(1).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(3).specialtyId(2).build()).vet(Vet.builder().id(3).build()).specialty(Specialty.builder().id(2).build()).build() @@ -838,6 +823,6 @@ public void testUpsertAndFetchBatchExistingCompoundPk() { assertEquals(1, entities.getFirst().id().specialtyId()); assertEquals(3, entities.getLast().id().vetId()); assertEquals(2, entities.getLast().id().specialtyId()); - } + }); } } diff --git a/storm-mysql/pom.xml b/storm-mysql/pom.xml index f28580d6c..2ae3015eb 100644 --- a/storm-mysql/pom.xml +++ b/storm-mysql/pom.xml @@ -6,7 +6,7 @@ st.orm storm-framework - 1.3.2 + 1.3.3 ../pom.xml storm-mysql diff --git a/storm-mysql/src/main/java/st/orm/spi/mysql/MySQLEntityRepositoryImpl.java b/storm-mysql/src/main/java/st/orm/spi/mysql/MySQLEntityRepositoryImpl.java index eb58d2572..9efdbdc2c 100644 --- a/storm-mysql/src/main/java/st/orm/spi/mysql/MySQLEntityRepositoryImpl.java +++ b/storm-mysql/src/main/java/st/orm/spi/mysql/MySQLEntityRepositoryImpl.java @@ -142,12 +142,12 @@ public void upsert(@Nonnull E entity) { } validateUpsert(entity); var versionAware = new AtomicBoolean(); - try (var _ = intercept(sql -> sql.versionAware(versionAware.getPlain()))) { + intercept(sql -> sql.versionAware(versionAware.getPlain()), () -> { var query = ormTemplate.query(flatten(RAW.""" INSERT INTO \{model.type()} VALUES \{entity}\{onDuplicateKey(versionAware)}""")); query.executeUpdate(); - } + }); } /** @@ -172,21 +172,22 @@ public ID upsertAndFetchId(@Nonnull E entity) { } validateUpsert(entity); var versionAware = new AtomicBoolean(); - try (var _ = intercept(sql -> sql.versionAware(versionAware.getPlain())); - var query = ormTemplate.query(flatten(RAW.""" + return intercept(sql -> sql.versionAware(versionAware.getPlain()), () -> { + try (var query = ormTemplate.query(flatten(RAW.""" INSERT INTO \{model.type()} VALUES \{entity}\{onDuplicateKey(versionAware)}""")).prepare()) { - query.executeUpdate(); - if (autoGeneratedPrimaryKey) { - try (var stream = query.getGeneratedKeys(model.primaryKeyType())) { - return stream - .reduce((_, _) -> { - throw new NonUniqueResultException("Expected single result, but found more than one."); - }).orElseThrow(() -> new NoResultException("Expected single result, but found none.")); + query.executeUpdate(); + if (autoGeneratedPrimaryKey) { + try (var stream = query.getGeneratedKeys(model.primaryKeyType())) { + return stream + .reduce((_, _) -> { + throw new NonUniqueResultException("Expected single result, but found more than one."); + }).orElseThrow(() -> new NoResultException("Expected single result, but found none.")); + } } + return entity.id(); } - return entity.id(); - } + }); } /** @@ -451,11 +452,11 @@ private Map> partition(@Nonnull List entities) { protected PreparedQuery prepareUpsertQuery() { var bindVars = ormTemplate.createBindVars(); var versionAware = new AtomicBoolean(); - try (var _ = intercept(sql -> sql.versionAware(versionAware.getPlain()))) { - return ormTemplate.query(flatten(RAW.""" + return intercept(sql -> sql.versionAware(versionAware.getPlain()), () -> + ormTemplate.query(flatten(RAW.""" INSERT INTO \{model.type()} - VALUES \{bindVars}\{onDuplicateKey(versionAware)}""")).prepare(); - } + VALUES \{bindVars}\{onDuplicateKey(versionAware)}""") + ).prepare()); } protected void upsertAndFetchIds(@Nonnull List batch, @Nonnull Supplier querySupplier, @Nullable BatchCallback callback) { diff --git a/storm-mysql/src/test/java/st/orm/spi/mysql/MySQLEntityRepositoryTest.java b/storm-mysql/src/test/java/st/orm/spi/mysql/MySQLEntityRepositoryTest.java index ef4e6fb9c..32bce2a02 100644 --- a/storm-mysql/src/test/java/st/orm/spi/mysql/MySQLEntityRepositoryTest.java +++ b/storm-mysql/src/test/java/st/orm/spi/mysql/MySQLEntityRepositoryTest.java @@ -36,6 +36,7 @@ import static org.springframework.test.util.AssertionErrors.assertNull; import static st.orm.template.Operator.EQUALS; import static st.orm.template.Operator.GREATER_THAN_OR_EQUAL; +import static st.orm.template.SqlInterceptor.observe; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = IntegrationConfig.class) @@ -93,9 +94,7 @@ public void testSelectLimit() { FROM owner o LIMIT 2"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().limit(2).getResultList(); assertEquals(2, entities.size()); assertEquals("Betty", entities.getFirst().firstName()); @@ -110,7 +109,7 @@ public void testSelectLimit() { assertEquals("Madison", entities.getLast().address().city()); assertEquals("6085551023", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -121,9 +120,7 @@ public void testSelectLimitOffset() { ORDER BY o.id LIMIT 2 OFFSET 1"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().orderBy(Metamodel.of(Owner.class, "id")).offset(1).limit(2).getResultList(); assertEquals(2, entities.size()); assertEquals("George", entities.getFirst().firstName()); @@ -138,7 +135,7 @@ public void testSelectLimitOffset() { assertEquals("McFarland", entities.getLast().address().city()); assertEquals("6085558763", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -149,9 +146,7 @@ public void testSelectOffset() { ORDER BY o.id LIMIT 18446744073709551615 OFFSET 1"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().orderBy(Metamodel.of(Owner.class, "id")).offset(1).getResultList(); assertEquals(9, entities.size()); assertEquals("George", entities.getFirst().firstName()); @@ -159,7 +154,7 @@ public void testSelectOffset() { assertEquals("110 W. Liberty St.", entities.getFirst().address().address()); assertEquals("Madison", entities.getFirst().address().city()); assertEquals("6085551023", entities.getFirst().telephone()); - } + }); } @Test @@ -169,7 +164,7 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -177,12 +172,12 @@ INSERT INTO vet (first_name, last_name) assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(Vet.builder().firstName("John").lastName("Doe").build()); assertTrue(entity.id() > 0); assertEquals("John", entity.firstName()); assertEquals("Doe", entity.lastName()); - } + }); } @Test @@ -192,7 +187,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) VALUES (?, ?, ?, ?, ?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -200,7 +195,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(Owner.builder().firstName("John").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build()); assertTrue(entity.id() > 0); assertEquals("John", entity.firstName()); @@ -209,7 +204,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sunnyvale", entity.address().city()); assertNull("telephone", entity.telephone()); assertEquals(0, entity.version()); - } + }); } @Test @@ -219,14 +214,14 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) VALUES (?, ?, ?, ?, ?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( Owner.builder().firstName("John").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build(), Owner.builder().firstName("Jane").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build() @@ -244,7 +239,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sunnyvale", entities.getLast().address().city()); assertNull("telephone", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -254,14 +249,14 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( Vet.builder().firstName("John").lastName("Doe").build(), Vet.builder().firstName("Jane").lastName("Doe").build() @@ -271,7 +266,7 @@ INSERT INTO vet (first_name, last_name) assertEquals("Doe", entities.getFirst().lastName()); assertEquals("Jane", entities.getLast().firstName()); assertEquals("Doe", entities.getLast().lastName()); - } + }); } @Test @@ -283,7 +278,7 @@ public void testUpdateAndFetchInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -296,7 +291,7 @@ public void testUpdateAndFetchInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { var update = repo.updateAndFetch(entity.toBuilder().lastName("Smith").build()); assertEquals("Betty", update.firstName()); assertEquals("Smith", update.lastName()); @@ -304,7 +299,7 @@ public void testUpdateAndFetchInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -316,14 +311,14 @@ public void testUpdateAndFetchInlineVersionBatch() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entities = repo.findAllById(List.of(1, 2)); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertTrue(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var updates = repo.updateAndFetch( entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList() ).stream().sorted(Comparator.comparingInt(Entity::id)).toList(); @@ -340,7 +335,7 @@ public void testUpdateAndFetchInlineVersionBatch() { assertEquals("Madison", updates.getLast().address().city()); assertEquals("6085551023", updates.getLast().telephone()); assertEquals(1, updates.getLast().version()); - } + }); } @Test @@ -351,14 +346,14 @@ public void testUpdateAndFetchBatch() { WHERE id = ?"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( Vet.builder().id(1).firstName("John").lastName("Doe").build(), Vet.builder().id(2).firstName("Jane").lastName("Doe").build() @@ -368,7 +363,7 @@ public void testUpdateAndFetchBatch() { assertEquals("Doe", entities.getFirst().lastName()); assertEquals("Jane", entities.getLast().firstName()); assertEquals("Doe", entities.getLast().lastName()); - } + }); } @Test @@ -379,15 +374,13 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id), first_name = VALUES(first_name), last_name = VALUES(last_name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(Vet.builder().firstName("John").lastName("Doe").build()); - } + }, () -> repo.upsert(Vet.builder().firstName("John").lastName("Doe").build())); var entity = repo.select().where(Metamodel.of(Vet.class, "firstName"), EQUALS, "John").getSingleResult(); repo.upsert(entity.toBuilder().lastName("Smith").build()); var updated = repo.select().where(Metamodel.of(Vet.class, "firstName"), EQUALS, "John").getSingleResult(); @@ -404,16 +397,14 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id), first_name = VALUES(first_name), last_name = VALUES(last_name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); - })) { - repo.upsert(List.of( - Vet.builder().firstName("John").lastName("Doe").build(), - Vet.builder().firstName("Jane").lastName("Doe").build())); - } + }, () -> repo.upsert(List.of( + Vet.builder().firstName("John").lastName("Doe").build(), + Vet.builder().firstName("Jane").lastName("Doe").build()))); var entities = repo.select().where(Metamodel.of(Vet.class, "lastName"), EQUALS, "Doe").getResultList(); repo.upsert(entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList()); var updated = repo.select().where(Metamodel.of(Vet.class, "lastName"), EQUALS, "Smith").getResultList(); @@ -432,7 +423,7 @@ public void testUpsertInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -445,7 +436,7 @@ public void testUpsertInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { repo.upsert(entity.toBuilder().lastName("Smith").build()); var update = repo.getById(1); assertEquals("Betty", update.firstName()); @@ -454,7 +445,7 @@ public void testUpsertInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -466,7 +457,7 @@ public void testUpsertAndFetchInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -479,7 +470,7 @@ public void testUpsertAndFetchInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { var update = repo.upsertAndFetch(entity.toBuilder().lastName("Smith").build()); assertEquals("Betty", update.firstName()); assertEquals("Smith", update.lastName()); @@ -487,7 +478,7 @@ public void testUpsertAndFetchInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -500,7 +491,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -511,7 +502,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sun Prairie", sql.parameters().get(3).dbValue()); assertEquals("6085551749", sql.parameters().get(4).dbValue()); } - })) { + }, () -> { var insert = repo.upsertAndFetch(entity.toBuilder() .id(0) // Default value. .lastName("Smith").build()); @@ -522,7 +513,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sun Prairie", insert.address().city()); assertEquals("6085551749", insert.telephone()); assertEquals(0, insert.version()); - } + }); } @Test @@ -534,14 +525,14 @@ public void testUpsertInlineVersionBatch() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entities = repo.findAllById(List.of(1, 2)); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertTrue(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { repo.upsert( entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList() ); @@ -559,7 +550,7 @@ public void testUpsertInlineVersionBatch() { assertEquals("Madison", updates.getLast().address().city()); assertEquals("6085551023", updates.getLast().telephone()); assertEquals(1, updates.getLast().version()); - } + }); } @Builder(toBuilder = true) @@ -577,15 +568,13 @@ INSERT INTO pet_type (name, description) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id), name = VALUES(name), description = VALUES(description)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(PetType.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertEquals("dragon", sql.parameters().get(0).dbValue()); assertEquals("description", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(PetType.builder().name("dragon").description("description").build()); - } + }, () -> repo.upsert(PetType.builder().name("dragon").description("description").build())); var entity = repo.select().where(Metamodel.of(PetType.class, "name"), EQUALS, "dragon").getSingleResult(); assertEquals("description", entity.description()); repo.upsert(PetType.builder().name("dragon").description(null).build()); @@ -606,15 +595,13 @@ INSERT INTO specialty (id, name) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = VALUES(id), name = VALUES(name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertEquals(4, sql.parameters().get(0).dbValue()); assertEquals("anaesthetics", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(Specialty.builder().id(4).name("anaesthetics").build()); - } + }, () -> repo.upsert(Specialty.builder().id(4).name("anaesthetics").build())); var entity = repo.select().where(Metamodel.of(Specialty.class, "name"), EQUALS, "anaesthetics").getSingleResult(); repo.upsert(entity.toBuilder().name("anaesthetist").build()); var updated = repo.select().where(Metamodel.of(Specialty.class, "name"), EQUALS, "anaesthetist").getSingleResult(); @@ -630,7 +617,7 @@ INSERT INTO specialty (id, name) ON DUPLICATE KEY UPDATE id = VALUES(id), name = VALUES(name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -638,12 +625,12 @@ INSERT INTO specialty (id, name) assertEquals(4, sql.parameters().get(0).dbValue()); assertEquals("anaesthetics", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.upsertAndFetch(Specialty.builder().id(4).name("anaesthetics").build()); var updated = repo.upsertAndFetch(entity.toBuilder().name("anaesthetist").build()); assertEquals(entity.id(), updated.id()); assertEquals("anaesthetist", updated.name()); - } + }); } @Test @@ -653,16 +640,14 @@ INSERT INTO specialty (id, name) VALUES (?, ?) ON DUPLICATE KEY UPDATE id = VALUES(id), name = VALUES(name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); - })) { - repo.upsert(List.of( - Specialty.builder().id(4).name("anaesthetics").build(), - Specialty.builder().id(5).name("nurse").build())); - } + }, () -> repo.upsert(List.of( + Specialty.builder().id(4).name("anaesthetics").build(), + Specialty.builder().id(5).name("nurse").build()))); var entities = repo.select().where(Metamodel.of(Specialty.class, "id"), GREATER_THAN_OR_EQUAL, 4).getResultList(); repo.upsert(entities.stream().map(e -> e.toBuilder().name(STR."\{e.name()}s").build()).toList()); var updated = repo.select().where(Metamodel.of(Specialty.class, "id"), GREATER_THAN_OR_EQUAL, 4).getResultList(); @@ -678,21 +663,21 @@ INSERT INTO specialty (id, name) ON DUPLICATE KEY UPDATE id = VALUES(id), name = VALUES(name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( Specialty.builder().id(4).name("anaesthetics").build(), Specialty.builder().id(5).name("nurse").build())); var updated = repo.upsertAndFetch(entities.stream().map(e -> e.toBuilder().name(STR."\{e.name()}s").build()).toList()); assertEquals(2, updated.size()); assertTrue(updated.stream().allMatch(entity -> entity.name().endsWith("s"))); - } + }); } @Builder(toBuilder = true) @@ -719,7 +704,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -727,11 +712,11 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(1, sql.parameters().get(0).dbValue()); assertEquals(2, sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).build()); assertEquals(1, entity.id().vetId()); assertEquals(2, entity.id().specialtyId()); - } + }); } @Test @@ -741,14 +726,14 @@ INSERT INTO vet_specialty (vet_id, specialty_id) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(6).specialtyId(3).build()).build() @@ -758,7 +743,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(2, entities.getFirst().id().specialtyId()); assertEquals(6, entities.getLast().id().vetId()); assertEquals(3, entities.getLast().id().specialtyId()); - } + }); } @Test @@ -769,14 +754,14 @@ INSERT INTO vet_specialty (vet_id, specialty_id) ON DUPLICATE KEY UPDATE vet_id = VALUES(vet_id), specialty_id = VALUES(specialty_id)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).vet(Vet.builder().id(1).build()).specialty(Specialty.builder().id(2).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(6).specialtyId(3).build()).vet(Vet.builder().id(6).build()).specialty(Specialty.builder().id(3).build()).build() @@ -786,7 +771,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(2, entities.getFirst().id().specialtyId()); assertEquals(6, entities.getLast().id().vetId()); assertEquals(3, entities.getLast().id().specialtyId()); - } + }); } @Test @@ -797,14 +782,14 @@ INSERT INTO vet_specialty (vet_id, specialty_id) ON DUPLICATE KEY UPDATE vet_id = VALUES(vet_id), specialty_id = VALUES(specialty_id)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(2).specialtyId(1).build()).vet(Vet.builder().id(1).build()).specialty(Specialty.builder().id(1).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(3).specialtyId(2).build()).vet(Vet.builder().id(3).build()).specialty(Specialty.builder().id(2).build()).build() @@ -814,6 +799,6 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(1, entities.getFirst().id().specialtyId()); assertEquals(3, entities.getLast().id().vetId()); assertEquals(2, entities.getLast().id().specialtyId()); - } + }); } } diff --git a/storm-oracle/pom.xml b/storm-oracle/pom.xml index bd615eb25..a0a0bfba7 100644 --- a/storm-oracle/pom.xml +++ b/storm-oracle/pom.xml @@ -6,7 +6,7 @@ st.orm storm-framework - 1.3.2 + 1.3.3 ../pom.xml storm-oracle diff --git a/storm-oracle/src/main/java/st/orm/spi/oracle/OracleEntityRepositoryImpl.java b/storm-oracle/src/main/java/st/orm/spi/oracle/OracleEntityRepositoryImpl.java index cb99bd542..d0828d734 100644 --- a/storm-oracle/src/main/java/st/orm/spi/oracle/OracleEntityRepositoryImpl.java +++ b/storm-oracle/src/main/java/st/orm/spi/oracle/OracleEntityRepositoryImpl.java @@ -209,13 +209,13 @@ public void upsert(@Nonnull E entity) { } validateUpsert(entity); var versionAware = new AtomicBoolean(); - try (var _ = intercept(sql -> sql.versionAware(versionAware.getPlain()))) { + intercept(sql -> sql.versionAware(versionAware.getPlain()), () -> { var query = ormTemplate.query(flatten(RAW.""" MERGE INTO \{table(model.type())} t USING (\{mergeSelect(entity)}) src ON (\{mergeOn()})\{mergeUpdate(versionAware)}\{mergeInsert()}""")); query.executeUpdate(); - } + }); } /** @@ -540,12 +540,12 @@ protected PreparedQuery prepareInsertQuery() { protected PreparedQuery prepareUpsertQuery() { var bindVars = ormTemplate.createBindVars(); var versionAware = new AtomicBoolean(); - try (var _ = intercept(sql -> sql.versionAware(versionAware.getPlain()))) { - return ormTemplate.query(flatten(RAW.""" + return intercept(sql -> sql.versionAware(versionAware.getPlain()), () -> + ormTemplate.query(flatten(RAW.""" MERGE INTO \{table(model.type())} t USING (\{mergeSelect(bindVars)}) src - ON (\{mergeOn()})\{mergeUpdate(versionAware)}\{mergeInsert()}""")).prepare(); - } + ON (\{mergeOn()})\{mergeUpdate(versionAware)}\{mergeInsert()}""") + ).prepare()); } protected void insertAndFetchIds(@Nonnull List batch, @Nonnull Supplier querySupplier, @Nullable BatchCallback callback) { diff --git a/storm-oracle/src/test/java/st/orm/spi/oracle/OracleEntityRepositoryTest.java b/storm-oracle/src/test/java/st/orm/spi/oracle/OracleEntityRepositoryTest.java index 426436bd9..d8f9fc1b5 100644 --- a/storm-oracle/src/test/java/st/orm/spi/oracle/OracleEntityRepositoryTest.java +++ b/storm-oracle/src/test/java/st/orm/spi/oracle/OracleEntityRepositoryTest.java @@ -41,6 +41,7 @@ import static org.springframework.test.util.AssertionErrors.assertNull; import static st.orm.template.Operator.EQUALS; import static st.orm.template.Operator.GREATER_THAN_OR_EQUAL; +import static st.orm.template.SqlInterceptor.observe; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = IntegrationConfig.class) @@ -112,9 +113,7 @@ public void testSelectLimit() { ORDER BY o.id FETCH FIRST 2 ROWS ONLY"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().orderBy(Metamodel.of(Owner.class, "id")).limit(2).getResultList(); assertEquals(2, entities.size()); assertEquals("Betty", entities.getFirst().firstName()); @@ -129,7 +128,7 @@ public void testSelectLimit() { assertEquals("Madison", entities.getLast().address().city()); assertEquals("6085551023", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -140,9 +139,7 @@ public void testSelectLimitOffset() { ORDER BY o.id OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().orderBy(Metamodel.of(Owner.class, "id")).offset(1).limit(2).getResultList(); assertEquals(2, entities.size()); assertEquals("George", entities.getFirst().firstName()); @@ -157,7 +154,7 @@ public void testSelectLimitOffset() { assertEquals("McFarland", entities.getLast().address().city()); assertEquals("6085558763", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -168,9 +165,7 @@ public void testSelectOffset() { ORDER BY o.id OFFSET 1 ROWS"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().orderBy(Metamodel.of(Owner.class, "id")).offset(1).getResultList(); assertEquals(9, entities.size()); assertEquals("George", entities.getFirst().firstName()); @@ -178,7 +173,7 @@ public void testSelectOffset() { assertEquals("110 W. Liberty St.", entities.getFirst().address().address()); assertEquals("Madison", entities.getFirst().address().city()); assertEquals("6085551023", entities.getFirst().telephone()); - } + }); } @Test @@ -188,7 +183,7 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -196,12 +191,12 @@ INSERT INTO vet (first_name, last_name) assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(Vet.builder().firstName("John").lastName("Doe").build()); assertTrue(entity.id() > 0); assertEquals("John", entity.firstName()); assertEquals("Doe", entity.lastName()); - } + }); } @Test @@ -211,7 +206,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) VALUES (?, ?, ?, ?, ?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -219,7 +214,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(Owner.builder().firstName("John").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build()); assertTrue(entity.id() > 0); assertEquals("John", entity.firstName()); @@ -228,7 +223,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sunnyvale", entity.address().city()); assertNull("telephone", entity.telephone()); assertEquals(0, entity.version()); - } + }); } @Test @@ -238,14 +233,14 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) VALUES (?, ?, ?, ?, ?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( Owner.builder().firstName("John").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build(), Owner.builder().firstName("Jane").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build() @@ -263,7 +258,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sunnyvale", entities.getLast().address().city()); assertNull("telephone", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -273,14 +268,14 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( Vet.builder().firstName("John").lastName("Doe").build(), Vet.builder().firstName("Jane").lastName("Doe").build() @@ -290,7 +285,7 @@ INSERT INTO vet (first_name, last_name) assertEquals("Doe", entities.getFirst().lastName()); assertEquals("Jane", entities.getLast().firstName()); assertEquals("Doe", entities.getLast().lastName()); - } + }); } @Test @@ -302,7 +297,7 @@ public void testUpdateAndFetchInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -315,7 +310,7 @@ public void testUpdateAndFetchInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { var update = repo.updateAndFetch(entity.toBuilder().lastName("Smith").build()); assertEquals("Betty", update.firstName()); assertEquals("Smith", update.lastName()); @@ -323,7 +318,7 @@ public void testUpdateAndFetchInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -335,14 +330,14 @@ public void testUpdateAndFetchInlineVersionBatch() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entities = repo.findAllById(List.of(1, 2)); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertTrue(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var updates = repo.updateAndFetch( entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList() ).stream().sorted(Comparator.comparingInt(Entity::id)).toList(); @@ -359,7 +354,7 @@ public void testUpdateAndFetchInlineVersionBatch() { assertEquals("Madison", updates.getLast().address().city()); assertEquals("6085551023", updates.getLast().telephone()); assertEquals(1, updates.getLast().version()); - } + }); } @Test @@ -370,14 +365,14 @@ public void testUpdateAndFetchBatch() { WHERE id = ?"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( Vet.builder().id(1).firstName("John").lastName("Doe").build(), Vet.builder().id(2).firstName("Jane").lastName("Doe").build() @@ -387,7 +382,7 @@ public void testUpdateAndFetchBatch() { assertEquals("Doe", entities.getFirst().lastName()); assertEquals("Jane", entities.getLast().firstName()); assertEquals("Doe", entities.getLast().lastName()); - } + }); } @Test @@ -397,15 +392,13 @@ public void testUpsert() { INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(Vet.builder().firstName("John").lastName("Doe").build()); - } + }, () -> repo.upsert(Vet.builder().firstName("John").lastName("Doe").build())); var entity = repo.select().where(Metamodel.of(Vet.class, "firstName"), EQUALS, "John").getSingleResult(); repo.upsert(entity.toBuilder().lastName("Smith").build()); var updated = repo.select().where(Metamodel.of(Vet.class, "firstName"), EQUALS, "John").getSingleResult(); @@ -421,16 +414,14 @@ public void testUpsertBatch() { INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); - })) { - repo.upsert(List.of( - Vet.builder().firstName("John").lastName("Doe").build(), - Vet.builder().firstName("Jane").lastName("Doe").build())); - } + }, () -> repo.upsert(List.of( + Vet.builder().firstName("John").lastName("Doe").build(), + Vet.builder().firstName("Jane").lastName("Doe").build()))); var entities = repo.select().where(Metamodel.of(Vet.class, "lastName"), EQUALS, "Doe").getResultList(); repo.upsert(entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList()); var updated = repo.select().where(Metamodel.of(Vet.class, "lastName"), EQUALS, "Smith").getResultList(); @@ -449,7 +440,7 @@ public void testUpsertInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -462,7 +453,7 @@ public void testUpsertInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { repo.upsert(entity.toBuilder().lastName("Smith").build()); var update = repo.getById(1); assertEquals("Betty", update.firstName()); @@ -471,7 +462,7 @@ public void testUpsertInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -483,7 +474,7 @@ public void testUpsertAndFetchInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -496,7 +487,7 @@ public void testUpsertAndFetchInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { var update = repo.upsertAndFetch(entity.toBuilder().lastName("Smith").build()); assertEquals("Betty", update.firstName()); assertEquals("Smith", update.lastName()); @@ -504,7 +495,7 @@ public void testUpsertAndFetchInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -516,7 +507,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -527,7 +518,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sun Prairie", sql.parameters().get(3).dbValue()); assertEquals("6085551749", sql.parameters().get(4).dbValue()); } - })) { + }, () -> { var insert = repo.upsertAndFetch(entity.toBuilder() .id(0) // Default value. .lastName("Smith").build()); @@ -538,7 +529,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sun Prairie", insert.address().city()); assertEquals("6085551749", insert.telephone()); assertEquals(0, insert.version()); - } + }); } @Test @@ -550,14 +541,14 @@ public void testUpsertInlineVersionBatch() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entities = repo.findAllById(List.of(1, 2)); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertTrue(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { repo.upsert( entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList() ); @@ -575,7 +566,7 @@ public void testUpsertInlineVersionBatch() { assertEquals("Madison", updates.getLast().address().city()); assertEquals("6085551023", updates.getLast().telephone()); assertEquals(1, updates.getLast().version()); - } + }); } @@ -594,15 +585,13 @@ public void testUpsertUniqueKey() { INSERT INTO pet_type (name, description) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(PetType.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertEquals("dragon", sql.parameters().get(0).dbValue()); assertEquals("description", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(PetType.builder().name("dragon").description("description").build()); - } + }, () -> repo.upsert(PetType.builder().name("dragon").description("description").build())); var entity = repo.select().where(Metamodel.of(PetType.class, "name"), EQUALS, "dragon").getSingleResult(); assertEquals("description", entity.description()); var e = assertThrows(PersistenceException.class, () -> repo.upsert(PetType.builder().name("dragon").description("description").build())); @@ -627,15 +616,13 @@ public void testUpsertNonAutoGenerated() { INSERT (id, name) VALUES (src.id, src.name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertEquals(4, sql.parameters().get(0).dbValue()); assertEquals("anaesthetics", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(Specialty.builder().id(4).name("anaesthetics").build()); - } + }, () -> repo.upsert(Specialty.builder().id(4).name("anaesthetics").build())); var entity = repo.select().where(Metamodel.of(Specialty.class, "name"), EQUALS, "anaesthetics").getSingleResult(); repo.upsert(entity.toBuilder().name("anaesthetist").build()); var updated = repo.select().where(Metamodel.of(Specialty.class, "name"), EQUALS, "anaesthetist").getSingleResult(); @@ -656,7 +643,7 @@ public void testUpsertAndFetchNonAutoGenerated() { VALUES (src.id, src.name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -664,12 +651,12 @@ public void testUpsertAndFetchNonAutoGenerated() { assertEquals(4, sql.parameters().get(0).dbValue()); assertEquals("anaesthetics", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.upsertAndFetch(Specialty.builder().id(4).name("anaesthetics").build()); var updated = repo.upsertAndFetch(entity.toBuilder().name("anaesthetist").build()); assertEquals(entity.id(), updated.id()); assertEquals("anaesthetist", updated.name()); - } + }); } @Test @@ -684,16 +671,14 @@ public void testUpsertNonAutoGeneratedBatch() { INSERT (id, name) VALUES (src.id, src.name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); - })) { - repo.upsert(List.of( - Specialty.builder().id(4).name("anaesthetics").build(), - Specialty.builder().id(5).name("nurse").build())); - } + }, () -> repo.upsert(List.of( + Specialty.builder().id(4).name("anaesthetics").build(), + Specialty.builder().id(5).name("nurse").build()))); var entities = repo.select().where(Metamodel.of(Specialty.class, "id"), GREATER_THAN_OR_EQUAL, 4).getResultList(); repo.upsert(entities.stream().map(e -> e.toBuilder().name(STR."\{e.name()}s").build()).toList()); var updated = repo.select().where(Metamodel.of(Specialty.class, "id"), GREATER_THAN_OR_EQUAL, 4).getResultList(); @@ -714,21 +699,21 @@ public void testUpsertAndFetchNonAutoGeneratedBatch() { VALUES (src.id, src.name)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( Specialty.builder().id(4).name("anaesthetics").build(), Specialty.builder().id(5).name("nurse").build())); var updated = repo.upsertAndFetch(entities.stream().map(e -> e.toBuilder().name(STR."\{e.name()}s").build()).toList()); assertEquals(2, updated.size()); assertTrue(updated.stream().allMatch(entity -> entity.name().endsWith("s"))); - } + }); } @Builder(toBuilder = true) @@ -755,7 +740,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -763,11 +748,11 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(1, sql.parameters().get(0).dbValue()); assertEquals(2, sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).build()); assertEquals(1, entity.id().vetId()); assertEquals(2, entity.id().specialtyId()); - } + }); } @Test @@ -777,14 +762,14 @@ INSERT INTO vet_specialty (vet_id, specialty_id) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(6).specialtyId(3).build()).build() @@ -794,7 +779,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(2, entities.getFirst().id().specialtyId()); assertEquals(6, entities.getLast().id().vetId()); assertEquals(3, entities.getLast().id().specialtyId()); - } + }); } @Test @@ -808,14 +793,14 @@ public void testUpsertAndFetchBatchNewCompoundPk() { VALUES (src.vet_id, src.specialty_id)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).vet(Vet.builder().id(1).build()).specialty(Specialty.builder().id(2).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(6).specialtyId(3).build()).vet(Vet.builder().id(6).build()).specialty(Specialty.builder().id(3).build()).build() @@ -825,7 +810,7 @@ public void testUpsertAndFetchBatchNewCompoundPk() { assertEquals(2, entities.getFirst().id().specialtyId()); assertEquals(6, entities.getLast().id().vetId()); assertEquals(3, entities.getLast().id().specialtyId()); - } + }); } @Test @@ -839,14 +824,14 @@ public void testUpsertAndFetchBatchExistingCompoundPk() { VALUES (src.vet_id, src.specialty_id)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(2).specialtyId(1).build()).vet(Vet.builder().id(1).build()).specialty(Specialty.builder().id(1).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(3).specialtyId(2).build()).vet(Vet.builder().id(3).build()).specialty(Specialty.builder().id(2).build()).build() @@ -856,6 +841,6 @@ public void testUpsertAndFetchBatchExistingCompoundPk() { assertEquals(1, entities.getFirst().id().specialtyId()); assertEquals(3, entities.getLast().id().vetId()); assertEquals(2, entities.getLast().id().specialtyId()); - } + }); } } diff --git a/storm-postgresql/pom.xml b/storm-postgresql/pom.xml index d07b62666..0383551ef 100644 --- a/storm-postgresql/pom.xml +++ b/storm-postgresql/pom.xml @@ -6,7 +6,7 @@ st.orm storm-framework - 1.3.2 + 1.3.3 ../pom.xml storm-postgresql diff --git a/storm-postgresql/src/main/java/st/orm/spi/postgresql/PostgreSQLEntityRepositoryImpl.java b/storm-postgresql/src/main/java/st/orm/spi/postgresql/PostgreSQLEntityRepositoryImpl.java index 39b901372..bfd11a985 100644 --- a/storm-postgresql/src/main/java/st/orm/spi/postgresql/PostgreSQLEntityRepositoryImpl.java +++ b/storm-postgresql/src/main/java/st/orm/spi/postgresql/PostgreSQLEntityRepositoryImpl.java @@ -134,12 +134,12 @@ public void upsert(@Nonnull E entity) { } validateUpsert(entity); var versionAware = new AtomicBoolean(); - try (var _ = intercept(sql -> sql.versionAware(versionAware.getPlain()))) { + intercept(sql -> sql.versionAware(versionAware.getPlain()), () -> { var query = ormTemplate.query(flatten(RAW.""" INSERT INTO \{model.type()} VALUES \{entity}\{onConflictClause(versionAware)}""")); query.executeUpdate(); - } + }); } /** @@ -153,20 +153,21 @@ public ID upsertAndFetchId(@Nonnull E entity) { } validateUpsert(entity); var versionAware = new AtomicBoolean(); - try (var _ = intercept(sql -> sql.versionAware(versionAware.getPlain())); - var query = ormTemplate.query(flatten(RAW.""" - INSERT INTO \{model.type()} - VALUES \{entity}\{onConflictClause(versionAware)}""")).prepare()) { - query.executeUpdate(); - if (autoGeneratedPrimaryKey) { - try (var stream = query.getGeneratedKeys(model.primaryKeyType())) { - return stream.reduce((_, __) -> { - throw new NonUniqueResultException("Expected single result, but found more than one."); - }).orElseThrow(() -> new NoResultException("Expected single result, but found none.")); + return intercept(sql -> sql.versionAware(versionAware.getPlain()), () -> { + try (var query = ormTemplate.query(flatten(RAW.""" + INSERT INTO \{model.type()} + VALUES \{entity}\{onConflictClause(versionAware)}""")).prepare()) { + query.executeUpdate(); + if (autoGeneratedPrimaryKey) { + try (var stream = query.getGeneratedKeys(model.primaryKeyType())) { + return stream.reduce((_, __) -> { + throw new NonUniqueResultException("Expected single result, but found more than one."); + }).orElseThrow(() -> new NoResultException("Expected single result, but found none.")); + } } + return entity.id(); } - return entity.id(); - } + }); } /** @@ -302,11 +303,11 @@ private Map> partition(@Nonnull List entities) { protected PreparedQuery prepareUpsertQuery() { var bindVars = ormTemplate.createBindVars(); var versionAware = new AtomicBoolean(); - try (var _ = intercept(sql -> sql.versionAware(versionAware.getPlain()))) { - return ormTemplate.query(flatten(RAW.""" + return intercept(sql -> sql.versionAware(versionAware.getPlain()), () -> + ormTemplate.query(flatten(RAW.""" INSERT INTO \{model.type()} - VALUES \{bindVars}\{onConflictClause(versionAware)}""")).prepare(); - } + VALUES \{bindVars}\{onConflictClause(versionAware)}""") + ).prepare()); } protected void upsertAndFetchIds(@Nonnull List batch, @Nonnull Supplier querySupplier, @Nullable BatchCallback callback) { diff --git a/storm-postgresql/src/test/java/st/orm/spi/postgresql/PostgreSQLEntityRepositoryTest.java b/storm-postgresql/src/test/java/st/orm/spi/postgresql/PostgreSQLEntityRepositoryTest.java index e5ad5bfa2..c3b708377 100644 --- a/storm-postgresql/src/test/java/st/orm/spi/postgresql/PostgreSQLEntityRepositoryTest.java +++ b/storm-postgresql/src/test/java/st/orm/spi/postgresql/PostgreSQLEntityRepositoryTest.java @@ -40,6 +40,7 @@ import static org.springframework.test.util.AssertionErrors.assertNull; import static st.orm.template.Operator.EQUALS; import static st.orm.template.Operator.GREATER_THAN_OR_EQUAL; +import static st.orm.template.SqlInterceptor.observe; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = IntegrationConfig.class) @@ -97,9 +98,7 @@ public void testSelectLimit() { FROM owner o LIMIT 2"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().limit(2).getResultList(); assertEquals(2, entities.size()); assertEquals("Betty", entities.getFirst().firstName()); @@ -114,7 +113,7 @@ public void testSelectLimit() { assertEquals("Madison", entities.getLast().address().city()); assertEquals("6085551023", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -125,9 +124,7 @@ public void testSelectLimitOffset() { ORDER BY o.id OFFSET 1 LIMIT 2"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().orderBy(Metamodel.of(Owner.class, "id")).offset(1).limit(2).getResultList(); assertEquals(2, entities.size()); assertEquals("George", entities.getFirst().firstName()); @@ -142,7 +139,7 @@ public void testSelectLimitOffset() { assertEquals("McFarland", entities.getLast().address().city()); assertEquals("6085558763", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -153,9 +150,7 @@ public void testSelectOffset() { ORDER BY o.id OFFSET 1"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); - try (var _ = SqlInterceptor.consume(sql -> { - assertEquals(expectedSql, sql.statement()); - })) { + observe(sql -> assertEquals(expectedSql, sql.statement()), () -> { var entities = repo.select().orderBy(Metamodel.of(Owner.class, "id")).offset(1).getResultList(); assertEquals(9, entities.size()); assertEquals("George", entities.getFirst().firstName()); @@ -163,7 +158,7 @@ public void testSelectOffset() { assertEquals("110 W. Liberty St.", entities.getFirst().address().address()); assertEquals("Madison", entities.getFirst().address().city()); assertEquals("6085551023", entities.getFirst().telephone()); - } + }); } @Test @@ -173,7 +168,7 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -181,12 +176,12 @@ INSERT INTO vet (first_name, last_name) assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(Vet.builder().firstName("John").lastName("Doe").build()); assertTrue(entity.id() > 0); assertEquals("John", entity.firstName()); assertEquals("Doe", entity.lastName()); - } + }); } @Test @@ -196,7 +191,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) VALUES (?, ?, ?, ?, ?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -204,7 +199,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(Owner.builder().firstName("John").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build()); assertTrue(entity.id() > 0); assertEquals("John", entity.firstName()); @@ -213,7 +208,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sunnyvale", entity.address().city()); assertNull("telephone", entity.telephone()); assertEquals(0, entity.version()); - } + }); } @Test @@ -223,14 +218,14 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) VALUES (?, ?, ?, ?, ?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( Owner.builder().firstName("John").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build(), Owner.builder().firstName("Jane").lastName("Doe").address(Address.builder().address("243 Acalanes Dr").city("Sunnyvale").build()).build() @@ -248,7 +243,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sunnyvale", entities.getLast().address().city()); assertNull("telephone", entities.getLast().telephone()); assertEquals(0, entities.getLast().version()); - } + }); } @Test @@ -258,14 +253,14 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( Vet.builder().firstName("John").lastName("Doe").build(), Vet.builder().firstName("Jane").lastName("Doe").build() @@ -275,7 +270,7 @@ INSERT INTO vet (first_name, last_name) assertEquals("Doe", entities.getFirst().lastName()); assertEquals("Jane", entities.getLast().firstName()); assertEquals("Doe", entities.getLast().lastName()); - } + }); } @Test @@ -287,7 +282,7 @@ public void testUpdateAndFetchInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -300,7 +295,7 @@ public void testUpdateAndFetchInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { var update = repo.updateAndFetch(entity.toBuilder().lastName("Smith").build()); assertEquals("Betty", update.firstName()); assertEquals("Smith", update.lastName()); @@ -308,7 +303,7 @@ public void testUpdateAndFetchInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -320,14 +315,14 @@ public void testUpdateAndFetchInlineVersionBatch() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entities = repo.findAllById(List.of(1, 2)); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertTrue(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var updates = repo.updateAndFetch( entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList() ).stream().sorted(Comparator.comparingInt(Entity::id)).toList(); @@ -344,7 +339,7 @@ public void testUpdateAndFetchInlineVersionBatch() { assertEquals("Madison", updates.getLast().address().city()); assertEquals("6085551023", updates.getLast().telephone()); assertEquals(1, updates.getLast().version()); - } + }); } @Test @@ -355,14 +350,14 @@ public void testUpdateAndFetchBatch() { WHERE id = ?"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( Vet.builder().id(1).firstName("John").lastName("Doe").build(), Vet.builder().id(2).firstName("Jane").lastName("Doe").build() @@ -372,7 +367,7 @@ public void testUpdateAndFetchBatch() { assertEquals("Doe", entities.getFirst().lastName()); assertEquals("Jane", entities.getLast().firstName()); assertEquals("Doe", entities.getLast().lastName()); - } + }); } @Test @@ -382,15 +377,13 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?) ON CONFLICT (id) DO UPDATE SET first_name = EXCLUDED.first_name, last_name = EXCLUDED.last_name"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertEquals("John", sql.parameters().get(0).dbValue()); assertEquals("Doe", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(Vet.builder().firstName("John").lastName("Doe").build()); - } + }, () -> repo.upsert(Vet.builder().firstName("John").lastName("Doe").build())); var entity = repo.select().where(Metamodel.of(Vet.class, "firstName"), EQUALS, "John").getSingleResult(); repo.upsert(entity.toBuilder().lastName("Smith").build()); var updated = repo.select().where(Metamodel.of(Vet.class, "firstName"), EQUALS, "John").getSingleResult(); @@ -406,16 +399,14 @@ INSERT INTO vet (first_name, last_name) VALUES (?, ?) ON CONFLICT (id) DO UPDATE SET first_name = EXCLUDED.first_name, last_name = EXCLUDED.last_name"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Vet.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); - })) { - repo.upsert(List.of( - Vet.builder().firstName("John").lastName("Doe").build(), - Vet.builder().firstName("Jane").lastName("Doe").build())); - } + }, () -> repo.upsert(List.of( + Vet.builder().firstName("John").lastName("Doe").build(), + Vet.builder().firstName("Jane").lastName("Doe").build()))); var entities = repo.select().where(Metamodel.of(Vet.class, "lastName"), EQUALS, "Doe").getResultList(); repo.upsert(entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList()); var updated = repo.select().where(Metamodel.of(Vet.class, "lastName"), EQUALS, "Smith").getResultList(); @@ -434,7 +425,7 @@ public void testUpsertInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -447,7 +438,7 @@ public void testUpsertInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { repo.upsert(entity.toBuilder().lastName("Smith").build()); var update = repo.getById(1); assertEquals("Betty", update.firstName()); @@ -456,7 +447,7 @@ public void testUpsertInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -468,7 +459,7 @@ public void testUpsertAndFetchInlineVersion() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -481,7 +472,7 @@ public void testUpsertAndFetchInlineVersion() { assertEquals(1, sql.parameters().get(5).dbValue()); assertEquals(0, sql.parameters().get(6).dbValue()); } - })) { + }, () -> { var update = repo.upsertAndFetch(entity.toBuilder().lastName("Smith").build()); assertEquals("Betty", update.firstName()); assertEquals("Smith", update.lastName()); @@ -489,7 +480,7 @@ public void testUpsertAndFetchInlineVersion() { assertEquals("Sun Prairie", update.address().city()); assertEquals("6085551749", update.telephone()); assertEquals(1, update.version()); - } + }); } @Test @@ -501,7 +492,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entity = repo.getById(1); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); @@ -512,7 +503,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sun Prairie", sql.parameters().get(3).dbValue()); assertEquals("6085551749", sql.parameters().get(4).dbValue()); } - })) { + }, () -> { var insert = repo.upsertAndFetch(entity.toBuilder() .id(0) // Default value. .lastName("Smith").build()); @@ -523,7 +514,7 @@ INSERT INTO owner (first_name, last_name, address, city, telephone, version) assertEquals("Sun Prairie", insert.address().city()); assertEquals("6085551749", insert.telephone()); assertEquals(0, insert.version()); - } + }); } @Test @@ -535,14 +526,14 @@ public void testUpsertInlineVersionBatch() { var repo = PreparedStatementTemplate.ORM(dataSource).entity(Owner.class); var entities = repo.findAllById(List.of(1, 2)); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertTrue(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { repo.upsert( entities.stream().map(entity -> entity.toBuilder().lastName("Smith").build()).toList() ); @@ -560,7 +551,7 @@ public void testUpsertInlineVersionBatch() { assertEquals("Madison", updates.getLast().address().city()); assertEquals("6085551023", updates.getLast().telephone()); assertEquals(1, updates.getLast().version()); - } + }); } @Builder(toBuilder = true) @@ -579,15 +570,13 @@ INSERT INTO pet_type (name, description) VALUES (?, ?) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, description = EXCLUDED.description"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(PetType.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of("id")); assertFalse(sql.versionAware()); assertEquals("dragon", sql.parameters().get(0).dbValue()); assertEquals("description", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(PetType.builder().name("dragon").description("description").build()); - } + }, () -> repo.upsert(PetType.builder().name("dragon").description("description").build())); var entity = repo.select().where(Metamodel.of(PetType.class, "name"), EQUALS, "dragon").getSingleResult(); assertEquals("description", entity.description()); var e = assertThrows(PersistenceException.class, () -> repo.upsert(PetType.builder().name("dragon").description("description").build())); @@ -607,15 +596,13 @@ INSERT INTO specialty (id, name) VALUES (?, ?) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertEquals(4, sql.parameters().get(0).dbValue()); assertEquals("anaesthetics", sql.parameters().get(1).dbValue()); - })) { - repo.upsert(Specialty.builder().id(4).name("anaesthetics").build()); - } + }, () -> repo.upsert(Specialty.builder().id(4).name("anaesthetics").build())); var entity = repo.select().where(Metamodel.of(Specialty.class, "name"), EQUALS, "anaesthetics").getSingleResult(); repo.upsert(entity.toBuilder().name("anaesthetist").build()); var updated = repo.select().where(Metamodel.of(Specialty.class, "name"), EQUALS, "anaesthetist").getSingleResult(); @@ -631,7 +618,7 @@ INSERT INTO specialty (id, name) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -639,12 +626,12 @@ INSERT INTO specialty (id, name) assertEquals(4, sql.parameters().get(0).dbValue()); assertEquals("anaesthetics", sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.upsertAndFetch(Specialty.builder().id(4).name("anaesthetics").build()); var updated = repo.upsertAndFetch(entity.toBuilder().name("anaesthetist").build()); assertEquals(entity.id(), updated.id()); assertEquals("anaesthetist", updated.name()); - } + }); } @Test @@ -654,16 +641,14 @@ INSERT INTO specialty (id, name) VALUES (?, ?) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); - try (SqlInterceptor _ = SqlInterceptor.consume(sql -> { + observe(sql -> { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); - })) { - repo.upsert(List.of( - Specialty.builder().id(4).name("anaesthetics").build(), - Specialty.builder().id(5).name("nurse").build())); - } + }, () -> repo.upsert(List.of( + Specialty.builder().id(4).name("anaesthetics").build(), + Specialty.builder().id(5).name("nurse").build()))); var entities = repo.select().where(Metamodel.of(Specialty.class, "id"), GREATER_THAN_OR_EQUAL, 4).getResultList(); repo.upsert(entities.stream().map(e -> e.toBuilder().name(STR."\{e.name()}s").build()).toList()); var updated = repo.select().where(Metamodel.of(Specialty.class, "id"), GREATER_THAN_OR_EQUAL, 4).getResultList(); @@ -679,21 +664,21 @@ INSERT INTO specialty (id, name) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(Specialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( Specialty.builder().id(4).name("anaesthetics").build(), Specialty.builder().id(5).name("nurse").build())); var updated = repo.upsertAndFetch(entities.stream().map(e -> e.toBuilder().name(STR."\{e.name()}s").build()).toList()); assertEquals(2, updated.size()); assertTrue(updated.stream().allMatch(entity -> entity.name().endsWith("s"))); - } + }); } @Builder(toBuilder = true) @@ -720,7 +705,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); @@ -728,11 +713,11 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(1, sql.parameters().get(0).dbValue()); assertEquals(2, sql.parameters().get(1).dbValue()); } - })) { + }, () -> { var entity = repo.insertAndFetch(VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).build()); assertEquals(1, entity.id().vetId()); assertEquals(2, entity.id().specialtyId()); - } + }); } @Test @@ -742,14 +727,14 @@ INSERT INTO vet_specialty (vet_id, specialty_id) VALUES (?, ?)"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.insertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(6).specialtyId(3).build()).build() @@ -759,7 +744,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(2, entities.getFirst().id().specialtyId()); assertEquals(6, entities.getLast().id().vetId()); assertEquals(3, entities.getLast().id().specialtyId()); - } + }); } @Test @@ -770,14 +755,14 @@ INSERT INTO vet_specialty (vet_id, specialty_id) ON CONFLICT (vet_id, specialty_id) DO NOTHING"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(1).specialtyId(2).build()).vet(Vet.builder().id(1).build()).specialty(Specialty.builder().id(2).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(6).specialtyId(3).build()).vet(Vet.builder().id(6).build()).specialty(Specialty.builder().id(3).build()).build() @@ -787,7 +772,7 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(2, entities.getFirst().id().specialtyId()); assertEquals(6, entities.getLast().id().vetId()); assertEquals(3, entities.getLast().id().specialtyId()); - } + }); } @Test @@ -798,14 +783,14 @@ INSERT INTO vet_specialty (vet_id, specialty_id) ON CONFLICT (vet_id, specialty_id) DO NOTHING"""; var repo = PreparedStatementTemplate.ORM(dataSource).entity(VetSpecialty.class); var first = new AtomicBoolean(false); - try (var _ = SqlInterceptor.consume(sql -> { + observe(sql -> { if (!first.getAndSet(true)) { assertEquals(expectedSql, sql.statement()); assertEquals(sql.generatedKeys(), List.of()); assertFalse(sql.versionAware()); assertTrue(sql.bindVariables().isPresent()); } - })) { + }, () -> { var entities = repo.upsertAndFetch(List.of( VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(2).specialtyId(1).build()).vet(Vet.builder().id(1).build()).specialty(Specialty.builder().id(1).build()).build(), VetSpecialty.builder().id(VetSpecialtyPK.builder().vetId(3).specialtyId(2).build()).vet(Vet.builder().id(3).build()).specialty(Specialty.builder().id(2).build()).build() @@ -815,6 +800,6 @@ INSERT INTO vet_specialty (vet_id, specialty_id) assertEquals(1, entities.getFirst().id().specialtyId()); assertEquals(3, entities.getLast().id().vetId()); assertEquals(2, entities.getLast().id().specialtyId()); - } + }); } } diff --git a/storm-spring/pom.xml b/storm-spring/pom.xml index 957f7752b..70806bed5 100644 --- a/storm-spring/pom.xml +++ b/storm-spring/pom.xml @@ -6,7 +6,7 @@ st.orm storm-framework - 1.3.2 + 1.3.3 ../pom.xml storm-spring diff --git a/storm-spring/src/main/java/module-info.java b/storm-spring/src/main/java/module-info.java index 84c6644e6..0d9be14a6 100644 --- a/storm-spring/src/main/java/module-info.java +++ b/storm-spring/src/main/java/module-info.java @@ -8,5 +8,7 @@ requires spring.boot; requires jakarta.annotation; requires org.reflections; + requires spring.core; + requires spring.aop; exports st.orm.spring; } \ No newline at end of file diff --git a/storm-spring/src/main/java/st/orm/spring/RepositoryProxyingPostProcessor.java b/storm-spring/src/main/java/st/orm/spring/RepositoryProxyingPostProcessor.java new file mode 100644 index 000000000..3308956a2 --- /dev/null +++ b/storm-spring/src/main/java/st/orm/spring/RepositoryProxyingPostProcessor.java @@ -0,0 +1,47 @@ +/* + * Copyright 2024 - 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package st.orm.spring; + +import jakarta.annotation.Nonnull; +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Configuration; +import st.orm.kotlin.repository.KRepository; +import st.orm.repository.Repository; + +import java.util.Set; + +import static java.util.Arrays.stream; + +@Configuration +public class RepositoryProxyingPostProcessor implements BeanPostProcessor { + + private final Set> repositoryInterfaces = Set.of( + Repository.class, + KRepository.class + ); + + @Override + public Object postProcessAfterInitialization(@Nonnull Object bean, @Nonnull String beanName) { + if (stream(bean.getClass().getInterfaces()) + .anyMatch(repositoryInterfaces::contains)) { + ProxyFactory factory = new ProxyFactory(bean); + factory.setProxyTargetClass(true); + return factory.getProxy(); + } + return bean; + } +} \ No newline at end of file diff --git a/storm/pom.xml b/storm/pom.xml index aee7d26d8..93dd2f836 100644 --- a/storm/pom.xml +++ b/storm/pom.xml @@ -6,7 +6,7 @@ st.orm storm-framework - 1.3.2 + 1.3.3 ../pom.xml storm @@ -28,51 +28,25 @@ org.apache.maven.plugins maven-compiler-plugin - - - default-compile - none - - - default-testCompile - none - - - compile - compile - - compile - - - - testCompile - test-compile - - testCompile - - - - - - org.projectlombok - lombok - 1.18.30 - - - st.orm - storm-metamodel-processor - ${project.version} - - - - - ${java.version} ${java.version} ${java.version} - --enable-preview + + --enable-preview + + + + org.projectlombok + lombok + 1.18.30 + + + st.orm + storm-metamodel-processor + ${project.version} + + diff --git a/storm/src/main/java/st/orm/template/SqlInterceptor.java b/storm/src/main/java/st/orm/template/SqlInterceptor.java index 97e0a671d..f78cf58b1 100644 --- a/storm/src/main/java/st/orm/template/SqlInterceptor.java +++ b/storm/src/main/java/st/orm/template/SqlInterceptor.java @@ -18,7 +18,9 @@ import jakarta.annotation.Nonnull; import st.orm.template.impl.SqlInterceptorManager; +import java.util.concurrent.Callable; import java.util.function.Consumer; +import java.util.function.Supplier; import java.util.function.UnaryOperator; /** @@ -30,15 +32,15 @@ * * @since 1.1 */ -public interface SqlInterceptor extends AutoCloseable { +public interface SqlInterceptor { /** - * Register a global consumer that will be called for all SQL statements being generated. + * Register a global observer that will be called for all SQL statements being generated. * - * @param consumer the consumer to call for each SQL statement. + * @param observer the observer to call for each SQL statement. */ - static void registerGlobalConsumer(@Nonnull Consumer consumer) { - SqlInterceptorManager.registerGlobalConsumer(consumer); + static void registerGlobalObserver(@Nonnull Consumer observer) { + SqlInterceptorManager.registerGlobalObserver(observer); } /** @@ -51,12 +53,12 @@ static void registerGlobalInterceptor(@Nonnull UnaryOperator interceptor) { } /** - * Unregister a global consumer. + * Unregister a global observer. * - * @param consumer the consumer to unregister. + * @param observer the observer to unregister. */ - static void unregisterGlobalConsumer(@Nonnull Consumer consumer) { - SqlInterceptorManager.unregisterGlobalConsumer(consumer); + static void unregisterGlobalObserver(@Nonnull Consumer observer) { + SqlInterceptorManager.unregisterGlobalObserver(observer); } /** @@ -65,36 +67,96 @@ static void unregisterGlobalConsumer(@Nonnull Consumer consumer) { * @param interceptor the interceptor to unregister. */ static void unregisterGlobalInterceptor(@Nonnull UnaryOperator interceptor) { - SqlInterceptorManager.unregisterGlobalConsumer(interceptor); + SqlInterceptorManager.unregisterGlobalObserver(interceptor); } /** - * Create a new interceptor that will be called for SQL statements generated 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. + * Executes a {@code Runnable} action within the context of an SQL observer, which consumes SQL statements. * - * @param consumer the consumer to call for each SQL statement. - * @return the interceptor. + *

This observer sees only SQL statements generated within the scope of this {@code runnable} and its child + * threads.

+ * + * @param observer the consumer invoked for each SQL statement. + * @param runnable the action to execute. + */ + static void observe(@Nonnull Consumer observer, Runnable runnable) { + SqlInterceptorManager.intercept(observer).run(runnable); + } + + /** + * Executes a {@code Supplier} within the context of an SQL observer, returning its result. + * + *

This observer sees only SQL statements generated within the scope of this {@code supplier} and its child + * threads.

+ * + * @param observer the consumer invoked for each SQL statement. + * @param supplier the action supplying the result. + * @param the type of the supplied result. + * @return the result of the supplied action. */ - static SqlInterceptor consume(@Nonnull Consumer consumer) { - return SqlInterceptorManager.create(consumer); + static T observe(@Nonnull Consumer observer, @Nonnull Supplier supplier) { + return SqlInterceptorManager.intercept(observer).get(supplier); } /** - * Create a new interceptor that will be called for SQL statements generated 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. + * Executes a {@code Callable} action within the context of an SQL observer, returning its result and potentially + * throwing exceptions. + * + *

This observer sees only SQL statements generated within the scope of this {@code callable} and its child + * threads.

* - * @param interceptor the consumer to call for each SQL statement. - * @return the interceptor. + * @param observer the consumer invoked for each SQL statement. + * @param callable the action supplying the result and potentially throwing exceptions. + * @param the type of the result. + * @return the result of the callable action. + * @throws Exception if the callable action throws an exception. */ - static SqlInterceptor intercept(@Nonnull UnaryOperator interceptor) { - return SqlInterceptorManager.create(interceptor); + static T observeThrowing(@Nonnull Consumer observer, @Nonnull Callable callable) throws Exception { + return SqlInterceptorManager.intercept(observer).call(callable); } /** - * Close the interceptor. + * Executes a {@code Runnable} action within the context of an SQL interceptor, which modifies SQL statements. + * + *

This interceptor sees only SQL statements generated within the scope of this {@code runnable} and its child + * threads.

+ * + * @param interceptor the operator applied to each SQL statement. + * @param runnable the action to execute. */ - @Override - void close(); + static void intercept(@Nonnull UnaryOperator interceptor, @Nonnull Runnable runnable) { + SqlInterceptorManager.intercept(interceptor).run(runnable); + } + + /** + * Executes a {@code Supplier} within the context of an SQL interceptor, returning its result. + * + *

This interceptor sees only SQL statements generated within the scope of this {@code supplier} and its child + * threads.

+ * + * @param interceptor the operator applied to each SQL statement. + * @param supplier the action supplying the result. + * @param the type of the supplied result. + * @return the result of the supplied action. + */ + static T intercept(@Nonnull UnaryOperator interceptor, @Nonnull Supplier supplier) { + return SqlInterceptorManager.intercept(interceptor).get(supplier); + } + + /** + * Executes a {@code Callable} action within the context of an SQL interceptor, returning its result and potentially + * throwing exceptions. + * + *

This interceptor sees only SQL statements generated within the scope of this {@code callable} and its child + * threads.

+ * + * @param interceptor the operator applied to each SQL statement. + * @param callable the action supplying the result and potentially throwing exceptions. + * @param the type of the result. + * @return the result of the callable action. + * @throws Exception if the callable action throws an exception. + */ + static T interceptThrowing(@Nonnull UnaryOperator interceptor, @Nonnull Callable callable) throws Exception { + return SqlInterceptorManager.intercept(interceptor).call(callable); + } } diff --git a/storm/src/main/java/st/orm/template/impl/ORMTemplateImpl.java b/storm/src/main/java/st/orm/template/impl/ORMTemplateImpl.java index 4c4bc278d..ec919acba 100644 --- a/storm/src/main/java/st/orm/template/impl/ORMTemplateImpl.java +++ b/storm/src/main/java/st/orm/template/impl/ORMTemplateImpl.java @@ -44,7 +44,7 @@ import static java.lang.reflect.Proxy.newProxyInstance; import static st.orm.spi.Providers.getEntityRepository; import static st.orm.spi.Providers.getProjectionRepository; -import static st.orm.template.SqlInterceptor.consume; +import static st.orm.template.SqlInterceptor.observeThrowing; public final class ORMTemplateImpl extends QueryTemplateImpl implements ORMTemplate { @@ -234,14 +234,16 @@ private static void getAllInterfaces(Class clazz, Set> interfacesFou private T wrapRepository(@Nonnull T repository) { return (T) newProxyInstance(repository.getClass().getClassLoader(), getAllInterfaces(repository.getClass()).toArray(new Class[0]), (_, method, args) -> { var lastSql = new AtomicReference(); - try (var _ = consume(lastSql::setPlain)) { - try { - return method.invoke(repository, args); - } catch (Exception | Error e) { - throw e; - } catch (Throwable e) { - throw new PersistenceException(e); - } + try { + return observeThrowing(lastSql::setPlain, () -> { + try { + return method.invoke(repository, args); + } catch (Exception | Error e) { + throw e; + } catch (Throwable e) { + throw new PersistenceException(e); + } + }); } catch (InvocationTargetException e) { try { throw e.getTargetException(); diff --git a/storm/src/main/java/st/orm/template/impl/SqlInterceptorManager.java b/storm/src/main/java/st/orm/template/impl/SqlInterceptorManager.java index f6b0e469a..3400aadaf 100644 --- a/storm/src/main/java/st/orm/template/impl/SqlInterceptorManager.java +++ b/storm/src/main/java/st/orm/template/impl/SqlInterceptorManager.java @@ -16,19 +16,23 @@ package st.orm.template.impl; import jakarta.annotation.Nonnull; +import st.orm.PersistenceException; import st.orm.template.Sql; -import st.orm.template.SqlInterceptor; +import java.lang.ScopedValue.Carrier; +import java.util.ArrayList; +import java.util.ConcurrentModificationException; import java.util.IdentityHashMap; -import java.util.LinkedHashSet; -import java.util.SequencedSet; +import java.util.List; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Consumer; import java.util.function.UnaryOperator; +import static java.lang.ScopedValue.newInstance; import static java.util.Collections.newSetFromMap; +import static java.util.Collections.synchronizedList; /** * Manages SQL interceptors. @@ -40,8 +44,7 @@ public final class SqlInterceptorManager { private static final ReadWriteLock LOCK = new ReentrantReadWriteLock(); private static final Set GLOBAL_OPERATORS = newSetFromMap(new IdentityHashMap<>()); - private static final ThreadLocal>> LOCAL_OPERATORS = - ThreadLocal.withInitial(LinkedHashSet::new); + private static final ScopedValue>> LOCAL_OPERATORS = newInstance(); private SqlInterceptorManager() { } @@ -62,110 +65,101 @@ public static void registerGlobalInterceptor(@Nonnull UnaryOperator interce /** - * Register a global consumer that will be called for all SQL statements. + * Register a global observer that will be called for all SQL statements. * - * @param consumer the consumer to call for each SQL statement. + * @param observer the observer to call for each SQL statement. */ - public static void registerGlobalConsumer(@Nonnull Consumer consumer) { + public static void registerGlobalObserver(@Nonnull Consumer observer) { LOCK.writeLock().lock(); try { - GLOBAL_OPERATORS.add(consumer); + GLOBAL_OPERATORS.add(observer); } finally { LOCK.writeLock().unlock(); } } /** - * Unregister a global consumer. + * Unregister a global observer. * - * @param consumer the consumer to unregister. + * @param observer the observer to unregister. */ - public static void unregisterGlobalConsumer(@Nonnull UnaryOperator consumer) { + public static void unregisterGlobalObserver(@Nonnull UnaryOperator observer) { LOCK.writeLock().lock(); try { - GLOBAL_OPERATORS.remove(consumer); + GLOBAL_OPERATORS.remove(observer); } finally { LOCK.writeLock().unlock(); } } /** - * Unregister a global interceptor. + * Unregister a global observer. * - * @param consumer the consumer to unregister. + * @param observer the observer to unregister. */ - public static void unregisterGlobalConsumer(@Nonnull Consumer consumer) { + public static void unregisterGlobalObserver(@Nonnull Consumer 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