From 3f3be68e5509ab3cac4296c8e920428a5cc5acca Mon Sep 17 00:00:00 2001 From: Nikolai Amelichev Date: Thu, 27 Nov 2025 16:26:59 +0100 Subject: [PATCH] #197: WIP WIP WIP Pass Db and Tx in transaction body lambda parameters WIP WIP WIP Very very experimental atm. Problems: - Not especially nice to use compared to @Inject'ing your Db implementation (that is just BaseDb.current()) /Using BaseDb.current() directly. - Function and Consumer are lambda signature-compatible, this leads to unability to have the same tx() name for both methods, and we get ScopedTxManager.call() and ScopedTxManager.run(), which is sub-optimal - Even if we only have one Function-taking method in ScopedManager, we get generics hell with AssertJ's assertThat(): results of Function-taking method are considered by javac to be compatible with almost every interface argument that assertThat() takes! - ReadOnlyBuilder/ScanBuilder are not supported (yet) --- .../ydb/yoj/repository/test/ListingTest.java | 250 ++-- .../yoj/repository/test/RepositoryTest.java | 1046 +++++++++-------- .../test/TableQueryBuilderTest.java | 129 +- .../yoj/repository/test/sample/TestDb.java | 4 +- .../ydb/YdbRepositoryIntegrationTest.java | 250 ++-- .../ydb/list/YdbListingIntegrationTest.java | 6 +- .../merge/YqlQueryMergerIntegrationTest.java | 10 +- .../repository/db/DelegatingTxManager.java | 47 +- .../yoj/repository/db/ScopedTxManager.java | 49 + 9 files changed, 924 insertions(+), 867 deletions(-) create mode 100644 repository/src/main/java/tech/ydb/yoj/repository/db/ScopedTxManager.java diff --git a/repository-test/src/main/java/tech/ydb/yoj/repository/test/ListingTest.java b/repository-test/src/main/java/tech/ydb/yoj/repository/test/ListingTest.java index 48f1b2d3..b604a321 100644 --- a/repository-test/src/main/java/tech/ydb/yoj/repository/test/ListingTest.java +++ b/repository-test/src/main/java/tech/ydb/yoj/repository/test/ListingTest.java @@ -4,6 +4,7 @@ import tech.ydb.yoj.databind.expression.FilterExpression; import tech.ydb.yoj.databind.expression.OrderExpression; import tech.ydb.yoj.repository.db.Repository; +import tech.ydb.yoj.repository.db.ScopedTxManager; import tech.ydb.yoj.repository.db.list.BadListingException.BadOffset; import tech.ydb.yoj.repository.db.list.BadListingException.BadPageSize; import tech.ydb.yoj.repository.db.list.ListRequest; @@ -12,7 +13,6 @@ import tech.ydb.yoj.repository.db.list.ViewListResult; import tech.ydb.yoj.repository.test.entity.TestEntities; import tech.ydb.yoj.repository.test.sample.TestDb; -import tech.ydb.yoj.repository.test.sample.TestDbImpl; import tech.ydb.yoj.repository.test.sample.model.Complex; import tech.ydb.yoj.repository.test.sample.model.LogEntry; import tech.ydb.yoj.repository.test.sample.model.Project; @@ -34,17 +34,17 @@ import static tech.ydb.yoj.repository.db.EntityExpressions.newOrderBuilder; public abstract class ListingTest extends RepositoryTestSupport { - protected TestDb db; + protected ScopedTxManager tx; @Override public void setUp() { super.setUp(); - this.db = new TestDbImpl<>(this.repository); + this.tx = new ScopedTxManager<>(this.repository, TestDb.class); } @Override public void tearDown() { - this.db = null; + this.tx = null; super.tearDown(); } @@ -61,19 +61,19 @@ public void basic() { Project notInOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); Project p3 = new Project(new Project.Id("uuid001"), "ZZZ"); - db.tx(() -> db.projects().insert(p1, p2, notInOutput, p3)); + tx.run(db -> db.projects().insert(p1, p2, notInOutput, p3)); OrderExpression orderBy = newOrderBuilder(Project.class).orderBy("name").descending().build(); FilterExpression filter = newFilterBuilder(Project.class).where("name").in("AAA", "XXX", "ZZZ").build(); - db.tx(() -> { - ListResult page1 = listProjects(ListRequest.builder(Project.class) + tx.run(db -> { + ListResult page1 = db.projects().list(ListRequest.builder(Project.class) .pageSize(1) .orderBy(orderBy) .filter(filter) .build()); assertThat(page1).containsExactly(p3); - ListResult page2 = listProjects(ListRequest.builder(Project.class) + ListResult page2 = db.projects().list(ListRequest.builder(Project.class) .pageSize(1) .orderBy(orderBy) .filter(filter) @@ -81,7 +81,7 @@ public void basic() { .build()); assertThat(page2).containsExactly(p2); - ListResult page3 = listProjects(ListRequest.builder(Project.class) + ListResult page3 = db.projects().list(ListRequest.builder(Project.class) .pageSize(1) .orderBy(orderBy) .filter(filter) @@ -98,10 +98,10 @@ public void complexIdRange() { Complex c2 = new Complex(new Complex.Id(999_999, 15L, "UUU", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 15L, "KKK", Complex.Status.OK)); Complex c4 = new Complex(new Complex.Id(999_000, 15L, "AAA", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4)); - db.tx(() -> { - ListResult page = listComplex(ListRequest.builder(Complex.class) + tx.run(db -> { + ListResult page = db.complexes().list(ListRequest.builder(Complex.class) .pageSize(3) .filter(fb -> fb.where("id.a").eq(999_999)) .build()); @@ -116,10 +116,10 @@ public void complexIdFullScan() { Complex c2 = new Complex(new Complex.Id(999_999, 15L, "UUU", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 15L, "KKK", Complex.Status.OK)); Complex c4 = new Complex(new Complex.Id(999_000, 15L, "AAA", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4)); - db.tx(() -> { - ListResult page = listComplex(ListRequest.builder(Complex.class) + tx.run(db -> { + ListResult page = db.complexes().list(ListRequest.builder(Complex.class) .pageSize(3) .filter(fb -> fb.where("id.c").eq("UUU")) .build()); @@ -139,9 +139,9 @@ public void uuid() { UniqueEntityNative c2 = new UniqueEntityNative(new UniqueEntityNative.Id(uuid2), "c2"); UniqueEntityNative c3 = new UniqueEntityNative(new UniqueEntityNative.Id(uuid3), "c3"); UniqueEntityNative c4 = new UniqueEntityNative(new UniqueEntityNative.Id(uuid4), "c4"); - db.tx(() -> db.table(UniqueEntityNative.class).insert(c1, c2, c3, c4)); + tx.run(db -> db.table(UniqueEntityNative.class).insert(c1, c2, c3, c4)); - db.tx(() -> { + tx.run(db -> { ListResult page1 = db.table(UniqueEntityNative.class).list( ListRequest.builder(UniqueEntityNative.class) .pageSize(3) @@ -162,32 +162,29 @@ public void uuid() { @Test public void failOnZeroPageSize() { - db.tx(() -> { - assertThatExceptionOfType(BadPageSize.class).isThrownBy(() -> - listProjects(ListRequest.builder(Project.class) - .pageSize(0) - .build())); - }); + tx.run(db -> assertThatExceptionOfType(BadPageSize.class).isThrownBy(() -> + db.projects().list(ListRequest.builder(Project.class) + .pageSize(0) + .build()) + )); } @Test public void failOnTooLargePageSize() { - db.tx(() -> { - assertThatExceptionOfType(BadPageSize.class).isThrownBy(() -> - listProjects(ListRequest.builder(Project.class) - .pageSize(100_000) - .build())); - }); + tx.run(db -> assertThatExceptionOfType(BadPageSize.class).isThrownBy(() -> + db.projects().list(ListRequest.builder(Project.class) + .pageSize(100_000) + .build()) + )); } @Test public void failOnTooLargeOffset() { - db.tx(() -> { - assertThatExceptionOfType(BadOffset.class).isThrownBy(() -> - listProjects(ListRequest.builder(Project.class) - .offset(10_001) - .build())); - }); + tx.run(db -> assertThatExceptionOfType(BadOffset.class).isThrownBy(() -> + db.projects().list(ListRequest.builder(Project.class) + .offset(10_001) + .build()) + )); } @Test @@ -196,10 +193,10 @@ public void defaultOrderingIsByIdAscending() { Complex c2 = new Complex(new Complex.Id(999_999, 15L, "UUU", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 0L, "UUU", Complex.Status.OK)); Complex c4 = new Complex(new Complex.Id(999_000, 0L, "UUU", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4)); - db.tx(() -> { - ListResult page = listComplex(ListRequest.builder(Complex.class) + tx.run(db -> { + ListResult page = db.complexes().list(ListRequest.builder(Complex.class) .pageSize(4) .build()); assertThat(page).containsExactly(c4, c3, c2, c1); @@ -214,10 +211,10 @@ public void and() { Complex c3 = new Complex(new Complex.Id(1, 300L, "KKK", Complex.Status.OK)); Complex notInOutput = new Complex(new Complex.Id(2, 300L, "AAA", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, notInOutput)); + tx.run(db -> db.complexes().insert(c1, c2, c3, notInOutput)); - db.tx(() -> { - ListResult page = listComplex(ListRequest.builder(Complex.class) + tx.run(db -> { + ListResult page = db.complexes().list(ListRequest.builder(Complex.class) .pageSize(3) .filter(fb -> fb.where("id.a").eq(1).and("id.b").gte(100L).and("id.b").lte(300L)) .build()); @@ -238,17 +235,17 @@ public void enumParsing() { .filter(filter) .build(); - db.tx(() -> db.typeFreaks() + tx.run(db -> db.typeFreaks() .list(request) .returnWithParams(ListingParams.empty())); } @Test public void embeddedNulls() { - db.tx(() -> db.typeFreaks().insert( + tx.run(db -> db.typeFreaks().insert( new TypeFreak(new TypeFreak.Id("b1p", 1), false, (byte) 0, (byte) 0, (short) 0, 0, 0, 0.0f, 0.0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null) )); - ListResult lst = db.tx(() -> db.typeFreaks().list(ListRequest.builder(TypeFreak.class) + ListResult lst = tx.call(db -> db.typeFreaks().list(ListRequest.builder(TypeFreak.class) .filter(fb -> fb.where("embedded.a.a").eq("myfqdn")) .pageSize(1) .build())); @@ -260,9 +257,9 @@ public void embeddedNulls() { @Test public void flattenedIsNull() { var tf = new TypeFreak(new TypeFreak.Id("b1p", 1), false, (byte) 0, (byte) 0, (short) 0, 0, 0, 0.0f, 0.0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); - db.tx(() -> db.typeFreaks().insert(tf)); + tx.run(db -> db.typeFreaks().insert(tf)); - ListResult lst = db.tx(() -> db.typeFreaks().list(ListRequest.builder(TypeFreak.class) + ListResult lst = tx.call(db -> db.typeFreaks().list(ListRequest.builder(TypeFreak.class) .filter(fb -> fb.where("jsonEmbedded").isNull()) .pageSize(1) .build())); @@ -274,9 +271,9 @@ public void flattenedIsNull() { @Test public void flattenedIsNotNull() { var tf = new TypeFreak(new TypeFreak.Id("b1p", 1), false, (byte) 0, (byte) 0, (short) 0, 0, 0, 0.0f, 0.0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new TypeFreak.Embedded(new TypeFreak.A("A"), new TypeFreak.B("B")), null, null, null, null, null, null, null, null, null, null, null); - db.tx(() -> db.typeFreaks().insert(tf)); + tx.run(db -> db.typeFreaks().insert(tf)); - ListResult lst = db.tx(() -> db.typeFreaks().list(ListRequest.builder(TypeFreak.class) + ListResult lst = tx.call(db -> db.typeFreaks().list(ListRequest.builder(TypeFreak.class) .filter(fb -> fb.where("jsonEmbedded").isNotNull()) .pageSize(1) .build())); @@ -291,12 +288,12 @@ public void simpleIdIn() { Project notInOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); Project p3 = new Project(new Project.Id("uuid001"), "ZZZ"); - db.tx(() -> db.projects().insert(p1, p2, notInOutput, p3)); + tx.run(db -> db.projects().insert(p1, p2, notInOutput, p3)); FilterExpression filter = newFilterBuilder(Project.class).where("id").in("uuid777", "uuid001", "uuid002").build(); OrderExpression orderBy = newOrderBuilder(Project.class).orderBy("id").ascending().build(); - db.tx(() -> { - ListResult page = listProjects(ListRequest.builder(Project.class) + tx.run(db -> { + ListResult page = db.projects().list(ListRequest.builder(Project.class) .pageSize(100) .filter(filter) .orderBy(orderBy) @@ -312,10 +309,10 @@ public void complexIdIn() { Complex c2 = new Complex(new Complex.Id(999_999, 14L, "BBB", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_000, 13L, "CCC", Complex.Status.FAIL)); Complex c4 = new Complex(new Complex.Id(999_000, 12L, "DDD", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4)); - db.tx(() -> { - ListResult page = listComplex(ListRequest.builder(Complex.class) + tx.run(db -> { + ListResult page = db.complexes().list(ListRequest.builder(Complex.class) .pageSize(100) .filter(fb -> fb .where("id.a").in(999_999, 999_000) @@ -339,10 +336,10 @@ public void complexUnixTimestampRelational() { Complex c1 = new Complex(new Complex.Id(999_999, now.toEpochMilli(), "AAA", Complex.Status.OK)); Complex c2 = new Complex(new Complex.Id(999_999, nowPlus1.toEpochMilli(), "BBB", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_000, nowPlus2.toEpochMilli(), "CCC", Complex.Status.FAIL)); - db.tx(() -> db.complexes().insert(c1, c2, c3)); + tx.run(db -> db.complexes().insert(c1, c2, c3)); - db.tx(() -> { - ListResult page = listComplex(ListRequest.builder(Complex.class) + tx.run(db -> { + ListResult page = db.complexes().list(ListRequest.builder(Complex.class) .pageSize(100) .filter(fb -> fb.where("id.a").in(999_999, 999_000).and("id.b").gte(now).and("id.b").lt(nowPlus2)) .orderBy(ob -> ob.orderBy("id.a").descending()) @@ -361,10 +358,10 @@ public void complexUnixTimestampIn() { Complex c1 = new Complex(new Complex.Id(999_999, now.toEpochMilli(), "AAA", Complex.Status.OK)); Complex c2 = new Complex(new Complex.Id(999_999, nowPlus1.toEpochMilli(), "BBB", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_000, nowPlus2.toEpochMilli(), "CCC", Complex.Status.FAIL)); - db.tx(() -> db.complexes().insert(c1, c2, c3)); + tx.run(db -> db.complexes().insert(c1, c2, c3)); - db.tx(() -> { - ListResult page = listComplex(ListRequest.builder(Complex.class) + tx.run(db -> { + ListResult page = db.complexes().list(ListRequest.builder(Complex.class) .pageSize(100) .filter(fb -> fb.where("id.a").in(999_999, 999_000).and("id.b").in(now, nowPlus2)) .orderBy(ob -> ob.orderBy("id.a").descending()) @@ -379,10 +376,10 @@ public void or() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project notInOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, notInOutput)); + tx.run(db -> db.projects().insert(p1, p2, notInOutput)); - db.tx(() -> { - ListResult page = listProjects(ListRequest.builder(Project.class) + tx.run(db -> { + ListResult page = db.projects().list(ListRequest.builder(Project.class) .pageSize(100) .filter(newFilterBuilder(Project.class) .where("id").eq("uuid002").or("id").eq("uuid777") @@ -398,10 +395,10 @@ public void notOr() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project inOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, inOutput)); + tx.run(db -> db.projects().insert(p1, p2, inOutput)); - db.tx(() -> { - ListResult page = listProjects(ListRequest.builder(Project.class) + tx.run(db -> { + ListResult page = db.projects().list(ListRequest.builder(Project.class) .pageSize(100) .filter(not(newFilterBuilder(Project.class) .where("id").eq("uuid002").or("id").eq("uuid777") @@ -417,10 +414,10 @@ public void notRel() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project inOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, inOutput)); + tx.run(db -> db.projects().insert(p1, p2, inOutput)); - db.tx(() -> { - ListResult page = listProjects(ListRequest.builder(Project.class) + tx.run(db -> { + ListResult page = db.projects().list(ListRequest.builder(Project.class) .pageSize(100) .filter(not(newFilterBuilder(Project.class) .where("id").gt("uuid002") @@ -436,10 +433,10 @@ public void notIn() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project inOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, inOutput)); + tx.run(db -> db.projects().insert(p1, p2, inOutput)); - db.tx(() -> { - ListResult page = listProjects(ListRequest.builder(Project.class) + tx.run(db -> { + ListResult page = db.projects().list(ListRequest.builder(Project.class) .pageSize(100) .filter(not(newFilterBuilder(Project.class) .where("id").in("uuid002", "uuid777") @@ -492,10 +489,10 @@ public void listByNamesWithUnderscores() { "CUSTOM NAMED COLUMN", null ); - db.tx(() -> db.typeFreaks().insert(tf)); + tx.run(db -> db.typeFreaks().insert(tf)); - db.tx(() -> { - ListResult page = listTypeFreak(ListRequest.builder(TypeFreak.class) + tx.run(db -> { + ListResult page = db.typeFreaks().list(ListRequest.builder(TypeFreak.class) .pageSize(50) .filter(newFilterBuilder(TypeFreak.class) .where("customNamedColumn").eq("CUSTOM NAMED COLUMN") @@ -509,10 +506,10 @@ public void listByNamesWithUnderscores() { @Test public void listStringValuedFilteredByString() { TypeFreak typeFreak = new TypeFreak(new TypeFreak.Id("b1p", 1), false, (byte) 0, (byte) 0, (short) 0, 0, 0, 0.0f, 0.0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new TypeFreak.Ticket("CLOUD", 100500)); - db.tx(() -> db.typeFreaks().insert(typeFreak)); + tx.run(db -> db.typeFreaks().insert(typeFreak)); - db.tx(() -> { - ListResult page = listTypeFreak(ListRequest.builder(TypeFreak.class) + tx.run(db -> { + ListResult page = db.typeFreaks().list(ListRequest.builder(TypeFreak.class) .pageSize(100) .filter(newFilterBuilder(TypeFreak.class) .where("ticket").eq("CLOUD-100500") @@ -526,10 +523,10 @@ public void listStringValuedFilteredByString() { @Test public void listStringValuedFilteredByString2() { TypeFreak typeFreak = new TypeFreak(new TypeFreak.Id("b1p", 1), false, (byte) 0, (byte) 0, (short) 0, 0, 0, 0.0f, 0.0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new TypeFreak.StringValueWrapper("svw 123"), null, null); - db.tx(() -> db.typeFreaks().insert(typeFreak)); + tx.run(db -> db.typeFreaks().insert(typeFreak)); - db.tx(() -> { - ListResult page = listTypeFreak(ListRequest.builder(TypeFreak.class) + tx.run(db -> { + ListResult page = db.typeFreaks().list(ListRequest.builder(TypeFreak.class) .pageSize(100) .filter(newFilterBuilder(TypeFreak.class) .where("stringValueWrapper").eq("svw 123") @@ -544,10 +541,10 @@ public void listStringValuedFilteredByString2() { public void listStringValuedFilteredByStruct() { TypeFreak.Ticket ticket = new TypeFreak.Ticket("CLOUD", 100500); TypeFreak typeFreak = new TypeFreak(new TypeFreak.Id("b1p", 1), false, (byte) 0, (byte) 0, (short) 0, 0, 0, 0.0f, 0.0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, ticket); - db.tx(() -> db.typeFreaks().insert(typeFreak)); + tx.run(db -> db.typeFreaks().insert(typeFreak)); - db.tx(() -> { - ListResult page = listTypeFreak(ListRequest.builder(TypeFreak.class) + tx.run(db -> { + ListResult page = db.typeFreaks().list(ListRequest.builder(TypeFreak.class) .pageSize(100) .filter(newFilterBuilder(TypeFreak.class) .where("ticket").eq(ticket) @@ -564,10 +561,10 @@ public void contains() { LogEntry notInOutput = new LogEntry(new LogEntry.Id("log2", 2L), LogEntry.Level.DEBUG, "will be ignored"); LogEntry e2 = new LogEntry(new LogEntry.Id("log1", 4L), LogEntry.Level.WARN, "middle msg"); LogEntry e3 = new LogEntry(new LogEntry.Id("log1", 5L), LogEntry.Level.INFO, "latest msg"); - db.tx(() -> db.logEntries().insert(e1, e2, notInOutput, e3)); + tx.run(db -> db.logEntries().insert(e1, e2, notInOutput, e3)); - db.tx(() -> { - ListResult page = listLogEntries(ListRequest.builder(LogEntry.class) + tx.run(db -> { + ListResult page = db.logEntries().list(ListRequest.builder(LogEntry.class) .pageSize(100) .filter(fb -> fb.where("message").contains("msg")) .build()); @@ -582,10 +579,10 @@ public void iContains() { LogEntry notInOutput = new LogEntry(new LogEntry.Id("log2", 2L), LogEntry.Level.DEBUG, "will be ignored"); LogEntry e2 = new LogEntry(new LogEntry.Id("log1", 4L), LogEntry.Level.WARN, "middle msg"); LogEntry e3 = new LogEntry(new LogEntry.Id("log1", 5L), LogEntry.Level.INFO, "latest msg"); - db.tx(() -> db.logEntries().insert(e1, e2, notInOutput, e3)); + tx.run(db -> db.logEntries().insert(e1, e2, notInOutput, e3)); - db.tx(() -> { - ListResult page = listLogEntries(ListRequest.builder(LogEntry.class) + tx.run(db -> { + ListResult page = db.logEntries().list(ListRequest.builder(LogEntry.class) .pageSize(100) .filter(fb -> fb.where("message").containsIgnoreCase("MsG")) .build()); @@ -600,10 +597,10 @@ public void notContains() { LogEntry inOutput = new LogEntry(new LogEntry.Id("log2", 2L), LogEntry.Level.DEBUG, "will be ignored"); LogEntry e2 = new LogEntry(new LogEntry.Id("log1", 4L), LogEntry.Level.WARN, "middle msg"); LogEntry e3 = new LogEntry(new LogEntry.Id("log1", 5L), LogEntry.Level.INFO, "latest msg"); - db.tx(() -> db.logEntries().insert(e1, e2, inOutput, e3)); + tx.run(db -> db.logEntries().insert(e1, e2, inOutput, e3)); - db.tx(() -> { - ListResult page = listLogEntries(ListRequest.builder(LogEntry.class) + tx.run(db -> { + ListResult page = db.logEntries().list(ListRequest.builder(LogEntry.class) .pageSize(100) .filter(fb -> fb.where("message").doesNotContain("msg")) .build()); @@ -618,10 +615,10 @@ public void containsEscaped() { LogEntry notInOutput = new LogEntry(new LogEntry.Id("log2", 2L), LogEntry.Level.DEBUG, "will be ignored"); LogEntry e2 = new LogEntry(new LogEntry.Id("log1", 4L), LogEntry.Level.WARN, "__hi%_there_"); LogEntry e3 = new LogEntry(new LogEntry.Id("log1", 5L), LogEntry.Level.INFO, "%_"); - db.tx(() -> db.logEntries().insert(e1, e2, notInOutput, e3)); + tx.run(db -> db.logEntries().insert(e1, e2, notInOutput, e3)); - db.tx(() -> { - ListResult page = listLogEntries(ListRequest.builder(LogEntry.class) + tx.run(db -> { + ListResult page = db.logEntries().list(ListRequest.builder(LogEntry.class) .pageSize(100) .filter(fb -> fb.where("message").contains("%_")) .build()); @@ -636,10 +633,10 @@ public void startsWith() { LogEntry notInOutput = new LogEntry(new LogEntry.Id("log2", 2L), LogEntry.Level.DEBUG, "will be ignored"); LogEntry e2 = new LogEntry(new LogEntry.Id("log1", 4L), LogEntry.Level.WARN, "#tag middle msg"); LogEntry e3 = new LogEntry(new LogEntry.Id("log1", 5L), LogEntry.Level.INFO, "#tag latest msg"); - db.tx(() -> db.logEntries().insert(e1, e2, notInOutput, e3)); + tx.run(db -> db.logEntries().insert(e1, e2, notInOutput, e3)); - db.tx(() -> { - ListResult page = listLogEntries(ListRequest.builder(LogEntry.class) + tx.run(db -> { + ListResult page = db.logEntries().list(ListRequest.builder(LogEntry.class) .pageSize(100) .filter(fb -> fb.where("message").startsWith("#tag")) .build()); @@ -654,10 +651,10 @@ public void startsWithEscaped() { LogEntry notInOutput = new LogEntry(new LogEntry.Id("log2", 2L), LogEntry.Level.DEBUG, "will be ignored"); LogEntry e2 = new LogEntry(new LogEntry.Id("log1", 4L), LogEntry.Level.WARN, "__hi%_there_"); LogEntry e3 = new LogEntry(new LogEntry.Id("log1", 5L), LogEntry.Level.INFO, "%_"); - db.tx(() -> db.logEntries().insert(e1, e2, notInOutput, e3)); + tx.run(db -> db.logEntries().insert(e1, e2, notInOutput, e3)); - db.tx(() -> { - ListResult page = listLogEntries(ListRequest.builder(LogEntry.class) + tx.run(db -> { + ListResult page = db.logEntries().list(ListRequest.builder(LogEntry.class) .pageSize(100) .filter(fb -> fb.where("message").startsWith("%_")) .build()); @@ -672,10 +669,10 @@ public void endsWith() { LogEntry inOutput = new LogEntry(new LogEntry.Id("log2", 2L), LogEntry.Level.DEBUG, "will be ignored"); LogEntry e2 = new LogEntry(new LogEntry.Id("log1", 4L), LogEntry.Level.WARN, "middle msg #tag"); LogEntry e3 = new LogEntry(new LogEntry.Id("log1", 5L), LogEntry.Level.INFO, "latest msg #tag"); - db.tx(() -> db.logEntries().insert(e1, e2, inOutput, e3)); + tx.run(db -> db.logEntries().insert(e1, e2, inOutput, e3)); - db.tx(() -> { - ListResult page = listLogEntries(ListRequest.builder(LogEntry.class) + tx.run(db -> { + ListResult page = db.logEntries().list(ListRequest.builder(LogEntry.class) .pageSize(100) .filter(fb -> fb.where("message").endsWith(" #tag")) .build()); @@ -690,10 +687,10 @@ public void endsWithEscaped() { LogEntry notInOutput = new LogEntry(new LogEntry.Id("log2", 2L), LogEntry.Level.DEBUG, "will be ignored"); LogEntry e2 = new LogEntry(new LogEntry.Id("log1", 4L), LogEntry.Level.WARN, "__hi%_there_"); LogEntry e3 = new LogEntry(new LogEntry.Id("log1", 5L), LogEntry.Level.INFO, "%_"); - db.tx(() -> db.logEntries().insert(e1, e2, notInOutput, e3)); + tx.run(db -> db.logEntries().insert(e1, e2, notInOutput, e3)); - db.tx(() -> { - ListResult page = listLogEntries(ListRequest.builder(LogEntry.class) + tx.run(db -> { + ListResult page = db.logEntries().list(ListRequest.builder(LogEntry.class) .pageSize(100) .filter(fb -> fb.where("message").endsWith("%_")) .build()); @@ -702,20 +699,21 @@ public void endsWithEscaped() { }); } - @Test public void view() { LogEntry e1 = new LogEntry(new LogEntry.Id("log1", 1L), LogEntry.Level.ERROR, "earliest msg"); LogEntry notInOutput = new LogEntry(new LogEntry.Id("log2", 2L), LogEntry.Level.DEBUG, "will be ignored"); LogEntry e2 = new LogEntry(new LogEntry.Id("log1", 4L), LogEntry.Level.WARN, "middle msg"); LogEntry e3 = new LogEntry(new LogEntry.Id("log1", 5L), LogEntry.Level.INFO, "latest msg"); - db.tx(() -> db.logEntries().insert(e1, e2, notInOutput, e3)); - - db.tx(() -> { - ViewListResult page = listLogMessages(ListRequest.builder(LogEntry.class) - .pageSize(100) - .filter(fb -> fb.where("id.logId").eq("log1")) - .build()); + tx.run(db -> db.logEntries().insert(e1, e2, notInOutput, e3)); + + tx.run(db -> { + ViewListResult page = db.logEntries().list( + LogEntry.Message.class, + ListRequest.builder(LogEntry.class) + .pageSize(100) + .filter(fb -> fb.where("id.logId").eq("log1")) + .build()); assertThat(page).containsExactly( new LogEntry.Message(new LogEntry.Id("log1", 1L), "earliest msg"), new LogEntry.Message(new LogEntry.Id("log1", 4L), "middle msg"), @@ -724,24 +722,4 @@ public void view() { assertThat(page.isLastPage()).isTrue(); }); } - - protected final ListResult listProjects(ListRequest request) { - return db.projects().list(request); - } - - protected final ListResult listComplex(ListRequest request) { - return db.complexes().list(request); - } - - protected final ListResult listTypeFreak(ListRequest request) { - return db.typeFreaks().list(request); - } - - protected final ListResult listLogEntries(ListRequest request) { - return db.logEntries().list(request); - } - - protected final ViewListResult listLogMessages(ListRequest request) { - return db.logEntries().list(LogEntry.Message.class, request); - } } diff --git a/repository-test/src/main/java/tech/ydb/yoj/repository/test/RepositoryTest.java b/repository-test/src/main/java/tech/ydb/yoj/repository/test/RepositoryTest.java index 884a7657..0df4dea7 100644 --- a/repository-test/src/main/java/tech/ydb/yoj/repository/test/RepositoryTest.java +++ b/repository-test/src/main/java/tech/ydb/yoj/repository/test/RepositoryTest.java @@ -4,7 +4,6 @@ import com.google.common.collect.Iterators; import lombok.NonNull; import lombok.SneakyThrows; -import org.assertj.core.api.Assertions; import org.junit.Test; import tech.ydb.yoj.databind.ByteArray; import tech.ydb.yoj.databind.expression.FieldValue; @@ -20,6 +19,7 @@ import tech.ydb.yoj.repository.db.Repository; import tech.ydb.yoj.repository.db.RepositoryTransaction; import tech.ydb.yoj.repository.db.SchemaOperations; +import tech.ydb.yoj.repository.db.ScopedTxManager; import tech.ydb.yoj.repository.db.StdTxManager; import tech.ydb.yoj.repository.db.Table; import tech.ydb.yoj.repository.db.Tx; @@ -40,7 +40,6 @@ import tech.ydb.yoj.repository.db.statement.Changeset; import tech.ydb.yoj.repository.test.entity.TestEntities; import tech.ydb.yoj.repository.test.sample.TestDb; -import tech.ydb.yoj.repository.test.sample.TestDbImpl; import tech.ydb.yoj.repository.test.sample.TestEntityOperations; import tech.ydb.yoj.repository.test.sample.model.BadlyWrappedEntity; import tech.ydb.yoj.repository.test.sample.model.BadlyWrappedEntity.BadStringValueWrapper; @@ -106,12 +105,15 @@ import static java.util.stream.Collectors.toSet; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIterable; +import static org.assertj.core.api.Assertions.assertThatList; +import static org.assertj.core.api.Assertions.assertThatObject; import static org.assertj.core.api.Assertions.fail; import static tech.ydb.yoj.repository.db.EntityExpressions.newFilterBuilder; @SuppressWarnings("checkstyle:MethodCount") public abstract class RepositoryTest extends RepositoryTestSupport { - protected TestDb db; + protected ScopedTxManager tx; @Override protected Repository createRepository() { @@ -121,12 +123,12 @@ protected Repository createRepository() { @Override public void setUp() { super.setUp(); - this.db = new TestDbImpl<>(this.repository); + this.tx = new ScopedTxManager<>(this.repository, TestDb.class); } @Override public void tearDown() { - this.db = null; + this.tx = null; super.tearDown(); } @@ -202,13 +204,14 @@ private void checkEmpty(TxManager txManager) { @Test public void listWithQueryByNullableField() { - Project projectWithName1 = db.tx(() -> db.projects().save(new Project(new Project.Id("1"), "named_1"))); + Project projectWithName1 = tx.call(db -> db.projects().save(new Project(new Project.Id("1"), "named_1"))); assertThat(projectWithName1.getName()).isEqualTo("named_1"); - Project projectWithName2 = db.tx(() -> db.projects().save(new Project(new Project.Id("2"), "named_2"))); + Project projectWithName2 = tx.call(db -> db.projects().save(new Project(new Project.Id("2"), "named_2"))); assertThat(projectWithName2.getName()).isEqualTo("named_2"); - Project projectWithoutName = db.tx(() -> db.projects().save(new Project(new Project.Id("3"), null))); + Project projectWithoutName = tx.call(db -> db.projects().save(new Project(new Project.Id("3"), null))); assertThat(projectWithoutName.getName()).isNull(); - assertThat(db.readOnly().run(() -> db.projects().findAll())).hasSize(3); + // FIXME + assertThat(tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().findAll())).hasSize(3); FilterExpression eqNotNullExp = newFilterBuilder(Project.class) .where("name").eq("named_1") @@ -233,32 +236,32 @@ public void listWithQueryByNullableField() { @Test public void simpleCrudInDifferentTx() { - Project p1 = db.tx(() -> { + Project p1 = tx.call(db -> { Project p = new Project(new Project.Id("1"), "named"); db.projects().save(p); return p; }); - Project p2 = db.tx(() -> db.projects().find(new Project.Id("1"))); + Project p2 = tx.call(db -> db.projects().find(new Project.Id("1"))); assertThat(p2).isEqualTo(p1); - Project p3 = db.tx(() -> { + Project p3 = tx.call(db -> { Project p = new Project(new Project.Id("1"), "renamed"); db.projects().save(p); return p; }); - Project p4 = db.tx(() -> db.projects().find(new Project.Id("1"))); + Project p4 = tx.call(db -> db.projects().find(new Project.Id("1"))); assertThat(p4).isEqualTo(p3); - db.tx(() -> db.projects().delete(new Project.Id("1"))); - Project p5 = db.tx(() -> db.projects().find(new Project.Id("1"))); + tx.run(db -> db.projects().delete(new Project.Id("1"))); + Project p5 = tx.call(db -> db.projects().find(new Project.Id("1"))); assertThat(p5).isNull(); } @Test public void deferAfterCommitDontRunInDryRun() { - db.withDryRun(true).tx( - () -> Tx.Current.get().defer( + tx.withDryRun(true).run((_1, txControl) -> + txControl.defer( () -> fail("defer after commit musn't call in dry run") ) ); @@ -266,8 +269,8 @@ public void deferAfterCommitDontRunInDryRun() { @Test public void deferNotInTxContext() { - db.tx( - () -> Tx.Current.get().defer( + tx.run((_1, txControl) -> + txControl.defer( () -> assertThat(Tx.Current.exists()) .withFailMessage("defer must not run in tx context") .isFalse() @@ -277,8 +280,8 @@ public void deferNotInTxContext() { @Test public void deferFinallyDontRunInDryRun() { - db.withDryRun(true).tx( - () -> Tx.Current.get().deferFinally( + tx.withDryRun(true).run((_1, txControl) -> + txControl.deferFinally( () -> fail("defer after commit musn't be called in dry-run mode") ) ); @@ -287,15 +290,15 @@ public void deferFinallyDontRunInDryRun() { @Test public void deferFinallyCommit() { AtomicInteger executions = new AtomicInteger(); - db.tx(() -> Tx.Current.get().deferFinally(executions::incrementAndGet)); + tx.run((_1, txControl) -> txControl.deferFinally(executions::incrementAndGet)); assertThat(executions).hasValue(1); } @Test public void deferFinallyRollback() { AtomicInteger executions = new AtomicInteger(); - assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> db.tx(() -> { - Tx.Current.get().deferFinally(executions::incrementAndGet); + assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> tx.run((_1, txControl) -> { + txControl.deferFinally(executions::incrementAndGet); throw new RuntimeException(); })); assertThat(executions).hasValue(1); @@ -304,7 +307,7 @@ public void deferFinallyRollback() { @Test public void deferFinallyRollbackRetryable() { AtomicInteger executions = new AtomicInteger(); - assertThatExceptionOfType(UnavailableException.class).isThrownBy(() -> db.tx(() -> { + assertThatExceptionOfType(UnavailableException.class).isThrownBy(() -> tx.call(db -> { Tx.Current.get().deferFinally(executions::incrementAndGet); throw new OptimisticLockException(""); })); @@ -313,7 +316,7 @@ public void deferFinallyRollbackRetryable() { @Test public void deferFinallyNotInTxContext() { - db.tx(() -> Tx.Current.get().deferFinally(() -> + tx.run((_1, txControl) -> txControl.deferFinally(() -> assertThat(Tx.Current.exists()) .withFailMessage("deferFinally must not be in a tx context") .isFalse() @@ -322,8 +325,8 @@ public void deferFinallyNotInTxContext() { @Test public void deferFinallyRollbackNotInTxContext() { - assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> db.tx(() -> { - Tx.Current.get().deferFinally(() -> + assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> tx.run((_1, txControl) -> { + txControl.deferFinally(() -> assertThat(Tx.Current.exists()) .withFailMessage("deferFinally must not be in a tx context") .isFalse() @@ -334,24 +337,24 @@ public void deferFinallyRollbackNotInTxContext() { @Test public void findAll() { - List all = db.tx(() -> db.projects().findAll()); + List all = tx.call(db -> db.projects().findAll()); assertThat(all).isEmpty(); - Project p1 = db.tx(() -> { + Project p1 = tx.call(db -> { Project p = new Project(new Project.Id("1"), "p1"); db.projects().save(p); return p; }); - List all1 = db.tx(() -> db.projects().findAll()); + List all1 = tx.call(db -> db.projects().findAll()); assertThat(all1).hasSize(1); assertThat(all1.get(0)).isEqualTo(p1); - db.tx(() -> db.projects().save(new Project(new Project.Id("2"), "p2"))); - List all2 = db.tx(() -> db.projects().findAll()); + tx.call(db -> db.projects().save(new Project(new Project.Id("2"), "p2"))); + List all2 = tx.call(db -> db.projects().findAll()); assertThat(all2).hasSize(2); - db.tx(() -> db.projects().deleteAll()); - List all3 = db.tx(() -> db.projects().findAll()); + tx.run(db -> db.projects().deleteAll()); + List all3 = tx.call(db -> db.projects().findAll()); assertThat(all3).isEmpty(); } @@ -359,18 +362,18 @@ public void findAll() { public void streamAll() { for (int i = 1; i < 5; i++) { Project p = new Project(new Project.Id(String.valueOf(i)), ""); - db.tx(() -> db.projects().save(p)); - assertThat(db.tx(() -> db.projects().streamAll(2).collect(toList()))) + tx.call(db -> db.projects().save(p)); + assertThat(tx.>call(db -> db.projects().streamAll(2).collect(toList()))) .hasSize(i); } - assertThat(db.tx(() -> db.projects().streamAll(2).limit(1).collect(toList()))) + assertThat(tx.>call(db -> db.projects().streamAll(2).limit(1).collect(toList()))) .hasSize(1); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> db.tx(() -> db.projects().streamAll(0))); + .isThrownBy(() -> tx.run(db -> db.projects().streamAll(0))); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> db.tx(() -> db.projects().streamAll(5001))); + .isThrownBy(() -> tx.run(db -> db.projects().streamAll(5001))); } private static > ReadTableParams defaultReadTableParamsNonLegacy() { @@ -383,14 +386,16 @@ private static > ReadTableParams.ReadTableParamsBuilder< @Test public void readTable() { - assertThat(db.readOnly().run(() -> db.projects().readTable(defaultReadTableParamsNonLegacy()).count())).isEqualTo(0); + // FIXME + assertThat(tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTable(defaultReadTableParamsNonLegacy()).count())).isEqualTo(0); List expectedProjects = new ArrayList<>(); for (int i = 1; i <= 9; ++i) { Project p = new Project(new Project.Id(String.valueOf(i)), "project-" + i); expectedProjects.add(p); - db.tx(() -> db.projects().save(p)); - assertThat(db.readOnly().run(() -> db.projects().readTable(defaultReadTableParamsNonLegacy()).count())) + tx.call(db -> db.projects().save(p)); + // FIXME + assertThat(tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTable(defaultReadTableParamsNonLegacy()).count())) .isEqualTo(i); } @@ -404,7 +409,8 @@ public void readTable() { .rowLimit(5) .build(); assertThat( - db.readOnly().run(() -> db.projects().readTable(readFrom) + // FIXME + tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTable(readFrom) .map(p -> p.getId().getValue()) .collect(Collectors.toList())) ).isEqualTo( @@ -413,7 +419,8 @@ public void readTable() { .collect(Collectors.toList()) ); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> db.readOnly().run(() -> db.projects().readTable(readFromUnordered))); + // FIXME + .isThrownBy(() -> tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTable(readFromUnordered))); ReadTableParams readFromTo = RepositoryTest.buildReadTableParamsNonLegacy() .fromKeyInclusive(expectedProjects.get(3).getId()) .toKey(expectedProjects.get(7).getId()) @@ -424,7 +431,8 @@ public void readTable() { .toKey(expectedProjects.get(7).getId()) .build(); assertThat( - db.readOnly().run(() -> db.projects().readTable(readFromTo) + // FIXME + tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTable(readFromTo) .map(p -> p.getId().getValue()) .collect(Collectors.toList())) ).isEqualTo( @@ -433,22 +441,25 @@ public void readTable() { .collect(Collectors.toList()) ); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> db.readOnly().run(() -> db.projects().readTable(readFromToUnordered))); + // FIXME + .isThrownBy(() -> tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTable(readFromToUnordered))); assertThatExceptionOfType(IllegalTransactionIsolationLevelException.class) - .isThrownBy(() -> db.tx(() -> db.projects().readTable(defaultReadTableParamsNonLegacy()).count())); + .isThrownBy(() -> tx.call(db -> db.projects().readTable(defaultReadTableParamsNonLegacy()).count())); } @Test public void readTableIds() { - assertThat(db.readOnly().run(() -> db.projects().readTableIds(defaultReadTableParamsNonLegacy()).count())) + // FIXME + assertThat(tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTableIds(defaultReadTableParamsNonLegacy()).count())) .isEqualTo(0); List expectedProjectIds = new ArrayList<>(); for (int i = 1; i <= 9; ++i) { Project p = new Project(new Project.Id(String.valueOf(i)), "project-" + i); expectedProjectIds.add(p.getId()); - db.tx(() -> db.projects().save(p)); - assertThat(db.readOnly().run(() -> db.projects().readTableIds(defaultReadTableParamsNonLegacy()).count())) + tx.call(db -> db.projects().save(p)); + // FIXME + assertThat(tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTableIds(defaultReadTableParamsNonLegacy()).count())) .isEqualTo(i); } @@ -462,7 +473,8 @@ public void readTableIds() { .rowLimit(5) .build(); assertThat( - db.readOnly().run(() -> db.projects().readTableIds(readFrom) + // FIXME + tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTableIds(readFrom) .map(Project.Id::getValue) .collect(Collectors.toList())) ).isEqualTo( @@ -471,7 +483,8 @@ public void readTableIds() { .collect(Collectors.toList()) ); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> db.readOnly().run(() -> db.projects().readTableIds(readFromUnordered))); + // FIXME + .isThrownBy(() -> tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTableIds(readFromUnordered))); ReadTableParams readFromTo = RepositoryTest.buildReadTableParamsNonLegacy() .fromKeyInclusive(expectedProjectIds.get(3)) .toKey(expectedProjectIds.get(7)) @@ -482,7 +495,8 @@ public void readTableIds() { .toKey(expectedProjectIds.get(7)) .build(); assertThat( - db.readOnly().run(() -> db.projects().readTableIds(readFromTo) + // FIXME + tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTableIds(readFromTo) .map(Project.Id::getValue) .collect(Collectors.toList())) ).isEqualTo( @@ -491,14 +505,16 @@ public void readTableIds() { .collect(Collectors.toList()) ); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> db.readOnly().run(() -> db.projects().readTableIds(readFromToUnordered))); + // FIXME + .isThrownBy(() -> tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTableIds(readFromToUnordered))); assertThatExceptionOfType(IllegalTransactionIsolationLevelException.class) - .isThrownBy(() -> db.tx(() -> db.projects().readTableIds(defaultReadTableParamsNonLegacy()).count())); + .isThrownBy(() -> tx.call(db -> db.projects().readTableIds(defaultReadTableParamsNonLegacy()).count())); } @Test public void readTableViews() { - assertThat(db.readOnly().run(() -> db.typeFreaks().readTableIds(defaultReadTableParamsNonLegacy()).count())) + // FIXME + assertThat(tx.readOnly().run(() -> BaseDb.current(TestDb.class).typeFreaks().readTableIds(defaultReadTableParamsNonLegacy()).count())) .isEqualTo(0); List expectedViews = new ArrayList<>(); @@ -506,12 +522,13 @@ public void readTableViews() { for (int i = 0; i < 100; i++) { TypeFreak tf = newTypeFreak(i, "AAA" + (i + 1), "bbb"); - db.tx(() -> db.typeFreaks().save(tf)); + tx.call(db -> db.typeFreaks().save(tf)); savedCount++; if (i < 50) { expectedViews.add(new TypeFreak.View(tf.getId(), tf.getEmbedded())); - assertThat(db.readOnly().run(() -> db.typeFreaks() + // FIXME + assertThat(tx.readOnly().run(() -> BaseDb.current(TestDb.class).typeFreaks() .readTable(TypeFreak.View.class, defaultReadTableParamsNonLegacy()).count())).isEqualTo(savedCount); } } @@ -521,7 +538,8 @@ public void readTableViews() { .rowLimit(expectedViews.size()) .ordered() .build(); - assertThat(db.readOnly().run(() -> db.typeFreaks().readTable(TypeFreak.View.class, readFrom).collect(toList()))) + // FIXME + assertThat(tx.readOnly().run(() -> BaseDb.current(TestDb.class).typeFreaks().readTable(TypeFreak.View.class, readFrom).collect(toList()))) .isEqualTo(expectedViews); ReadTableParams readFromTo = RepositoryTest.buildReadTableParamsNonLegacy() @@ -529,11 +547,12 @@ public void readTableViews() { .toKeyInclusive(expectedViews.get(expectedViews.size() - 1).getId()) .ordered() .build(); - assertThat(db.readOnly().run(() -> db.typeFreaks().readTable(TypeFreak.View.class, readFromTo).collect(toList()))) + // FIXME + assertThat(tx.readOnly().run(() -> BaseDb.current(TestDb.class).typeFreaks().readTable(TypeFreak.View.class, readFromTo).collect(toList()))) .isEqualTo(expectedViews); assertThatExceptionOfType(IllegalTransactionIsolationLevelException.class) - .isThrownBy(() -> db.tx(() -> db.typeFreaks().readTableIds(defaultReadTableParamsNonLegacy()).count())); + .isThrownBy(() -> tx.call(db -> db.typeFreaks().readTableIds(defaultReadTableParamsNonLegacy()).count())); } @Test @@ -541,66 +560,67 @@ public void doNotCommitAfterTLI() { Project.Id id1 = new Project.Id("id1"); Project.Id id2 = new Project.Id("id2"); - RepositoryTransaction tx = repository.startTransaction( + RepositoryTransaction repositoryTransaction = repository.startTransaction( TxOptions.create(IsolationLevel.SERIALIZABLE_READ_WRITE) .withImmediateWrites(true) .withFirstLevelCache(false) ); - tx.table(Project.class).find(id2); + repositoryTransaction.table(Project.class).find(id2); - db.tx(() -> db.projects().save(new Project(id2, "name2"))); + tx.run(db -> db.projects().save(new Project(id2, "name2"))); - tx.table(Project.class).save(new Project(id1, "name1")); // make tx available for TLI + repositoryTransaction.table(Project.class).save(new Project(id1, "name1")); // make tx available for TLI assertThatExceptionOfType(OptimisticLockException.class) - .isThrownBy(() -> tx.table(Project.class).find(id2)); + .isThrownBy(() -> repositoryTransaction.table(Project.class).find(id2)); try { - tx.commit(); + repositoryTransaction.commit(); } catch (IllegalStateException ignore) { // Some implementations throw, some don't } - tx.rollback(); // YOJ-tx rollback is possible. session.rollbackCommit() won't execute + repositoryTransaction.rollback(); // YOJ-tx rollback is possible. session.rollbackCommit() won't execute } @Test public void writeDontProduceTLI() { Project.Id id = new Project.Id("id"); - db.tx(() -> db.projects().save(new Project(id, "name"))); + tx.call(db -> db.projects().save(new Project(id, "name"))); - RepositoryTransaction tx = repository.startTransaction( + RepositoryTransaction repositoryTransaction = repository.startTransaction( TxOptions.create(IsolationLevel.SERIALIZABLE_READ_WRITE) .withImmediateWrites(true) .withFirstLevelCache(false) ); - tx.table(Project.class).find(id); + repositoryTransaction.table(Project.class).find(id); - db.tx(() -> { + tx.run(db -> { db.projects().find(id); db.projects().save(new Project(id, "name2")); }); // write don't produce TLI - tx.table(Project.class).save(new Project(id, "name3")); + repositoryTransaction.table(Project.class).save(new Project(id, "name3")); assertThatExceptionOfType(OptimisticLockException.class) - .isThrownBy(tx::commit); + .isThrownBy(repositoryTransaction::commit); } @Test public void spliteratorDoubleTryAdvance() { - db.tx(() -> { + tx.run(db -> { for (int i = 0; i < 100; i++) { db.projects().save(new Project(new Project.Id(String.valueOf(i)), "")); } }); - db.readOnly().run(() -> { - Spliterator spliterator = db.projects().readTable(defaultReadTableParamsNonLegacy()).spliterator(); + tx.readOnly().run(() -> { + //FIXME + Spliterator spliterator = BaseDb.current(TestDb.class).projects().readTable(defaultReadTableParamsNonLegacy()).spliterator(); // this loop calls tryAdvance() on spliterator one time after tryAdvance() says false while (true) { @@ -612,8 +632,9 @@ public void spliteratorDoubleTryAdvance() { }); // one more example - db.readOnly().run(() -> { - Stream stream = db.projects().readTable(defaultReadTableParamsNonLegacy()); + tx.readOnly().run(() -> { + // FIXME + Stream stream = BaseDb.current(TestDb.class).projects().readTable(defaultReadTableParamsNonLegacy()); Stream> stream2 = StreamSupport.stream( // With this line steam calls tryAdvance() on spliterator one time after tryAdvance() says false @@ -631,39 +652,37 @@ public void consistencyCheckAllColumnsOnFind() { Project.Id id1 = new Project.Id("id1"); Project.Id id2 = new Project.Id("id2"); - db.tx(() -> { + tx.run(db -> { db.projects().save(new Project(id1, "name")); db.projects().save(new Project(id2, "name")); }); - RepositoryTransaction tx = repository.startTransaction( + RepositoryTransaction repositoryTransaction = repository.startTransaction( TxOptions.create(IsolationLevel.SERIALIZABLE_READ_WRITE) .withImmediateWrites(true) .withFirstLevelCache(false) ); - tx.table(Project.class).save(new Project(new Project.Id("id3"), "name")); // make tx available for TLI + repositoryTransaction.table(Project.class).save(new Project(new Project.Id("id3"), "name")); // make tx available for TLI - tx.table(Project.class).find(id1); - tx.table(Project.class).find(id2); + repositoryTransaction.table(Project.class).find(id1); + repositoryTransaction.table(Project.class).find(id2); - db.tx(() -> { + tx.run(db -> { db.projects().find(id2); db.projects().save(new Project(id2, "name2")); }); assertThatExceptionOfType(OptimisticLockException.class) - .isThrownBy(() -> tx.table(Project.class).find(id1)); + .isThrownBy(() -> repositoryTransaction.table(Project.class).find(id1)); } @Test public void streamAllWithPartitioning() { - db.tx(() -> { - db.complexes().insert(new Complex(new Complex.Id(0, 0L, "0", Complex.Status.OK))); - }); + tx.run(db -> db.complexes().insert(new Complex(new Id(0, 0L, "0", Complex.Status.OK)))); - assertThat(db.tx(() -> { - ArrayList found = new ArrayList<>(); + assertThatList(tx.call(db -> { + List found = new ArrayList<>(); Iterators.partition( db.complexes() .streamAll(100) @@ -678,145 +697,122 @@ public void streamAllWithPartitioning() { public void viewStreamAll() { for (int i = 1; i < 5; i++) { Book b = new Book(new Book.Id(String.valueOf(i)), i, "title-" + i, List.of()); - db.tx(() -> db.table(Book.class).save(b)); - assertThat(db.tx(() -> db.table(Book.class).streamAll(Book.TitleViewId.class, 2).collect(toList()))) + tx.run(db -> db.table(Book.class).save(b)); + assertThatList(tx.call(db -> db.table(Book.class).streamAll(Book.TitleViewId.class, 2).collect(toList()))) .hasSize(i); } - db.tx(() -> db.table(Book.class) + tx.run(db -> db.table(Book.class) .streamAll(Book.TitleViewId.class, 100) .forEach(titleView -> assertThat(titleView.getTitle()).isNotBlank())); - assertThat(db.tx(() -> db.table(Book.class).streamAll(Book.TitleViewId.class, 2) + assertThatList(tx.call(db -> db.table(Book.class).streamAll(Book.TitleViewId.class, 2) .limit(1).collect(toList()))) .hasSize(1); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> db.tx(() -> db.table(Book.class).streamAll(Book.TitleViewId.class, 0))); + .isThrownBy(() -> tx.call(db -> db.table(Book.class).streamAll(Book.TitleViewId.class, 0))); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> db.tx(() -> db.table(Book.class).streamAll(Book.TitleViewId.class, 5001))); + .isThrownBy(() -> tx.call(db -> db.table(Book.class).streamAll(Book.TitleViewId.class, 5001))); } @Test public void streamAllComposite() { - db.tx(this::makeComplexes); - assertThat( - db.tx(() -> db.complexes().streamAll(2).collect(toList())) - ).isEqualTo( - db.tx(() -> db.complexes().findAll()) - ); + tx.run(this::makeComplexes); + tx.run(db -> assertThat(db.complexes().streamAll(2)) + .containsExactlyElementsOf(db.complexes().findAll())); } @Test public void streamEmpty() { - db.tx(() -> assertThat(db.complexes().streamAll(2)).isEmpty()); + tx.run(db -> assertThat(db.complexes().streamAll(2)).isEmpty()); } @Test public void streamPartial() { - db.tx(this::makeComplexes); - assertThat( - db.tx(() -> db.complexes().streamPartial(new Complex.Id(0, 0L, "aaa", Complex.Status.OK), 2).collect(toList())) - ).isEqualTo( - db.tx(() -> db.complexes().find(Range.create(new Complex.Id(0, 0L, "aaa", Complex.Status.OK)))) - ); - assertThat( - db.tx(() -> db.complexes().streamPartial(new Complex.Id(0, 0L, "aaa", null), 2).collect(toList())) - ).isEqualTo( - db.tx(() -> db.complexes().find(Range.create(new Complex.Id(0, 0L, "aaa", null)))) - ); - assertThat( - db.tx(() -> db.complexes().streamPartial(new Complex.Id(0, 0L, null, null), 2).collect(toList())) - ).isEqualTo( - db.tx(() -> db.complexes().find(Range.create(new Complex.Id(0, 0L, null, null)))) - ); - assertThat( - db.tx(() -> db.complexes().streamPartial(new Complex.Id(0, null, null, null), 2).collect(toList())) - ).isEqualTo( - db.tx(() -> db.complexes().find(Range.create(new Complex.Id(0, null, null, null)))) - ); - assertThat( - db.tx(() -> db.complexes().streamPartial(new Complex.Id(null, null, null, null), 2).collect(toList())) - ).isEqualTo( - db.tx(() -> db.complexes().find(Range.create(new Complex.Id(null, null, null, null)))) - ); + tx.run(this::makeComplexes); + tx.run(db -> { + assertThat(db.complexes().streamPartial(new Complex.Id(0, 0L, "aaa", Complex.Status.OK), 2)) + .containsExactlyElementsOf(db.complexes().find(Range.create(new Complex.Id(0, 0L, "aaa", Complex.Status.OK)))); + assertThat(db.complexes().streamPartial(new Complex.Id(0, 0L, "aaa", null), 2)) + .containsExactlyElementsOf(db.complexes().find(Range.create(new Complex.Id(0, 0L, "aaa", null)))); + assertThat(db.complexes().streamPartial(new Complex.Id(0, 0L, null, null), 2)) + .containsExactlyElementsOf(db.complexes().find(Range.create(new Complex.Id(0, 0L, null, null)))); + assertThat(db.complexes().streamPartial(new Complex.Id(0, null, null, null), 2)) + .containsExactlyElementsOf(db.complexes().find(Range.create(new Complex.Id(0, null, null, null)))); + assertThat(db.complexes().streamPartial(new Complex.Id(null, null, null, null), 2)) + .containsExactlyElementsOf(db.complexes().find(Range.create(new Complex.Id(null, null, null, null)))); + }); } @Test public void streamPartialWithPartitioning() { - db.tx(() -> { - db.complexes().insert(new Complex(new Complex.Id(0, 0L, "0", Complex.Status.OK))); - }); - - assertThat( - db.tx(() -> db.complexes().streamPartial(new Complex.Id(0, null, null, null), 100).collect(toList())) - ).isEqualTo( - db.tx(() -> db.complexes().find(Range.create(new Complex.Id(null, null, null, null)))) - ); + tx.run(db -> db.complexes().insert(new Complex(new Complex.Id(0, 0L, "0", Complex.Status.OK)))); + tx.run(db -> { + assertThat(db.complexes().streamPartial(new Complex.Id(0, null, null, null), 100)) + .containsExactlyElementsOf(db.complexes().find(Range.create(new Complex.Id(null, null, null, null)))); - assertThat(db.tx(() -> { - ArrayList found = new ArrayList<>(); + List found = new ArrayList<>(); Iterators.partition( - db.complexes() - .streamPartial(new Complex.Id(0, null, null, null), 100) - .map(Complex::getId) - .iterator(), 100) - .forEachRemaining(found::addAll); - return found; - })).hasSize(1); + db.complexes() + .streamPartial(new Complex.Id(0, null, null, null), 100) + .map(Complex::getId) + .iterator(), + 100 + ).forEachRemaining(found::addAll); + assertThat(found).hasSize(1); + }); } @Test public void streamPartialIds() { - db.tx(this::makeComplexes); - assertThat( - db.tx(() -> db.complexes().streamPartialIds(new Complex.Id(0, 0L, "aaa", Complex.Status.OK), 100).collect(toList())) - ).isEqualTo( - db.tx(() -> db.complexes().find(Range.create(new Complex.Id(0, 0L, "aaa", Complex.Status.OK))) - .stream().map(Complex::getId).collect(toList())) - ); - assertThat( - db.tx(() -> db.complexes().streamPartialIds(new Complex.Id(0, 0L, "aaa", null), 100).collect(toList())) - ).isEqualTo( - db.tx(() -> db.complexes().find(Range.create(new Complex.Id(0, 0L, "aaa", null))) - .stream().map(Complex::getId).collect(toList())) - ); - assertThat( - db.tx(() -> db.complexes().streamPartialIds(new Complex.Id(0, 0L, null, null), 100).collect(toList())) - ).isEqualTo( - db.tx(() -> db.complexes().find(Range.create(new Complex.Id(0, 0L, null, null))) - .stream().map(Complex::getId).collect(toList())) - ); - assertThat( - db.tx(() -> db.complexes().streamPartialIds(new Complex.Id(0, null, null, null), 100).collect(toList())) - ).isEqualTo( - db.tx(() -> db.complexes().find(Range.create(new Complex.Id(0, null, null, null))) - .stream().map(Complex::getId).collect(toList())) - ); - assertThat( - db.tx(() -> db.complexes().streamPartialIds(new Complex.Id(null, null, null, null), 100).collect(toList())) - ).isEqualTo( - db.tx(() -> db.complexes().find(Range.create(new Complex.Id(null, null, null, null))) - .stream().map(Complex::getId).collect(toList())) - ); + tx.run(this::makeComplexes); + + tx.run(db -> { + assertThat(db.complexes().streamPartialIds(new Complex.Id(0, 0L, "aaa", Complex.Status.OK), 100)) + .containsExactlyElementsOf( + db.complexes().find(Range.create(new Complex.Id(0, 0L, "aaa", Complex.Status.OK))) + .stream().map(Complex::getId).toList() + ); + assertThat(db.complexes().streamPartialIds(new Complex.Id(0, 0L, "aaa", null), 100)) + .containsExactlyElementsOf( + db.complexes().find(Range.create(new Complex.Id(0, 0L, "aaa", null))) + .stream().map(Complex::getId).toList() + ); + assertThat(db.complexes().streamPartialIds(new Complex.Id(0, 0L, null, null), 100)) + .containsExactlyElementsOf( + db.complexes().find(Range.create(new Complex.Id(0, 0L, null, null))) + .stream().map(Complex::getId).toList() + ); + assertThat(db.complexes().streamPartialIds(new Complex.Id(0, null, null, null), 100)) + .containsExactlyElementsOf( + db.complexes().find(Range.create(new Complex.Id(0, null, null, null))) + .stream().map(Complex::getId).toList() + ); + assertThat(db.complexes().streamPartialIds(new Complex.Id(null, null, null, null), 100)) + .containsExactlyElementsOf( + db.complexes().find(Range.create(new Complex.Id(null, null, null, null))) + .stream().map(Complex::getId).toList() + ); + }); } @Test public void countAll() { - long allSize = db.tx(() -> db.projects().countAll()); + long allSize = tx.call(db -> db.projects().countAll()); assertThat(allSize).isEqualTo(0L); - db.tx(() -> db.projects().save(new Project(new Project.Id("1"), "p1"))); + tx.run(db -> db.projects().save(new Project(new Project.Id("1"), "p1"))); - long all1 = db.tx(() -> db.projects().countAll()); + long all1 = tx.call(db -> db.projects().countAll()); assertThat(all1).isEqualTo(1L); - db.tx(() -> db.projects().save(new Project(new Project.Id("2"), "p2"))); - long all2 = db.tx(() -> db.projects().countAll()); + tx.run(db -> db.projects().save(new Project(new Project.Id("2"), "p2"))); + long all2 = tx.call(db -> db.projects().countAll()); assertThat(all2).isEqualTo(2L); - db.tx(() -> db.projects().deleteAll()); - long all3 = db.tx(() -> db.projects().countAll()); + tx.run(db -> db.projects().deleteAll()); + long all3 = tx.call(db -> db.projects().countAll()); assertThat(all3).isEqualTo(0L); } @@ -825,9 +821,9 @@ public void streamAllIterator() { List projects = IntStream.range(100, 200) .mapToObj(i -> new Project(new Project.Id("proj" + i), null)) .collect(toList()); - db.tx(() -> db.projects().insertAll(projects)); + tx.run(db -> db.projects().insertAll(projects)); - db.tx(() -> { + tx.run(db -> { List projectsViaStreamAllIterator = new ArrayList<>(); Iterator iterator = db.projects().streamAll(1_000).iterator(); while (iterator.hasNext()) { @@ -842,9 +838,9 @@ public void streamAllMultiPageIterator() { List projects = IntStream.range(200, 300) .mapToObj(i -> new Project(new Project.Id("p" + i), null)) .collect(toList()); - db.tx(() -> db.projects().insertAll(projects)); + tx.run(db -> db.projects().insertAll(projects)); - db.tx(() -> { + tx.run(db -> { List projectsViaStreamAllIterator = new ArrayList<>(); Iterator iterator = db.projects().streamAll(13).iterator(); while (iterator.hasNext()) { @@ -856,8 +852,8 @@ public void streamAllMultiPageIterator() { @Test public void findRange() { - db.tx(this::makeComplexes); - db.tx(() -> { + tx.run(this::makeComplexes); + tx.run(db -> { assertThat(db.complexes().find(Range.create(new Complex.Id(0, 0L, "aaa", Complex.Status.OK)))).hasSize(1); assertThat(db.complexes().find(Range.create(new Complex.Id(0, 0L, "aaa", null)))).hasSize(2); assertThat(db.complexes().find(Range.create(new Complex.Id(0, 0L, null, null)))).hasSize(6); @@ -902,7 +898,7 @@ private RepositoryTransaction startTransaction() { return repository.startTransaction(TxOptions.create(IsolationLevel.SERIALIZABLE_READ_WRITE)); } - protected void makeComplexes() { + private void makeComplexes(TestDb db) { for (int a = 0; a < 3; a++) { for (long b = 0; b < 3; b++) { for (String c : List.of("aaa", "aab", "bbb")) { @@ -916,8 +912,8 @@ protected void makeComplexes() { @Test public void findInCompleteIdAllFromCache() { - db.tx(this::makeComplexes); - db.tx(() -> { + tx.run(this::makeComplexes); + tx.run(db -> { var idsToFetch = Set.of( new Id(0, 0L, "aaa", Complex.Status.OK), new Id(0, 0L, "aaa", Complex.Status.FAIL) @@ -953,8 +949,8 @@ Test that find(Set<>) with complete Ids: var idUnexistent = new Id(0, -1L, "aaa", Complex.Status.OK); var idUnexistentMarkedEmptyInCache = new Id(0, -2L, "aaa", Complex.Status.OK); - db.tx(this::makeComplexes); - db.tx(() -> { + tx.run(this::makeComplexes); + tx.run(db -> { var entryInDbAndCache = db.complexes().find(idInDbAndCache); assertThat(entryInDbAndCache).isNotNull(); assertThat(db.complexes().find(idUnexistentMarkedEmptyInCache)).isNull(); @@ -979,7 +975,6 @@ Test that find(Set<>) with complete Ids: // this fetched from db and should exist assertThat(result.get(idInDbOnly)).isNotNull(); }); - } @Test @@ -999,13 +994,13 @@ Test that find(Set<>) with partial Ids: var idUnexistentMarkedEmptyInCache = new Id(0, -2L, "aaa", Complex.Status.OK); var createdEntries = new HashMap(); - db.tx(() -> { + tx.run(db -> { var entryOne = db.complexes().insert(new Complex(idInDbOnly)); var entryTwo = db.complexes().insert(new Complex(idInDbAndUpdatedInCache)); createdEntries.put(entryOne.getId(), entryOne); createdEntries.put(entryTwo.getId(), entryTwo); }); - db.tx(() -> { + tx.run(db -> { var updatedEntry = new Complex(idInDbAndUpdatedInCache, "UPDATED"); db.complexes().save(updatedEntry); assertThat(db.complexes().find(idUnexistentMarkedEmptyInCache)).isNull(); @@ -1036,12 +1031,12 @@ public void findInCorrectSort() { var idB = new Id(0, 0L, "bbb", Complex.Status.OK); var idC = new Id(0, 0L, "ccc", Complex.Status.OK); - db.tx(() -> { + tx.run(db -> { db.complexes().insert(new Complex(idA, "A")); db.complexes().insert(new Complex(idB, "B")); db.complexes().insert(new Complex(idC, "C")); }); - db.tx(() -> { + tx.run(db -> { assertThat(db.complexes().find(Set.of(idA, idB, idC))) .extracting(Entity::getId) .containsExactly(idA, idB, idC); @@ -1060,11 +1055,11 @@ public void findInConsiderDeleted() { var firstId = new Id(0, 0L, "aaa", Complex.Status.OK); var secondId = new Id(0, 1L, "aaa", Complex.Status.OK); - db.tx(() -> { + tx.run(db -> { db.complexes().insert(new Complex(firstId)); db.complexes().insert(new Complex(secondId)); }); - db.tx(() -> { + tx.run(db -> { var result = db.complexes().find(Set.of( firstId, secondId @@ -1132,7 +1127,7 @@ private Complex getComplex(int index) { } private void fillComplexTableForFindIn() { - db.tx(() -> IntStream.range(0, 4).mapToObj(this::getComplex).forEach(db.complexes()::save)); + tx.run(db -> IntStream.range(0, 4).mapToObj(this::getComplex).forEach(db.complexes()::save)); } @Test @@ -1154,7 +1149,7 @@ public void findInPrefixedIdsFilteredAndOrdered() { private void findInIdsFilteredAndOrdered(Set ids) { fillComplexTableForFindIn(); - var actual = db.tx(() -> db.complexes().query() + var actual = tx.call(db -> db.complexes().query() .ids(ids) .filter(fb -> fb.where("id.d").eq(Complex.Status.OK)) .orderBy(ob -> ob.orderBy("value").descending()) @@ -1183,7 +1178,7 @@ public void findInPrefixedIdsViewFilteredAndOrdered() { public void findInIdsViewFilteredAndOrdered(Set ids) { fillComplexTableForFindIn(); - var actual = db.tx(() -> db.complexes().query() + var actual = tx.call(db -> db.complexes().query() .ids(ids) .filter(fb -> fb.where("id.d").eq(Complex.Status.OK)) .orderBy(ob -> ob.orderBy("value").descending()) @@ -1222,7 +1217,7 @@ private IndexedEntity getIndexedEntity(int index) { } private void fillIndexedTableForFindIn() { - db.tx(() -> IntStream.range(0, 8).mapToObj(this::getIndexedEntity).forEach(db.indexedTable()::save)); + tx.run(db -> IntStream.range(0, 8).mapToObj(this::getIndexedEntity).forEach(db.indexedTable()::save)); } @Test @@ -1264,7 +1259,7 @@ private void findInPrefixedKeysFilteredAndOrdered(boolean limited) { private void findInKeysFilteredAndOrdered(Set keys, boolean limited) { fillIndexedTableForFindIn(); - var actual = db.tx(() -> { + List actual = tx.call(db -> { var queryBuilder = db.indexedTable().query() .index(IndexedEntity.VALUE_INDEX) .keys(keys) @@ -1312,7 +1307,7 @@ public void findInPrefixedKeysViewFilteredAndOrderedLimited() { public void findViewDistinct() { fillIndexedTableForFindIn(); - var actualViews = db.tx(() -> db.indexedTable().query().find(IndexedEntity.ValueIdView.class, true)); + var actualViews = tx.call(db -> db.indexedTable().query().find(IndexedEntity.ValueIdView.class, true)); var actualValueIds = actualViews.stream() .map(IndexedEntity.ValueIdView::getValueId) @@ -1339,7 +1334,7 @@ private void findInPrefixedKeysViewFilteredAndOrdered(boolean limited) { private void findInKeysViewFilteredAndOrdered(Set keys, boolean limited) { fillIndexedTableForFindIn(); - var actual = db.tx(() -> { + List actual = tx.call(db -> { var queryBuilder = db.indexedTable().query() .index(IndexedEntity.VALUE_INDEX) .keys(keys) @@ -1363,7 +1358,7 @@ private void findInKeysViewFilteredAndOrdered(Set keys, boole @Test public void indexMustExistToBeUsedInQueryBuilder() { assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> - db.tx(() -> db.indexedTable().query() + tx.run(db -> db.indexedTable().query() .index("nonexistent_index", IndexOrder.DESCENDING) .where("valueId2").neq(getIndexedEntityValue2(2)) .limit(5) @@ -1376,31 +1371,31 @@ public void indexMustExistToBeUsedInQueryBuilder() { public void uniqueIndex() { String verySameName = "valuableName"; UniqueProject ue1 = new UniqueProject(new UniqueProject.Id("id1"), verySameName, 1); - db.tx(() -> db.table(UniqueProject.class).save(ue1)); - db.tx(() -> db.table(UniqueProject.class).save(ue1).withVersion(2)); // no exception + tx.run(db -> db.table(UniqueProject.class).save(ue1)); + tx.run(db -> db.table(UniqueProject.class).save(ue1.withVersion(2))); // no exception UniqueProject ue2 = new UniqueProject(new UniqueProject.Id("id2"), verySameName, 1); assertThatExceptionOfType(EntityAlreadyExistsException.class) - .isThrownBy(() -> db.tx(() -> db.table(UniqueProject.class).save(ue2))); + .isThrownBy(() -> tx.run(db -> db.table(UniqueProject.class).save(ue2))); } @Test public void sameEntityMultipleTables() { String verySameName = "valuableName"; UniqueProject ue1 = new UniqueProject(new UniqueProject.Id("id1"), verySameName, 1); - db.tx(() -> { + tx.run(db -> { db.table(UniqueProject.class).save(ue1); db.table(TestEntities.SECOND_UNIQUE_PROJECT_TABLE).save(ue1.withVersion(2)); }); - UniqueProject firstTableProject = db.tx(() -> db.table(UniqueProject.class).find(ue1.getId())); - UniqueProject secondTableProject = db.tx(() -> db.table(TestEntities.SECOND_UNIQUE_PROJECT_TABLE).find(ue1.getId())); + UniqueProject firstTableProject = tx.call(db -> db.table(UniqueProject.class).find(ue1.getId())); + UniqueProject secondTableProject = tx.call(db -> db.table(TestEntities.SECOND_UNIQUE_PROJECT_TABLE).find(ue1.getId())); assertThat(firstTableProject.getVersion()).isEqualTo(1); assertThat(secondTableProject.getVersion()).isEqualTo(2); - db.tx(() -> db.table(UniqueProject.class).delete(ue1.getId())); + tx.run(db -> db.table(UniqueProject.class).delete(ue1.getId())); - firstTableProject = db.tx(() -> db.table(UniqueProject.class).find(ue1.getId())); - secondTableProject = db.tx(() -> db.table(TestEntities.SECOND_UNIQUE_PROJECT_TABLE).find(ue1.getId())); + firstTableProject = tx.call(db -> db.table(UniqueProject.class).find(ue1.getId())); + secondTableProject = tx.call(db -> db.table(TestEntities.SECOND_UNIQUE_PROJECT_TABLE).find(ue1.getId())); assertThat(firstTableProject).isNull(); assertThat(secondTableProject.getVersion()).isEqualTo(2); @@ -1408,24 +1403,24 @@ public void sameEntityMultipleTables() { @Test public void doubleTxIsOk() { - db.tx(this::findRange); + tx.tx(this::findRange); } @Test(expected = IllegalArgumentException.class) public void findPartialId() { - db.tx(() -> db.bubbles().find(new Bubble.Id("b", null))); + tx.call(db -> db.bubbles().find(new Bubble.Id("b", null))); } @Test public void fixSnapshotVersionOnFirstQuery() { var projectId = new Project.Id("value"); - db.tx(() -> db.tx(() -> db.projects().insert(new Project(projectId, "oldName")))); + tx.run(db1 -> tx.run(db2 -> db2.projects().insert(new Project(projectId, "oldName")))); String newName = "name"; - Project project = db.tx(() -> { - db.separate().tx(() -> db.projects().save(new Project(projectId, newName))); - return db.projects().find(projectId); + Project project = tx.call(db1 -> { + tx.separate().run(db2 -> db2.projects().save(new Project(projectId, newName))); + return db1.projects().find(projectId); }); assertThat(project.getName()).isEqualTo(newName); } @@ -1434,34 +1429,34 @@ public void fixSnapshotVersionOnFirstQuery() { public void dontCommitOnUserError() { var projectId = new Project.Id("value"); try { - db.delayedWrites().tx(() -> { + tx.delayedWrites().call(db -> { db.projects().save(new Project(projectId, "name")); throw new RuntimeException(""); }); } catch (RuntimeException ignore) { } - assertThat(db.tx(() -> db.projects().find(projectId))).isNull(); + assertThatObject(tx.call(db -> db.projects().find(projectId))).isNull(); try { - db.immediateWrites().tx(() -> { + tx.immediateWrites().run(db -> { db.projects().save(new Project(projectId, "name")); throw new RuntimeException(""); }); } catch (RuntimeException ignore) { } - assertThat(db.tx(() -> db.projects().find(projectId))).isNull(); + assertThatObject(tx.call(db -> db.projects().find(projectId))).isNull(); } @Test public void immediateWrites() { - db.delayedWrites().noFirstLevelCache().tx(() -> { + tx.delayedWrites().noFirstLevelCache().run(db -> { var projectId = new Project.Id("value1"); assertThat(db.projects().find(projectId)).isNull(); db.projects().save(new Project(projectId, "name")); assertThat(db.projects().find(projectId)).isNull(); }); - db.immediateWrites().noFirstLevelCache().tx(() -> { + tx.immediateWrites().noFirstLevelCache().run(db -> { var projectId = new Project.Id("value2"); var name = "name"; assertThat(db.projects().find(projectId)).isNull(); @@ -1475,27 +1470,27 @@ public void snapshotReadWithoutTli() { var projectId = new Project.Id("value"); String newName = "name"; - db.tx(() -> db.tx(() -> db.projects().insert(new Project(projectId, newName)))); + tx.run(db1 -> tx.call(db2 -> db2.projects().insert(new Project(projectId, newName)))); - Project project = db.tx(() -> { - Project findedProject = db.projects().find(projectId); - db.separate().tx(() -> db.projects().save(new Project(projectId, "invisible"))); + Project project = tx.call(db1 -> { + Project findedProject = db1.projects().find(projectId); + tx.separate().run(db2 -> db2.projects().save(new Project(projectId, "invisible"))); return findedProject; }); assertThat(project.getName()).isEqualTo(newName); - assertThat(db.tx(() -> db.projects().find(projectId)).getName()).isEqualTo("invisible"); + assertThat(tx.call(db -> db.projects().find(projectId)).getName()).isEqualTo("invisible"); - project = db.tx(() -> { - db.separate().tx(() -> db.projects().save(new Project(projectId, "invisible"))); - db.projects().find(projectId); - return db.projects().save(new Project(projectId, newName)); + project = tx.call(db1 -> { + tx.separate().run(db2 -> db2.projects().save(new Project(projectId, "invisible"))); + db1.projects().find(projectId); + return db1.projects().save(new Project(projectId, newName)); }); assertThat(project.getName()).isEqualTo(newName); } @Test public void findByPredicate() { - db.tx(() -> { + tx.run(db -> { db.projects().insert( new Project(new Project.Id("unnamed-p1"), null), new Project(new Project.Id("named-p2"), "P2"), @@ -1515,7 +1510,7 @@ public void findByPredicate() { ); }); - db.tx(() -> { + tx.run(db -> { assertThat(db.projects().findNamed()).containsExactlyInAnyOrder( new Project(new Project.Id("named-p3"), "P3"), new Project(new Project.Id("named-p2"), "P2") @@ -1550,12 +1545,12 @@ public void findByPredicateWithLimit() { .mapToObj(i -> new Project(new Project.Id("named-p" + i), "P" + i)) .collect(toList()); - db.tx(() -> { + tx.run(db -> { db.projects().insert(new Project(new Project.Id("unnamed-p1"), null)); db.projects().insertAll(named); }); - db.tx(() -> { + tx.run(db -> { final List topNamed = db.projects().findTopNamed(10); assertThat(topNamed).hasSize(10); }); @@ -1614,7 +1609,7 @@ protected TypeFreak newTypeFreak(int n, String a, String b) { @Test public void simpleCrudInTheSameTx() { - db.tx(() -> { + tx.run(db -> { Project p1 = new Project(new Project.Id("1"), "named"); db.projects().save(p1); // save() is pending until end of tx @@ -1627,7 +1622,7 @@ public void simpleCrudInTheSameTx() { Project p4 = db.projects().find(new Project.Id("1")); assertThat(p4).isEqualTo(new Project(new Project.Id("1"), "renamed")); }); - db.tx(() -> { + tx.run(db -> { Project p1 = db.projects().find(new Project.Id("1")); assertThat(p1).isEqualTo(new Project(new Project.Id("1"), "renamed")); db.projects().delete(new Project.Id("1")); // delete() is pending @@ -1635,7 +1630,7 @@ public void simpleCrudInTheSameTx() { Project p5 = db.projects().find(new Project.Id("1")); assertThat(p5).isNull(); // now it's deleted }); - db.tx(() -> { + tx.run(db -> { Project p1 = db.projects().find(new Project.Id("1")); assertThat(p1).isNull(); // now it's deleted }); @@ -1659,7 +1654,7 @@ public void optimisticLockWrite() { assertThatExceptionOfType(OptimisticLockException.class) .isThrownBy(tx3::commit); - Project p4 = db.tx(() -> db.table(Project.class).find(new Project.Id("1"))); + Project p4 = tx.call(db -> db.table(Project.class).find(new Project.Id("1"))); assertThat(p4).isEqualTo(new Project(new Project.Id("1"), p2.getName() + "y")); } @@ -1703,7 +1698,7 @@ public void concurrentWriteIsOk() { tx2.table(Project.class).save(new Project(new Project.Id("1"), "y")); tx1.commit(); tx2.commit(); - assertThat(db.tx(() -> db.table(Project.class).find(new Project.Id("1")))) + assertThatObject(tx.call(db -> db.table(Project.class).find(new Project.Id("1")))) .isEqualTo(new Project(new Project.Id("1"), "y")); } @@ -1723,14 +1718,14 @@ public void concurrentReadIsOk() { @Test public void allTypes() { - TypeFreak t1 = db.tx(() -> db.typeFreaks().save(newTypeFreak(1, "aaa", "bbb"))); - TypeFreak t2 = db.tx(() -> db.typeFreaks().find(new TypeFreak.Id("tf", 1))); + TypeFreak t1 = tx.call(db -> db.typeFreaks().save(newTypeFreak(1, "aaa", "bbb"))); + TypeFreak t2 = tx.call(db -> db.typeFreaks().find(new TypeFreak.Id("tf", 1))); assertThat(t2).isEqualTo(t1); } @Test public void allTypesNull() { - TypeFreak t1 = db.tx(() -> { + TypeFreak t1 = tx.call(db -> { TypeFreak t = new TypeFreak(new TypeFreak.Id("x", 1), true, (byte) 111, @@ -1774,13 +1769,13 @@ public void allTypesNull() { db.typeFreaks().save(t); return t; }); - TypeFreak t2 = db.tx(() -> db.typeFreaks().find(new TypeFreak.Id("x", 1))); + TypeFreak t2 = tx.call(db -> db.typeFreaks().find(new TypeFreak.Id("x", 1))); assertThat(t2).isEqualTo(t1); } @Test public void allTypesWithNulls() { - TypeFreak t1 = db.tx(() -> { + TypeFreak t1 = tx.call(db -> { TypeFreak t = new TypeFreak(new TypeFreak.Id("x", 1), true, (byte) 111, @@ -1824,7 +1819,7 @@ public void allTypesWithNulls() { db.typeFreaks().save(t); return t; }); - TypeFreak t2 = db.tx(() -> db.typeFreaks().find(new TypeFreak.Id("x", 1))); + TypeFreak t2 = tx.call(db -> db.typeFreaks().find(new TypeFreak.Id("x", 1))); assertThat(t2).isEqualTo(t1); } @@ -1840,14 +1835,14 @@ public void refs() { List.of(p1.getId(), p2.getId()), List.of(c1.getId(), c2.getId()) ); - db.tx(() -> { + tx.run(db -> { db.projects().save(p1); db.projects().save(p2); db.complexes().save(c1); db.complexes().save(c2); db.referrings().save(r); }); - db.tx(() -> { + tx.run(db -> { assertThat(db.referrings().find(r.getId())).isEqualTo(r); assertThat(db.projects().find(r.getProject())).isEqualTo(p1); assertThat(db.complexes().find(r.getComplex())).isEqualTo(c1); @@ -1860,7 +1855,7 @@ public void refs() { public void severalWriteOperationsInTX() { Complex.Id complexId = new Complex.Id(1, 1L, "c", Complex.Status.OK); - db.tx(() -> { + tx.run(db -> { db.projects().save(new Project(new Project.Id("1"), "p11")); db.projects().save(new Project(new Project.Id("2"), "p2")); db.projects().save(new Project(new Project.Id("1"), "p12")); @@ -1871,7 +1866,7 @@ public void severalWriteOperationsInTX() { db.projects().delete(new Project.Id("2")); }); - db.tx(() -> { + tx.run(db -> { assertThat(db.projects().find(new Project.Id("1"))).isNull(); assertThat(db.projects().find(new Project.Id("2"))).isNull(); assertThat(db.complexes().find(complexId)).isNull(); @@ -1880,7 +1875,7 @@ public void severalWriteOperationsInTX() { @Test public void insert() { - db.tx(() -> { + tx.run(db -> { db.projects().insert(new Project(new Project.Id("unnamed-p1"), null)); db.projects().insert(new Project(new Project.Id("named-p2"), "P2")); db.projects().insert(new Project(new Project.Id("named-p3"), "P3")); @@ -1889,24 +1884,20 @@ public void insert() { Project p1 = new Project(new Project.Id("1"), "p1"); Complex complex = new Complex(complexId); - db.tx(() -> { + tx.run(db -> { db.projects().insert(p1); db.complexes().insert(complex); }); - db.tx(() -> { + tx.run(db -> { assertThat(db.projects().find(new Project.Id("1"))).isEqualTo(p1); assertThat(db.complexes().find(complexId)).isEqualTo(complex); }); assertThatExceptionOfType(EntityAlreadyExistsException.class) - .isThrownBy(() -> db.tx(() -> { - db.projects().insert(p1); - })); + .isThrownBy(() -> tx.run(db -> db.projects().insert(p1))); assertThatExceptionOfType(EntityAlreadyExistsException.class) - .isThrownBy(() -> db.tx(() -> { - db.complexes().insert(complex); - })); + .isThrownBy(() -> tx.run(db -> db.complexes().insert(complex))); } @@ -1917,18 +1908,18 @@ public void alreadyExistsOnCommit() { //this is a problem for one tx. assertThatExceptionOfType(EntityAlreadyExistsException.class) - .isThrownBy(() -> db.tx(() -> { + .isThrownBy(() -> tx.run(db -> { db.projects().insert(p1); //this is a problem for one tx. db.projects().insert(p12); })); - db.tx(() -> db.projects().insert(p1)); + tx.run(db -> db.projects().insert(p1)); AtomicBoolean executed = new AtomicBoolean(false); //already exists only on commit assertThatExceptionOfType(EntityAlreadyExistsException.class) - .isThrownBy(() -> db.tx(() -> { + .isThrownBy(() -> tx.run(db -> { db.projects().insert(p1); //already exists only on commit executed.set(true); @@ -1938,26 +1929,24 @@ public void alreadyExistsOnCommit() { @Test public void updateSimpleFieldById() { - db.tx(() -> db.projects().insert(new Project(new Project.Id("1"), "p1"))); + tx.run(db -> db.projects().insert(new Project(new Project.Id("1"), "p1"))); - db.tx(() -> db.projects().updateName(new Project.Id("1"), "NEW-P1-NAME")); + tx.run(db -> db.projects().updateName(new Project.Id("1"), "NEW-P1-NAME")); - db.tx(() -> { - assertThat(db.projects().find(new Project.Id("1"))) - .isNotNull() - .extracting(Project::getName).isEqualTo("NEW-P1-NAME"); - }); + tx.run(db -> assertThat(db.projects().find(new Project.Id("1"))) + .isNotNull() + .extracting(Project::getName).isEqualTo("NEW-P1-NAME")); } @Test public void updateComplexFieldByComplexId() { TypeFreak tf = newTypeFreak(100500, "AAA", "BBB"); - db.tx(() -> db.typeFreaks().insert(tf)); + tx.run(db -> db.typeFreaks().insert(tf)); Embedded newEmbedded = new Embedded(new A("ZZZ"), new B("YYY")); - db.tx(() -> db.typeFreaks().updateEmbedded(tf.getId(), newEmbedded)); + tx.run(db -> db.typeFreaks().updateEmbedded(tf.getId(), newEmbedded)); - db.tx(() -> { + tx.run(db -> { assertThat(db.typeFreaks().find(tf.getId())) .isNotNull() .extracting(TypeFreak::getEmbedded).isEqualTo(newEmbedded); @@ -1966,14 +1955,12 @@ public void updateComplexFieldByComplexId() { @Test public void findByComplexIdUsingPredicates() { - db.tx(() -> db.typeFreaks().insert( + tx.run(db -> db.typeFreaks().insert( newTypeFreak(0, "AAA1", "bbb"), newTypeFreak(1, "AAA2", "bbb"))); - db.tx(() -> { - assertThat(db.typeFreaks().findByPredicateWithComplexId(new TypeFreak.Id("tf", 0))) - .isEqualTo(newTypeFreak(0, "AAA1", "bbb")); - }); + tx.run(db -> assertThat(db.typeFreaks().findByPredicateWithComplexId(new TypeFreak.Id("tf", 0))) + .isEqualTo(newTypeFreak(0, "AAA1", "bbb"))); } @Test @@ -1981,9 +1968,9 @@ public void findByManySimpleIdsUsingPredicates() { Project projA = new Project(new Project.Id("A"), "aaa"); Project projB = new Project(new Project.Id("B"), "bbb"); Project proj0 = new Project(new Project.Id("0"), "000"); - db.tx(() -> db.projects().insert(projA, projB, proj0)); + tx.run(db -> db.projects().insert(projA, projB, proj0)); - db.tx(() -> { + tx.run(db -> { assertThat(db.projects().findByPredicateWithManyIds(Set.of(new Project.Id("A"), new Project.Id("B")))) .containsOnly(projA, projB); assertThat(db.projects().findByPredicateWithManyIdValues(Set.of("A", "B"))) @@ -1994,11 +1981,11 @@ public void findByManySimpleIdsUsingPredicates() { @Test public void findViewById() { TypeFreak tf1 = newTypeFreak(0, "AAA1", "bbb"); - db.tx(() -> db.typeFreaks().insert( + tx.run(db -> db.typeFreaks().insert( tf1, newTypeFreak(1, "AAA2", "bbb"))); - db.tx(() -> { + tx.run(db -> { TypeFreak.View found = db.typeFreaks().find(TypeFreak.View.class, tf1.getId()); assertThat(found).isEqualTo(new TypeFreak.View(tf1.getId(), tf1.getEmbedded())); }); @@ -2007,11 +1994,11 @@ public void findViewById() { @Test public void findViewTypeConversion() { TypeFreak tf1 = newTypeFreak(0, "AAA1", "bbb"); - db.tx(() -> db.typeFreaks().insert( + tx.run(db -> db.typeFreaks().insert( tf1, newTypeFreak(1, "AAA2", "bbb"))); - db.tx(() -> { + tx.run(db -> { TypeFreak.StringView found = db.typeFreaks().find(TypeFreak.StringView.class, tf1.getId()); assertThat(found.getId()).isEqualTo(tf1.getId()); // ...sure looks like JSON to me: @@ -2037,9 +2024,9 @@ public void findViewByIdRange() { expectedViews.get(expectedViews.size() - 1).getId() ); - db.tx(() -> db.typeFreaks().insertAll(toInsert)); + tx.run(db -> db.typeFreaks().insertAll(toInsert)); - db.tx(() -> { + tx.run(db -> { List found = db.typeFreaks().find(TypeFreak.View.class, findRange); assertThat(found).containsExactlyInAnyOrderElementsOf(expectedViews); }); @@ -2055,9 +2042,9 @@ public void findAllViews() { expectedViews.add(new TypeFreak.View(tf.getId(), tf.getEmbedded())); } - db.tx(() -> db.typeFreaks().insertAll(toInsert)); + tx.run(db -> db.typeFreaks().insertAll(toInsert)); - db.tx(() -> { + tx.run(db -> { List found = db.typeFreaks().findAll(TypeFreak.View.class); assertThat(found).containsExactlyInAnyOrderElementsOf(expectedViews); }); @@ -2077,29 +2064,26 @@ public void findViewByPredicate() { embeddedAsToFind.add(embeddedA); } - db.tx(() -> db.typeFreaks().insertAll(toInsert)); + tx.run(db -> db.typeFreaks().insertAll(toInsert)); - db.tx(() -> { - List found = db.typeFreaks().findViewWithEmbeddedAIn(embeddedAsToFind); - assertThat(found).containsExactlyInAnyOrderElementsOf(toFind); - }); + List found = tx.call(db -> db.typeFreaks().findViewWithEmbeddedAIn(embeddedAsToFind)); + assertThat(found).containsExactlyInAnyOrderElementsOf(toFind); } @Test public void findRangeWithPrimitiveId() { - db.tx(() -> db.primitives().insert( + tx.run(db -> db.primitives().insert( new Primitive(new Primitive.Id(1), 100500), new Primitive(new Primitive.Id(42), 9000), new Primitive(new Primitive.Id(-100500), 0) )); - db.tx(() -> { - assertThat(db.primitives().find(Range.create(new Primitive.Id(-5), new Primitive.Id(50)))) - .containsOnly( - new Primitive(new Primitive.Id(1), 100500), - new Primitive(new Primitive.Id(42), 9000) - ); - }); + List found = tx.call(db -> db.primitives() + .find(Range.create(new Primitive.Id(-5), new Primitive.Id(50)))); + assertThat(found).containsOnly( + new Primitive(new Primitive.Id(1), 100500), + new Primitive(new Primitive.Id(42), 9000) + ); } @Test @@ -2109,9 +2093,9 @@ public void findAllResultsAreOrderedByIdAscending() { Complex c3 = new Complex(new Complex.Id(999_999, 15L, "UUU", Complex.Status.OK)); Complex c4 = new Complex(new Complex.Id(999_999, 0L, "UUU", Complex.Status.OK)); Complex c5 = new Complex(new Complex.Id(999_000, 0L, "UUU", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4, c5)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4, c5)); - List found = db.tx(() -> db.complexes().findAll()); + List found = tx.call(db -> db.complexes().findAll()); assertThat(found).containsExactly(c5, c4, c3, c2, c1); } @@ -2123,9 +2107,9 @@ public void findByRangeResultsAreOrderedByIdAscending() { Complex c4 = new Complex(new Complex.Id(999_999, 0L, "UUU", Complex.Status.OK)); Complex c5 = new Complex(new Complex.Id(999_000, 0L, "UUU", Complex.Status.OK)); Complex c6 = new Complex(new Complex.Id(999_000, 0L, "AAA", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4, c5, c6)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4, c5, c6)); - List found = db.tx(() -> db.complexes().find( + List found = tx.call(db -> db.complexes().find( Range.create( new Complex.Id(999_000, 0L, "AAA", null), new Complex.Id(999_000, 0L, "UUU", null) @@ -2135,13 +2119,13 @@ public void findByRangeResultsAreOrderedByIdAscending() { @Test public void findByPredicateResultsAreOrderedByIdAscending() { - db.tx(() -> db.projects().insert( + tx.run(db -> db.projects().insert( new Project(new Project.Id("named-p3"), "P3"), new Project(new Project.Id("unnamed-p1"), null), new Project(new Project.Id("named-p2"), "P2") )); - assertThat(db.tx(() -> db.projects().findNamed())).containsExactly( + assertThatList(tx.call(db -> db.projects().findNamed())).containsExactly( new Project(new Project.Id("named-p2"), "P2"), new Project(new Project.Id("named-p3"), "P3") ); @@ -2149,53 +2133,51 @@ public void findByPredicateResultsAreOrderedByIdAscending() { @Test public void projections() { - db.tx(() -> { + tx.run(db -> { db.table(Book.class).save(new Book(new Book.Id("1"), 1, "title1", List.of("author1"))); db.table(Book.class).save(new Book(new Book.Id("2"), 1, "title2", List.of("author2"))); db.table(Book.class).save(new Book(new Book.Id("3"), 1, null, List.of("author1", "author2"))); db.table(Book.class).save(new Book(new Book.Id("4"), 1, "title1", List.of())); }); - assertThat(db.tx(() -> db.table(Book.ByTitle.class).countAll())) + assertThat((long) tx.call(db -> db.table(Book.ByTitle.class).countAll())) .isEqualTo(3L); - assertThat(db.tx(() -> db.table(Book.ByTitle.class).find(Range.create(new Book.ByTitle.Id("title1", null))))) + assertThatList(tx.call(db -> db.table(Book.ByTitle.class).find(Range.create(new Book.ByTitle.Id("title1", null))))) .hasSize(2); - assertThat(db.tx(() -> db.table(Book.ByTitle.class).find(Range.create(new Book.ByTitle.Id("title2", null))))) + assertThatList(tx.call(db -> db.table(Book.ByTitle.class).find(Range.create(new Book.ByTitle.Id("title2", null))))) .hasSize(1); - assertThat(db.tx(() -> db.table(Book.ByAuthor.class).countAll())) + assertThat((long) tx.call(db -> db.table(Book.ByAuthor.class).countAll())) .isEqualTo(4L); - assertThat(db.tx(() -> db.table(Book.ByAuthor.class).find(Range.create(new Book.ByAuthor.Id("author1", null))))) + assertThatList(tx.call(db -> db.table(Book.ByAuthor.class).find(Range.create(new Book.ByAuthor.Id("author1", null))))) .hasSize(2); - assertThat(db.tx(() -> db.table(Book.ByAuthor.class).find(Range.create(new Book.ByAuthor.Id("author2", null))))) + assertThatList(tx.call(db -> db.table(Book.ByAuthor.class).find(Range.create(new Book.ByAuthor.Id("author2", null))))) .hasSize(2); - db.tx(() -> { + tx.run(db -> { db.table(Book.class).modifyIfPresent(new Book.Id("1"), b -> b.updateTitle("title2")); db.table(Book.class).modifyIfPresent(new Book.Id("2"), b -> b.updateTitle(null)); db.table(Book.class).modifyIfPresent(new Book.Id("3"), b -> b.withAuthors(List.of("author2"))); db.table(Book.class).modifyIfPresent(new Book.Id("4"), b -> b.withAuthors(List.of("author1", "author2"))); }); - assertThat(db.tx(() -> db.table(Book.ByTitle.class).countAll())) + assertThat((long) tx.call(db -> db.table(Book.ByTitle.class).countAll())) .isEqualTo(2L); - assertThat(db.tx(() -> db.table(Book.ByTitle.class).find(Range.create(new Book.ByTitle.Id("title1", null))))) + assertThatList(tx.call(db -> db.table(Book.ByTitle.class).find(Range.create(new Book.ByTitle.Id("title1", null))))) .hasSize(1); - assertThat(db.tx(() -> db.table(Book.ByTitle.class).find(Range.create(new Book.ByTitle.Id("title2", null))))) + assertThatList(tx.call(db -> db.table(Book.ByTitle.class).find(Range.create(new Book.ByTitle.Id("title2", null))))) .hasSize(1); - assertThat(db.tx(() -> db.table(Book.ByAuthor.class).countAll())) + assertThat((long) tx.call(db -> db.table(Book.ByAuthor.class).countAll())) .isEqualTo(5L); - assertThat(db.tx(() -> db.table(Book.ByAuthor.class).find(Range.create(new Book.ByAuthor.Id("author1", null))))) + assertThatList(tx.call(db -> db.table(Book.ByAuthor.class).find(Range.create(new Book.ByAuthor.Id("author1", null))))) .hasSize(2); - assertThat(db.tx(() -> db.table(Book.ByAuthor.class).find(Range.create(new Book.ByAuthor.Id("author2", null))))) + assertThatList(tx.call(db -> db.table(Book.ByAuthor.class).find(Range.create(new Book.ByAuthor.Id("author2", null))))) .hasSize(3); - db.tx(() -> db.table(Book.class).findAll().forEach(b -> db.table(Book.class).delete(b.getId()))); - assertThat(db.tx(() -> db.table(Book.ByTitle.class).countAll())) - .isEqualTo(0L); - assertThat(db.tx(() -> db.table(Book.ByAuthor.class).countAll())) - .isEqualTo(0L); + tx.run(db -> db.table(Book.class).findAll().forEach(b -> db.table(Book.class).delete(b.getId()))); + assertThat((long) tx.call(db -> db.table(Book.ByTitle.class).countAll())).isEqualTo(0L); + assertThat((long) tx.call(db -> db.table(Book.ByAuthor.class).countAll())).isEqualTo(0L); } /** @@ -2223,22 +2205,22 @@ public void rangeLock() { } private void parallelTx(boolean shouldThrown, boolean writeTx, Consumer> consumer) { - RepositoryTransaction tx = startTransaction(); + RepositoryTransaction repositoryTransaction = startTransaction(); if (writeTx) { - tx.table(Book.class).save(new Book(new Book.Id("1"), 1, "title1", List.of("author1"))); + repositoryTransaction.table(Book.class).save(new Book(new Book.Id("1"), 1, "title1", List.of("author1"))); } - consumer.accept(tx.table(Complex.class)); + consumer.accept(repositoryTransaction.table(Complex.class)); - db.tx(() -> db.complexes().insert(new Complex(new Complex.Id(1, 2L, "c", Complex.Status.OK)))); + tx.run(db -> db.complexes().insert(new Complex(new Complex.Id(1, 2L, "c", Complex.Status.OK)))); Runnable runnable = () -> { try { - consumer.accept(tx.table(Complex.class)); + consumer.accept(repositoryTransaction.table(Complex.class)); } catch (Exception e) { - tx.rollback(); + repositoryTransaction.rollback(); throw e; } - tx.commit(); + repositoryTransaction.commit(); }; if (shouldThrown) { assertThatExceptionOfType(OptimisticLockException.class).isThrownBy(runnable::run); @@ -2246,7 +2228,7 @@ private void parallelTx(boolean shouldThrown, boolean writeTx, Consumer db.complexes().deleteAll()); + tx.run(db -> db.complexes().deleteAll()); } @Test @@ -2254,7 +2236,7 @@ public void businessExceptionInTx() { class BusinessException extends RuntimeException { } - assertThatExceptionOfType(BusinessException.class).isThrownBy(() -> db.tx(() -> { + assertThatExceptionOfType(BusinessException.class).isThrownBy(() -> tx.run(db -> { db.primitives().find(new Primitive.Id(25)); throw new BusinessException(); })); @@ -2263,37 +2245,39 @@ class BusinessException extends RuntimeException { @Test public void readOnlyTransaction() { assertThatExceptionOfType(IllegalTransactionIsolationLevelException.class) - .isThrownBy(() -> db.readOnly().run(() -> db.projects().save(new Project(new Project.Id("13"), "p13")))) + // FIXME + .isThrownBy(() -> tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().save(new Project(new Project.Id("13"), "p13")))) .withMessage("Mutable operations are not allowed for isolation level ONLINE_CONSISTENT_READ_ONLY"); } @Test public void ctorValidationFailure() { EntityWithValidation goodValue = new EntityWithValidation(new EntityWithValidation.Id("hey"), 43L); - db.tx(() -> { + tx.run(db -> { db.entitiesWithValidation().save(goodValue); db.entitiesWithValidation().save(EntityWithValidation.BAD_VALUE); }); - assertThat(db.tx(() -> db.entitiesWithValidation().find(goodValue.getId()))) - .isEqualTo(goodValue); - assertThatExceptionOfType(ConversionException.class) - .isThrownBy(() -> db.tx(() -> db.entitiesWithValidation().find(EntityWithValidation.BAD_VALUE.getId()))); + assertThatObject(tx.call(db -> db.entitiesWithValidation().find(goodValue.getId()))).isEqualTo(goodValue); + assertThatExceptionOfType(ConversionException.class).isThrownBy(() -> tx.run(db -> + db.entitiesWithValidation().find(EntityWithValidation.BAD_VALUE.getId()))); } @Test public void viewCtorValidationFailure() { EntityWithValidation goodValue = new EntityWithValidation(new EntityWithValidation.Id("hey"), 43L); - db.tx(() -> { + tx.run(db -> { db.entitiesWithValidation().save(goodValue); db.entitiesWithValidation().save(EntityWithValidation.BAD_VALUE_IN_VIEW); }); - assertThat(db.tx(() -> db.entitiesWithValidation().find(EntityWithValidation.OnlyVal.class, goodValue.getId()))) - .isEqualTo(new EntityWithValidation.OnlyVal(goodValue.getValue())); - assertThatExceptionOfType(ConversionException.class) - .isThrownBy(() -> db.tx(() -> db.entitiesWithValidation() - .find(EntityWithValidation.OnlyVal.class, EntityWithValidation.BAD_VALUE_IN_VIEW.getId()))); + assertThatObject(tx.call(db -> + db.entitiesWithValidation().find(EntityWithValidation.OnlyVal.class, goodValue.getId()) + )).isEqualTo(new EntityWithValidation.OnlyVal(goodValue.getValue())); + assertThatExceptionOfType(ConversionException.class).isThrownBy(() -> tx.run(db -> + db.entitiesWithValidation() + .find(EntityWithValidation.OnlyVal.class, EntityWithValidation.BAD_VALUE_IN_VIEW.getId()) + )); } @Test @@ -2302,10 +2286,10 @@ public void complexIdEquals() { Complex c2 = new Complex(new Complex.Id(999_999, 42L, "ZZZ", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 76L, "ZZZ", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3)); + tx.run(db -> db.complexes().insert(c1, c2, c3)); - assertThat( - db.tx(() -> db.complexes().query() + assertThatList( + tx.call(db -> db.complexes().query() .where("id").eq(c1.getId()) .orderBy(ob -> ob.orderBy("id").ascending()) .find()) @@ -2318,10 +2302,10 @@ public void complexIdNotEquals() { Complex c2 = new Complex(new Complex.Id(999_999, 42L, "ZZZ", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 76L, "ZZZ", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3)); + tx.run(db -> db.complexes().insert(c1, c2, c3)); - assertThat( - db.tx(() -> db.complexes().query() + assertThatList( + tx.call(db -> db.complexes().query() .where("id").neq(c1.getId()) .orderBy(ob -> ob.orderBy("id").ascending()) .find()) @@ -2334,10 +2318,10 @@ public void complexIdGreaterThan() { Complex c2 = new Complex(new Complex.Id(999_999, 42L, "ZZZ", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 76L, "ZZZ", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3)); + tx.run(db -> db.complexes().insert(c1, c2, c3)); - assertThat( - db.tx(() -> db.complexes().query() + assertThatList( + tx.call(db -> db.complexes().query() .where("id").gt(c1.getId()) .orderBy(ob -> ob.orderBy("id").ascending()) .find()) @@ -2349,10 +2333,10 @@ public void complexIdLessThan() { Complex c1 = new Complex(new Complex.Id(999_999, 15L, "ZZZ", Complex.Status.OK)); Complex c2 = new Complex(new Complex.Id(999_999, 42L, "ZZZ", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 76L, "ZZZ", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3)); + tx.run(db -> db.complexes().insert(c1, c2, c3)); - assertThat( - db.tx(() -> db.complexes().query() + assertThatList( + tx.call(db -> db.complexes().query() .where("id").lt(c3.getId()) .orderBy(ob -> ob.orderBy("id").descending()) .find()) @@ -2365,9 +2349,9 @@ public void byteIdLessThan() { BytePkEntity e1 = BytePkEntity.valueOf(1, 2, 3); BytePkEntity e2 = BytePkEntity.valueOf(1, 2, 4); BytePkEntity e3 = BytePkEntity.valueOf(1, 3, 255); - db.tx(() -> db.bytePkEntities().insert(e0, e1, e2, e3)); + tx.run(db -> db.bytePkEntities().insert(e0, e1, e2, e3)); - assertThat(db.tx(() -> db.bytePkEntities().query() + assertThatList(tx.call(db -> db.bytePkEntities().query() .where("id").lt(e3.getId()) .orderBy(ob -> ob.orderBy("id").descending()) .find() @@ -2380,9 +2364,9 @@ public void byteIdGreaterThan() { BytePkEntity e1 = BytePkEntity.valueOf(1, 2, 3); BytePkEntity e2 = BytePkEntity.valueOf(1, 2, 4); BytePkEntity e3 = BytePkEntity.valueOf(1, 3, 255); - db.tx(() -> db.bytePkEntities().insert(e0, e1, e2, e3)); + tx.run(db -> db.bytePkEntities().insert(e0, e1, e2, e3)); - assertThat(db.tx(() -> db.bytePkEntities().query() + assertThatList(tx.call(db -> db.bytePkEntities().query() .where("id").gt(e1.getId()) .orderBy(ob -> ob.orderBy("id").ascending()) .find() @@ -2392,9 +2376,9 @@ public void byteIdGreaterThan() { @Test public void byteIdEmpty() { BytePkEntity e0 = BytePkEntity.valueOf(); - db.tx(() -> db.bytePkEntities().insert(e0)); + tx.run(db -> db.bytePkEntities().insert(e0)); - assertThat(db.tx(() -> db.bytePkEntities().find(e0.getId()))).isEqualTo(e0); + assertThatObject(tx.call(db -> db.bytePkEntities().find(e0.getId()))).isEqualTo(e0); } @Test @@ -2402,19 +2386,19 @@ public void complexIdLessThanWithEmbeddedId() { Supabubble sa = new Supabubble(new Supabubble.Id(new Project.Id("naher"), "bubble-A")); Supabubble sb = new Supabubble(new Supabubble.Id(new Project.Id("naher"), "bubble-B")); Supabubble sc = new Supabubble(new Supabubble.Id(new Project.Id("naher"), "bubble-C")); - db.tx(() -> db.supabubbles().insert(sa, sb, sc)); + tx.run(db -> db.supabubbles().insert(sa, sb, sc)); - assertThat( - db.tx(() -> db.supabubbles().query() + assertThatList(tx.call(db -> + db.supabubbles().query() .where("id").lt(sc.getId()) .orderBy(ob -> ob.orderBy("id").descending()) - .find()) - ).containsExactly(sb, sa); + .find() + )).containsExactly(sb, sa); } @Test public void checkCanMergeWorkProperly() { - db.tx(() -> { + tx.run(db -> { Project p1 = new Project(new Project.Id("1"), "first"); Project p2 = new Project(new Project.Id("2"), "second"); @@ -2424,19 +2408,18 @@ public void checkCanMergeWorkProperly() { }); } - @Test public void doMultipleSaveInOneTx() { Project p1 = new Project(new Project.Id("1"), "first"); Project p2 = new Project(new Project.Id("2"), "second"); - db.tx(() -> { + tx.run(db -> { db.projects().save(p1); db.projects().save(p2); }); - assertThat(db.tx(() -> db.projects().find(p1.getId()))).isEqualTo(p1); - assertThat(db.tx(() -> db.projects().find(p2.getId()))).isEqualTo(p2); + assertThatObject(tx.call(db -> db.projects().find(p1.getId()))).isEqualTo(p1); + assertThatObject(tx.call(db -> db.projects().find(p2.getId()))).isEqualTo(p2); } @Test @@ -2444,15 +2427,15 @@ public void deleteInsertInOneTx() { // merged to upsert, integration test, that's merge correct Project p1 = new Project(new Project.Id("1"), "project1"); Project p2 = new Project(new Project.Id("2"), "project2"); - db.tx(() -> { + tx.run(db -> { db.projects().delete(p1.getId()); db.projects().insert(p1); db.projects().delete(p2.getId()); db.projects().insert(p2); }); - assertThat(db.tx(() -> db.projects().find(p1.getId()))).isEqualTo(p1); - assertThat(db.tx(() -> db.projects().find(p2.getId()))).isEqualTo(p2); + assertThatObject(tx.call(db -> db.projects().find(p1.getId()))).isEqualTo(p1); + assertThatObject(tx.call(db -> db.projects().find(p2.getId()))).isEqualTo(p2); } @Test @@ -2460,13 +2443,15 @@ public void multistatementReadonlyTransaction() { Project p1 = new Project(new Project.Id("1"), "first"); Project p2 = new Project(new Project.Id("2"), "second"); - db.tx(() -> { + tx.run(db -> { db.projects().save(p1); db.projects().save(p2); }); - RepositoryTransaction tx = repository.startTransaction(TxOptions.create(IsolationLevel.STALE_CONSISTENT_READ_ONLY)); - assertThat(tx.table(Project.class).find(p1.getId())).isNotNull(); - assertThat(tx.table(Project.class).find(p2.getId())).isNotNull(); + RepositoryTransaction repositoryTransaction = repository.startTransaction( + TxOptions.create(IsolationLevel.STALE_CONSISTENT_READ_ONLY) + ); + assertThat(repositoryTransaction.table(Project.class).find(p1.getId())).isNotNull(); + assertThat(repositoryTransaction.table(Project.class).find(p2.getId())).isNotNull(); } @Test @@ -2501,10 +2486,10 @@ public void noOptimisticLockOnScan() { @Test public void findEntityAndViewWithTheSameKey() { TypeFreak tf1 = newTypeFreak(0, "AAA1", "bbb"); - db.tx(() -> db.typeFreaks().insert(tf1)); + tx.run(db -> db.typeFreaks().insert(tf1)); // find view by id and than find entity by id - db.tx(() -> { + tx.run(db -> { TypeFreak.View foundView = db.typeFreaks().find(TypeFreak.View.class, tf1.getId()); assertThat(foundView).isEqualTo(new TypeFreak.View(tf1.getId(), tf1.getEmbedded())); @@ -2513,7 +2498,7 @@ public void findEntityAndViewWithTheSameKey() { }); // find entity by id and than find view by id - db.tx(() -> { + tx.run(db -> { TypeFreak foundTF = db.typeFreaks().find(tf1.getId()); assertThat(foundTF).isEqualTo(tf1); @@ -2524,21 +2509,24 @@ public void findEntityAndViewWithTheSameKey() { @Test public void scanUpdateFails() { - Assertions.assertThatExceptionOfType(IllegalTransactionScanException.class) - .isThrownBy(() -> db.scan().run(() -> { - db.projects().save(new Project(new Project.Id("1"), "p1")); - })); + assertThatExceptionOfType(IllegalTransactionScanException.class).isThrownBy(() -> tx.scan().run(() -> + // FIXME + BaseDb.current(TestDb.class).projects().save(new Project(new Project.Id("1"), "p1"))) + ); } @Test public void scanNotTruncated() { int maxPageSizeBiggerThatReal = 11_000; - db.tx(() -> IntStream.range(0, maxPageSizeBiggerThatReal).forEach( + tx.run(db -> IntStream.range(0, maxPageSizeBiggerThatReal).forEach( i -> db.projects().save(new Project(new Project.Id("id_" + i), "name")) )); - List result = db.scan().withMaxSize(maxPageSizeBiggerThatReal).run(() -> db.projects().findAll()); + List result = tx.scan().withMaxSize(maxPageSizeBiggerThatReal).run(() -> + // FIXME + BaseDb.current(TestDb.class).projects().findAll() + ); assertThat(result).hasSize(maxPageSizeBiggerThatReal); } @@ -2546,11 +2534,10 @@ public void scanNotTruncated() { public void scanFind() { Project p1 = new Project(new Project.Id("1"), "p1"); - db.tx(() -> { - db.projects().save(p1); - }); + tx.run(db -> db.projects().save(p1)); - Project result = db.scan().run(() -> db.projects().find(p1.getId())); + // FIXME + Project result = tx.scan().run(() -> BaseDb.current(TestDb.class).projects().find(p1.getId())); assertThat(result).isEqualTo(p1); } @@ -2558,11 +2545,12 @@ public void scanFind() { public void scanStreamAll() { int size = 10; - db.tx(() -> IntStream.range(0, size).forEach( + tx.run(db -> IntStream.range(0, size).forEach( i -> db.projects().save(new Project(new Project.Id("id_" + i), "name")) )); - List result = db.scan().run(() -> db.projects().streamAll(1).collect(toList())); + // FIXME + List result = tx.scan().run(() -> BaseDb.current(TestDb.class).projects().streamAll(1).collect(toList())); assertThat(result).hasSize(size); } @@ -2571,7 +2559,7 @@ public void businessExceptionInScanTx() { class BusinessException extends RuntimeException { } - assertThatExceptionOfType(BusinessException.class).isThrownBy(() -> db.tx(() -> { + assertThatExceptionOfType(BusinessException.class).isThrownBy(() -> tx.run(db -> { db.primitives().find(new Primitive.Id(25)); throw new BusinessException(); })); @@ -2583,10 +2571,11 @@ public void throwConversionExceptionOnDeserializationProblem() { new NonDeserializableEntity.Id("ru-vladimirsky-central-001"), new NonDeserializableObject() ); - db.tx(() -> db.table(NonDeserializableEntity.class).insert(nonDeserializableEntity)); + tx.run(db -> db.table(NonDeserializableEntity.class).insert(nonDeserializableEntity)); - assertThatExceptionOfType(ConversionException.class) - .isThrownBy(() -> db.tx(() -> db.table(NonDeserializableEntity.class).find(nonDeserializableEntity.getId()))); + assertThatExceptionOfType(ConversionException.class).isThrownBy(() -> + tx.run(db -> db.table(NonDeserializableEntity.class).find(nonDeserializableEntity.getId())) + ); } @Test @@ -2595,17 +2584,20 @@ public void throwConversionExceptionOnDeserializationReadTableProblem() { new NonDeserializableEntity.Id("ru-vladimirsky-central-001"), new NonDeserializableObject() ); - db.tx(() -> db.table(NonDeserializableEntity.class).insert(nonDeserializableEntity)); + tx.run(db -> db.table(NonDeserializableEntity.class).insert(nonDeserializableEntity)); assertThatExceptionOfType(ConversionException.class).isThrownBy(() -> - db.readOnly().run(() -> db.table(NonDeserializableEntity.class).readTable(defaultReadTableParamsNonLegacy()).collect(toList())) + // FIXME + tx.readOnly().run(() -> BaseDb.current(TestDb.class).table(NonDeserializableEntity.class) + .readTable(defaultReadTableParamsNonLegacy()) + .collect(toList())) ); } @Test public void resolveOnReadTableStream() { - db.tx(() -> { + tx.run(db -> { db.projects().save(new Project(new Project.Id("1"), "p1")); db.referrings().save(new Referring(new Referring.Id("1"), new Project.Id("1"), null, null, null)); db.projects().save(new Project(new Project.Id("2"), "p2")); @@ -2613,9 +2605,11 @@ public void resolveOnReadTableStream() { }); - db.readOnly().run(() -> - db.referrings().readTable(defaultReadTableParamsNonLegacy()) - .forEach(r -> db.projects().find(r.getProject())) + tx.readOnly().run(() -> + // FIXME + BaseDb.current(TestDb.class).referrings().readTable(defaultReadTableParamsNonLegacy()) + // FIXME + .forEach(r -> BaseDb.current(TestDb.class).projects().find(r.getProject())) ); } @@ -2625,20 +2619,16 @@ public void customMarshaling() { new WithUnflattenableField.Id("id42"), new WithUnflattenableField.Unflattenable("Hello, world!", 100_500) ); - db.tx(() -> db.table(WithUnflattenableField.class).insert(entity)); + tx.run(db -> db.table(WithUnflattenableField.class).insert(entity)); - db.tx(() -> { - assertThat(db.table(WithUnflattenableField.class).find(entity.getId())).isEqualTo(entity); - }); + tx.run(db -> assertThat(db.table(WithUnflattenableField.class).find(entity.getId())).isEqualTo(entity)); } @Test public void readFromCache() { Complex.Id id = new Complex.Id(1, 2L, "c", Complex.Status.OK); - db.tx(() -> { - db.complexes().insert(new Complex(id)); - }); - db.tx(() -> { + tx.run(db -> db.complexes().insert(new Complex(id))); + tx.run(db -> { Complex first = db.complexes().find(id); Complex second = db.complexes().find(id); @@ -2649,8 +2639,8 @@ public void readFromCache() { @Test public void findRangeAndPutInCache() { - db.tx(this::makeComplexes); - db.tx(() -> { + tx.run(this::makeComplexes); + tx.run(db -> { List rangeResults = db.complexes().find(Range.create(new Complex.Id(0, 0L, null, null))); assertThat(rangeResults).hasSize(6); @@ -2664,8 +2654,8 @@ public void findRangeAndPutInCache() { @Test public void findAllAndPutInCache() { - db.tx(this::makeComplexes); - db.tx(() -> { + tx.run(this::makeComplexes); + tx.run(db -> { List rangeResults = db.complexes().findAll(); assertThat(rangeResults).hasSize(54); @@ -2685,23 +2675,23 @@ public void readAndFailOnInconsistentDataSucceedOnRetry() { final Project.Id vaderId = new Project.Id("Vader"); // But 'Always two there are', we consistently keep exactly 2 entities altogether: - Consumer createFirstTwo = (tx) -> { - tx.table(Primitive.class).insert(new Primitive(sidiousId, 10)); - tx.table(Complex.class).insert(new Complex(tyranusId)); + Consumer createFirstTwo = repositoryTransaction -> { + repositoryTransaction.table(Primitive.class).insert(new Primitive(sidiousId, 10)); + repositoryTransaction.table(Complex.class).insert(new Complex(tyranusId)); }; // Do initial transaction: runInTx(createFirstTwo); // Prepare altering transaction as a hook that doesn't do anything in the second attempt. - Runnable concurrentChanger = makeOneShotRunnable(() -> db.separate().tx(() -> { + Runnable concurrentChanger = makeOneShotRunnable(() -> tx.separate().run(db -> { db.complexes().delete(tyranusId); db.projects().insert(new Project(vaderId, "abc")); })); List txAttempts = new ArrayList<>(); - List> twoElements = db.tx(() -> { + List> twoElements = tx.call(db -> { txAttempts.add(null); db.table(Book.class).save(new Book(new Book.Id("1"), 1, "title1", List.of("author1"))); @@ -2739,32 +2729,32 @@ public void customValuedIds() { "payload-" + i, Math.random() < 0.5 ? UpdateFeedEntry.Status.ACTIVE : UpdateFeedEntry.Status.INACTIVE ); - db.tx(() -> db.updateFeedEntries().insert(snap)); + tx.run(db -> db.updateFeedEntries().insert(snap)); inserted.put(snap.getId(), snap); } - assertThat(db.tx(() -> db.updateFeedEntries().find(inserted.keySet()))) + assertThatList(tx.call(db -> db.updateFeedEntries().find(inserted.keySet()))) .containsExactlyInAnyOrderElementsOf(inserted.values()); - assertThat(db.tx(() -> db.updateFeedEntries().find(inserted.keySet()))) + assertThatList(tx.call(db -> db.updateFeedEntries().find(inserted.keySet()))) .containsExactlyInAnyOrderElementsOf(inserted.values()); - assertThat(db.tx(() -> db.updateFeedEntries().list(ListRequest.builder(UpdateFeedEntry.class) + assertThatIterable(tx.call(db -> db.updateFeedEntries().list(ListRequest.builder(UpdateFeedEntry.class) .filter(fb -> fb.where("id").in(inserted.keySet())) - .build()))) - .containsExactlyInAnyOrderElementsOf(inserted.values()); + .build())) + ).containsExactlyInAnyOrderElementsOf(inserted.values()); - assertThat(db.tx(() -> db.updateFeedEntries().list(ListRequest.builder(UpdateFeedEntry.class) + assertThatIterable(tx.call(db -> db.updateFeedEntries().list(ListRequest.builder(UpdateFeedEntry.class) .filter(fb -> fb.where("id").in(inserted.keySet().stream().map(Object::toString).collect(toSet()))) - .build()))) - .containsExactlyInAnyOrderElementsOf(inserted.values()); + .build())) + ).containsExactlyInAnyOrderElementsOf(inserted.values()); for (var e : inserted.entrySet()) { - assertThat(db.tx(() -> db.updateFeedEntries().query() + assertThatObject(tx.call(db -> db.updateFeedEntries().query() .filter(fb -> fb.where("id").eq(e.getKey())) .findOne())).isEqualTo(e.getValue()); - assertThat(db.tx(() -> db.updateFeedEntries().query() + assertThatObject(tx.call(db -> db.updateFeedEntries().query() .filter(fb -> fb.where("id").eq(e.getKey().toString())) .findOne())).isEqualTo(e.getValue()); } @@ -2778,26 +2768,26 @@ public void customValueType() { new NetworkAppliance.Ipv6Address("2e:a0::1"), new NetworkAppliance.SixtyFourBitString(Long.parseUnsignedLong("cafecafecafecafe", 16)) ); - db.tx(() -> db.networkAppliances().insert(app1)); - assertThat(db.tx(() -> db.networkAppliances().find(app1.id()))).isEqualTo(app1); + tx.run(db -> db.networkAppliances().insert(app1)); + assertThatObject(tx.call(db -> db.networkAppliances().find(app1.id()))).isEqualTo(app1); } @Test public void customValueTypeInFilter() { var ve = new VersionedEntity(new VersionedEntity.Id("heyhey", new Version(100L)), new Version(100_500L)); - db.tx(() -> db.versionedEntities().insert(ve)); - assertThat(db.tx(() -> db.versionedEntities().find(ve.id()))).isEqualTo(ve); - assertThat(db.tx(() -> db.versionedEntities().query() + tx.run(db -> db.versionedEntities().insert(ve)); + assertThatObject(tx.call(db -> db.versionedEntities().find(ve.id()))).isEqualTo(ve); + assertThatObject(tx.call(db -> db.versionedEntities().query() .where("id.version").eq(ve.id().version()) .and("version2").eq(ve.version2()) .findOne() )).isEqualTo(ve); - assertThat(db.tx(() -> db.versionedEntities().query() + assertThatObject(tx.call(db -> db.versionedEntities().query() .where("id.version").eq(100L) .and("version2").eq(100_500L) .findOne() )).isEqualTo(ve); - assertThat(db.tx(() -> db.versionedEntities().query() + assertThatObject(tx.call(db -> db.versionedEntities().query() .where("id.version").eq(100L) .and("version2").eq(null) .findOne() @@ -2807,25 +2797,35 @@ public void customValueTypeInFilter() { @Test public void customValueTypeInFilterByAlias() { UUID testPrefferedUUID = UUID.randomUUID(); - var ve = new VersionedAliasedEntity(new VersionedAliasedEntity.Id("heyhey", new Version(100L), testPrefferedUUID, new Sha256("100")), new Version(100_500L), testPrefferedUUID, new UniqueEntity.Id(testPrefferedUUID)); - db.tx(() -> db.versionedAliasedEntities().insert(ve)); - assertThat(db.tx(() -> db.versionedAliasedEntities().find(ve.id()))).isEqualTo(ve); - assertThat(db.tx(() -> db.versionedAliasedEntities().query() + var ve = new VersionedAliasedEntity( + new VersionedAliasedEntity.Id( + "heyhey", + new Version(100L), + testPrefferedUUID, + new Sha256("100") + ), + new Version(100_500L), + testPrefferedUUID, + new UniqueEntity.Id(testPrefferedUUID) + ); + tx.run(db -> db.versionedAliasedEntities().insert(ve)); + assertThatObject(tx.call(db -> db.versionedAliasedEntities().find(ve.id()))).isEqualTo(ve); + assertThatObject(tx.call(db -> db.versionedAliasedEntities().query() .where("id.version").eq(ve.id().version()) .and("version2").eq(ve.version2()) .findOne() )).isEqualTo(ve); - assertThat(db.tx(() -> db.versionedAliasedEntities().query() + assertThatObject(tx.call(db -> db.versionedAliasedEntities().query() .where("id.version").eq(100L) .and("version2").eq(100_500L) .findOne() )).isEqualTo(ve); - assertThat(db.tx(() -> db.versionedAliasedEntities().query() + assertThatObject(tx.call(db -> db.versionedAliasedEntities().query() .where("id.version").eq(100L) .and("version2").eq(null) .findOne() )).isNull(); - assertThat(db.tx(() -> db.versionedAliasedEntities().query() + assertThatObject(tx.call(db -> db.versionedAliasedEntities().query() .where("uniqueId").eq(ve.uniqueId()) .findOne() )).isEqualTo(ve); @@ -2836,15 +2836,15 @@ public void uuidEntityString() { var uuid = UUID.randomUUID(); var entity = new UniqueEntity(new UniqueEntity.Id(uuid), "hehe"); - db.tx(() -> db.table(UniqueEntity.class).save(entity)); - assertThat(db.tx(() -> db.table(UniqueEntity.class).find(new UniqueEntity.Id(uuid)))) + tx.run(db -> db.table(UniqueEntity.class).save(entity)); + assertThatObject(tx.call(db -> db.table(UniqueEntity.class).find(new UniqueEntity.Id(uuid)))) .isEqualTo(entity); - assertThat(db.tx(() -> db.table(UniqueEntity.class).query() + assertThatObject(tx.call(db -> db.table(UniqueEntity.class).query() .where("id").eq(new UniqueEntity.Id(uuid)) .and("value").eq("hehe") .findOne()) ).isEqualTo(entity); - assertThat(db.tx(() -> db.table(UniqueEntity.class).query() + assertThatObject(tx.call(db -> db.table(UniqueEntity.class).query() .where("id").eq(uuid) .and("value").eq("hehe") .findOne()) @@ -2856,15 +2856,15 @@ public void uuidEntityNative() { var uuid = UUID.randomUUID(); var entity = new UniqueEntityNative(new UniqueEntityNative.Id(uuid), "hehe"); - db.tx(() -> db.table(UniqueEntityNative.class).save(entity)); - assertThat(db.tx(() -> db.table(UniqueEntityNative.class).find(new UniqueEntityNative.Id(uuid)))) + tx.run(db -> db.table(UniqueEntityNative.class).save(entity)); + assertThatObject(tx.call(db -> db.table(UniqueEntityNative.class).find(new UniqueEntityNative.Id(uuid)))) .isEqualTo(entity); - assertThat(db.tx(() -> db.table(UniqueEntityNative.class).query() + assertThatObject(tx.call(db -> db.table(UniqueEntityNative.class).query() .where("id").eq(new UniqueEntityNative.Id(uuid)) .and("value").eq("hehe") .findOne()) ).isEqualTo(entity); - assertThat(db.tx(() -> db.table(UniqueEntityNative.class).query() + assertThatObject(tx.call(db -> db.table(UniqueEntityNative.class).query() .where("id").eq(uuid) .and("value").eq("hehe") .findOne()) @@ -2874,8 +2874,8 @@ public void uuidEntityNative() { @Test public void detachedEntity() { var theEntity = new DetachedEntity(new DetachedEntityId("some-id")); - db.tx(() -> db.detachedEntities().save(theEntity)); - assertThat(db.tx(() -> db.detachedEntities().find(theEntity.id()))).isEqualTo(theEntity); + tx.run(db -> db.detachedEntities().save(theEntity)); + assertThatObject(tx.call(db -> db.detachedEntities().find(theEntity.id()))).isEqualTo(theEntity); } @Test @@ -2885,14 +2885,14 @@ public void multiWrapperEntity2WithCustomConverterUnwrapped() { MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:013943912") ) )); - db.tx(() -> db.multiWrappedEntities2().save(theEntity)); + tx.run(db -> db.multiWrappedEntities2().save(theEntity)); var unwrapped = "xyzzy-central1:0"; - assertThat(db.tx(() -> db.multiWrappedEntities2().query() + assertThatObject(tx.call(db -> db.multiWrappedEntities2().query() .where("id").gt(unwrapped) .findOne() )).isEqualTo(theEntity); - assertThat(db.tx(() -> db.multiWrappedEntities2().query() + assertThatList(tx.call(db -> db.multiWrappedEntities2().query() .where("id").lt(unwrapped) .find() )).isEmpty(); @@ -2905,14 +2905,14 @@ public void multiWrapperEntity2WithCustomConverterOnceWrapped() { MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:013943912") ) )); - db.tx(() -> db.multiWrappedEntities2().save(theEntity)); + tx.run(db -> db.multiWrappedEntities2().save(theEntity)); var onceWrapped = MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:0"); - assertThat(db.tx(() -> db.multiWrappedEntities2().query() + assertThatObject(tx.call(db -> db.multiWrappedEntities2().query() .where("id").gt(onceWrapped) .findOne() )).isEqualTo(theEntity); - assertThat(db.tx(() -> db.multiWrappedEntities2().query() + assertThatList(tx.call(db -> db.multiWrappedEntities2().query() .where("id").lt(onceWrapped) .find() )).isEmpty(); @@ -2925,14 +2925,16 @@ public void multiWrapperEntity2WithCustomConverterTwiceWrapped() { MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:013943912") ) )); - db.tx(() -> db.multiWrappedEntities2().save(theEntity)); + tx.run(db -> db.multiWrappedEntities2().save(theEntity)); - var twiceWrapped = new MultiWrappedEntity2.WrapperOfIdStringValue(MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:0")); - assertThat(db.tx(() -> db.multiWrappedEntities2().query() + var twiceWrapped = new MultiWrappedEntity2.WrapperOfIdStringValue( + MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:0") + ); + assertThatObject(tx.call(db -> db.multiWrappedEntities2().query() .where("id").gt(twiceWrapped) .findOne() )).isEqualTo(theEntity); - assertThat(db.tx(() -> db.multiWrappedEntities2().query() + assertThatList(tx.call(db -> db.multiWrappedEntities2().query() .where("id").lt(twiceWrapped) .find() )).isEmpty(); @@ -2945,16 +2947,17 @@ public void multiWrapperEntity2WithCustomConverterPartiallyUnwrapped3() { MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:013943912") ) )); - db.tx(() -> db.multiWrappedEntities2().save(theEntity)); + tx.run(db -> db.multiWrappedEntities2().save(theEntity)); var schema = EntitySchema.of(MultiWrappedEntity2.class); var idField = schema.getField("id"); - var sv = new StringFieldValue("xyzzy-central1:0").getRaw(idField); // Will get flattest original value, which is the IdStringValue("xyzzy-..") - assertThat(db.tx(() -> db.multiWrappedEntities2().query() + // Will get flattest original value, which is the IdStringValue("xyzzy-..") + var sv = new StringFieldValue("xyzzy-central1:0").getRaw(idField); + assertThatObject(tx.call(db -> db.multiWrappedEntities2().query() .where("id").gt(sv) .findOne() )).isEqualTo(theEntity); - assertThat(db.tx(() -> db.multiWrappedEntities2().query() + assertThatList(tx.call(db -> db.multiWrappedEntities2().query() .where("id").lt(sv) .find() )).isEmpty(); @@ -2962,15 +2965,27 @@ public void multiWrapperEntity2WithCustomConverterPartiallyUnwrapped3() { @Test public void multiWrapperEntity2WithCustomConverterFullyWrapped() { - var theEntity = new MultiWrappedEntity2(new MultiWrappedEntity2.Id(new MultiWrappedEntity2.WrapperOfIdStringValue(MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:013943912")))); - db.tx(() -> db.multiWrappedEntities2().save(theEntity)); - - assertThat(db.tx(() -> db.multiWrappedEntities2().query() - .where("id").gt(new MultiWrappedEntity2.Id(new MultiWrappedEntity2.WrapperOfIdStringValue(MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:0")))) + var theEntity = new MultiWrappedEntity2(new MultiWrappedEntity2.Id( + new MultiWrappedEntity2.WrapperOfIdStringValue( + MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:013943912") + ) + )); + tx.run(db -> db.multiWrappedEntities2().save(theEntity)); + + assertThatObject(tx.call(db -> db.multiWrappedEntities2().query() + .where("id").gt(new MultiWrappedEntity2.Id( + new MultiWrappedEntity2.WrapperOfIdStringValue( + MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:0") + ) + )) .findOne() )).isEqualTo(theEntity); - assertThat(db.tx(() -> db.multiWrappedEntities2().query() - .where("id").lt(new MultiWrappedEntity2.Id(new MultiWrappedEntity2.WrapperOfIdStringValue(MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:0")))) + assertThatList(tx.call(db -> db.multiWrappedEntities2().query() + .where("id").lt(new MultiWrappedEntity2.Id( + new MultiWrappedEntity2.WrapperOfIdStringValue( + MultiWrappedEntity2.IdStringValue.fromString("xyzzy-central1:0") + ) + )) .find() )).isEmpty(); } @@ -2980,14 +2995,18 @@ public void nullWrappingStringValueColumn() { // must not be able to save a string-value field that returns null from toString() var badWrapperWithNullInside = new BadlyWrappedEntity(new BadlyWrappedEntity.Id("xxx"), new BadStringValueWrapper(null)); assertThatExceptionOfType(ConversionException.class) - .isThrownBy(() -> db.tx(() -> db.table(BadlyWrappedEntity.class).save(badWrapperWithNullInside))) + .isThrownBy(() -> tx.run(db -> db.table(BadlyWrappedEntity.class).save(badWrapperWithNullInside))) .withCauseInstanceOf(IllegalArgumentException.class); // null column value -> null string-value type field, without invoking the converter at all var noWrapper = new BadlyWrappedEntity(new BadlyWrappedEntity.Id("yyy"), null); - db.tx(() -> db.table(BadlyWrappedEntity.class).save(noWrapper)); - assertThat(db.tx(() -> db.table(BadlyWrappedEntity.class).find(new BadlyWrappedEntity.Id("yyy"))).badWrapper()).isNull(); - assertThat(db.tx(() -> db.table(BadlyWrappedEntity.class).find(new BadlyWrappedEntity.Id("yyy")))).isEqualTo(noWrapper); + tx.call(db -> db.table(BadlyWrappedEntity.class).save(noWrapper)); + assertThatObject( + tx.call(db -> db.table(BadlyWrappedEntity.class).find(new BadlyWrappedEntity.Id("yyy"))).badWrapper() + ).isNull(); + assertThatObject( + tx.call(db -> db.table(BadlyWrappedEntity.class).find(new BadlyWrappedEntity.Id("yyy"))) + ).isEqualTo(noWrapper); } @Test @@ -3021,17 +3040,17 @@ private void lenientEnumBehaviorTestImpl() { var entity1 = new EnumEntity(new EnumEntity.Id("qee1"), EnumEntity.IpVersion.IPV6, EnumEntity.NetworkType.OVERLAY); var entity2 = new EnumEntity(new EnumEntity.Id("qee2"), EnumEntity.IpVersion.IPV4, EnumEntity.NetworkType.UNDERLAY_V4); var entity3 = new EnumEntity(new EnumEntity.Id("qee3"), EnumEntity.IpVersion.IPV6, EnumEntity.NetworkType.UNDERLAY_V6); - db.tx(() -> db.table(EnumEntity.class).insert(entity1, entity2, entity3)); - db.tx(() -> db.table(EnumEntity.class).update(entity2.id(), new Changeset() + tx.run(db -> db.table(EnumEntity.class).insert(entity1, entity2, entity3)); + tx.run(db -> db.table(EnumEntity.class).update(entity2.id(), new Changeset() .set("ipVersion", "ipv4") // the lowercase of IPV4.name(), which IS allowed by the LENIENT enum deserializer .set("networkType", "underlay-v4") // this uses toString() to convert enums, and "underlay-v4" corresponds to a UNDERLAY_V4 constant )); - db.tx(() -> db.table(EnumEntity.class).update(entity3.id(), new Changeset() + tx.run(db -> db.table(EnumEntity.class).update(entity3.id(), new Changeset() .set("ipVersion", "IPV5") // mwahahahahah! This is an unknown constant which the LENIENT enum deserializer will turn into null (!!) .set("networkType", "zz") // same here!!! )); - db.tx(() -> { + tx.run(db -> { assertThat(db.table(EnumEntity.class).find(entity1.id())).isEqualTo(entity1); // lowercase 'ipv4' treated as 'IPV4' constant @@ -3046,21 +3065,21 @@ private void strictEnumBehaviorTestImpl() { var entity2 = new EnumEntity(new EnumEntity.Id("qee2"), EnumEntity.IpVersion.IPV4, EnumEntity.NetworkType.UNDERLAY_V4); var entity3 = new EnumEntity(new EnumEntity.Id("qee3"), EnumEntity.IpVersion.IPV4, EnumEntity.NetworkType.UNDERLAY_V4); var entity4 = new EnumEntity(new EnumEntity.Id("qee4"), EnumEntity.IpVersion.IPV6, EnumEntity.NetworkType.UNDERLAY_V6); - db.tx(() -> db.table(EnumEntity.class).insert(entity1, entity2, entity3, entity4)); - db.tx(() -> db.table(EnumEntity.class).update(entity2.id(), new Changeset() + tx.run(db -> db.table(EnumEntity.class).insert(entity1, entity2, entity3, entity4)); + tx.run(db -> db.table(EnumEntity.class).update(entity2.id(), new Changeset() .set("ipVersion", "IPV4") // IPV4.name(), verbatim .set("networkType", "underlay-v4") // this uses toString() to convert enums, and "underlay-v4" corresponds to a UNDERLAY_V4 constant )); - db.tx(() -> db.table(EnumEntity.class).update(entity3.id(), new Changeset() + tx.run(db -> db.table(EnumEntity.class).update(entity3.id(), new Changeset() .set("ipVersion", "ipv4") // the lowercase of IPV4.name(), which IS NOT allowed by the STRICT enum deserializer .set("networkType", "underlay-v4") // this uses toString() to convert enums, and "underlay-v4" corresponds to a UNDERLAY_V4 constant )); - db.tx(() -> db.table(EnumEntity.class).update(entity4.id(), new Changeset() + tx.run(db -> db.table(EnumEntity.class).update(entity4.id(), new Changeset() .set("ipVersion", "IPV5") // mwahahahahah! This is an unknown constant which the STRICT enum deserializer disallows (!!) .set("networkType", "zz") // same here!!! )); - db.tx(() -> { + tx.run(db -> { assertThat(db.table(EnumEntity.class).find(entity1.id())).isEqualTo(entity1); assertThat(db.table(EnumEntity.class).find(entity2.id())).isEqualTo(entity2); @@ -3074,7 +3093,7 @@ private void strictEnumBehaviorTestImpl() { @Test public void postLoadAndPreSave() { var now = Instant.now().truncatedTo(ChronoUnit.MILLIS); - db.tx(() -> { + tx.run(db -> { var table = db.table(MigrationEntity.class); var saved = table.save(new MigrationEntity(new MigrationEntity.Id("id"), null, null)); assertThat(saved).isEqualTo(new MigrationEntity(new MigrationEntity.Id("id"), null, Instant.EPOCH)); @@ -3089,7 +3108,7 @@ public void postLoadAndPreSave() { assertThat(inserted2).isEqualTo(new MigrationEntity(new MigrationEntity.Id("iid2"), "Hello, world!", now)); }); - db.tx(() -> { + tx.run(db -> { var table = db.table(MigrationEntity.class); assertThat(table.find(new MigrationEntity.Id("id"))) .isEqualTo(new MigrationEntity(new MigrationEntity.Id("id"), "Default Value", Instant.EPOCH)); @@ -3101,11 +3120,11 @@ public void postLoadAndPreSave() { .isEqualTo(new MigrationEntity(new MigrationEntity.Id("iid2"), "Hello, world!", now)); }); - db.tx(() -> { + tx.run(db -> { var table = db.table(MigrationEntity.class); table.update(new MigrationEntity.Id("id"), Changeset.setField("fillOnPreSave", null)); }); - db.tx(() -> { + tx.run(db -> { var table = db.table(MigrationEntity.class); assertThat(table.find(new MigrationEntity.Id("id"))) .isEqualTo(new MigrationEntity(new MigrationEntity.Id("id"), "Default Value", null)); @@ -3224,14 +3243,14 @@ public void stableListingParamsHash() { protected void runInTx(Consumer action) { // We do not retry transactions, because we do not expect conflicts in our test scenarios. - RepositoryTransaction transaction = startTransaction(); + RepositoryTransaction repositoryTransaction = startTransaction(); try { - action.accept(transaction); + action.accept(repositoryTransaction); } catch (Throwable t) { - transaction.rollback(); + repositoryTransaction.rollback(); throw t; } - transaction.commit(); + repositoryTransaction.commit(); } private void assertListingContains(FilterExpression filterExpression, Project... expectedProjects) { @@ -3239,7 +3258,8 @@ private void assertListingContains(FilterExpression filterExpression, P .filter(filterExpression) .build(); - ListResult result = db.readOnly().run(() -> db.projects().list(request)); + // FIXME + ListResult result = tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().list(request)); assertThat(result.getEntries()).containsExactlyInAnyOrder(expectedProjects); } diff --git a/repository-test/src/main/java/tech/ydb/yoj/repository/test/TableQueryBuilderTest.java b/repository-test/src/main/java/tech/ydb/yoj/repository/test/TableQueryBuilderTest.java index 054db97a..2fbc31af 100644 --- a/repository-test/src/main/java/tech/ydb/yoj/repository/test/TableQueryBuilderTest.java +++ b/repository-test/src/main/java/tech/ydb/yoj/repository/test/TableQueryBuilderTest.java @@ -2,9 +2,9 @@ import org.junit.Test; import tech.ydb.yoj.repository.db.Repository; +import tech.ydb.yoj.repository.db.ScopedTxManager; import tech.ydb.yoj.repository.test.entity.TestEntities; import tech.ydb.yoj.repository.test.sample.TestDb; -import tech.ydb.yoj.repository.test.sample.TestDbImpl; import tech.ydb.yoj.repository.test.sample.model.Complex; import tech.ydb.yoj.repository.test.sample.model.Project; import tech.ydb.yoj.repository.test.sample.model.TypeFreak; @@ -22,17 +22,17 @@ import static tech.ydb.yoj.repository.db.EntityExpressions.newFilterBuilder; public abstract class TableQueryBuilderTest extends RepositoryTestSupport { - protected TestDb db; + protected ScopedTxManager tx; @Override public void setUp() { super.setUp(); - this.db = new TestDbImpl<>(this.repository); + this.tx = new ScopedTxManager<>(this.repository, TestDb.class); } @Override public void tearDown() { - this.db = null; + this.tx = null; super.tearDown(); } @@ -49,9 +49,9 @@ public void basic() { Project notInOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); Project p3 = new Project(new Project.Id("uuid001"), "ZZZ"); - db.tx(() -> db.projects().insert(p1, p2, notInOutput, p3)); + tx.run(db -> db.projects().insert(p1, p2, notInOutput, p3)); - db.tx(() -> { + tx.run(db -> { List page1 = db.projects().query() .limit(1) .orderBy(ob -> ob.orderBy("name").descending()) @@ -91,9 +91,9 @@ public void complexIdRange() { Complex c2 = new Complex(new Complex.Id(999_999, 15L, "UUU", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 15L, "KKK", Complex.Status.OK)); Complex c4 = new Complex(new Complex.Id(999_000, 15L, "AAA", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4)); - db.tx(() -> { + tx.run(db -> { List page = db.complexes().query() .limit(3) .filter(fb -> fb.where("id.a").eq(999_999)) @@ -108,9 +108,9 @@ public void complexIdFullScan() { Complex c2 = new Complex(new Complex.Id(999_999, 15L, "UUU", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 15L, "KKK", Complex.Status.OK)); Complex c4 = new Complex(new Complex.Id(999_000, 15L, "AAA", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4)); - db.tx(() -> { + tx.run(db -> { List page = db.complexes().query() .limit(3) .filter(fb -> fb.where("id.c").eq("UUU")) @@ -125,9 +125,9 @@ public void defaultOrderingIsByIdAscending() { Complex c2 = new Complex(new Complex.Id(999_999, 15L, "UUU", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 0L, "UUU", Complex.Status.OK)); Complex c4 = new Complex(new Complex.Id(999_000, 0L, "UUU", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4)); - db.tx(() -> { + tx.run(db -> { List page = db.complexes().query() .limit(4) .find(); @@ -142,9 +142,9 @@ public void and() { Complex c3 = new Complex(new Complex.Id(1, 300L, "KKK", Complex.Status.OK)); Complex notInOutput = new Complex(new Complex.Id(2, 300L, "AAA", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, notInOutput)); + tx.run(db -> db.complexes().insert(c1, c2, c3, notInOutput)); - db.tx(() -> { + tx.run(db -> { List page = db.complexes().query() .limit(4) .filter(fb -> fb.where("id.a").eq(1).and("id.b").gte(100L).and("id.b").lte(300L)) @@ -155,7 +155,7 @@ public void and() { @Test public void enumParsing() { - db.tx(() -> db.typeFreaks().query() + tx.run(db -> db.typeFreaks().query() .where("status").eq(Status.DRAFT) .orderBy(ob -> ob.orderBy("status").descending()) .limit(1) @@ -165,9 +165,9 @@ public void enumParsing() { @Test public void flattenedIsNull() { var tf = new TypeFreak(new TypeFreak.Id("b1p", 1), false, (byte) 0, (byte) 0, (short) 0, 0, 0, 0.0f, 0.0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); - db.tx(() -> db.typeFreaks().insert(tf)); + tx.run(db -> db.typeFreaks().insert(tf)); - List lst = db.tx(() -> db.typeFreaks().query() + List lst = tx.call(db -> db.typeFreaks().query() .where("jsonEmbedded").isNull() .limit(100) .find()); @@ -177,9 +177,9 @@ public void flattenedIsNull() { @Test public void flattenedIsNotNull() { var tf = new TypeFreak(new TypeFreak.Id("b1p", 1), false, (byte) 0, (byte) 0, (short) 0, 0, 0, 0.0f, 0.0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new TypeFreak.Embedded(new TypeFreak.A("A"), new TypeFreak.B("B")), null, null, null, null, null, null, null, null, null, null, null); - db.tx(() -> db.typeFreaks().insert(tf)); + tx.run(db -> db.typeFreaks().insert(tf)); - List lst = db.tx(() -> db.typeFreaks().query() + List lst = tx.call(db -> db.typeFreaks().query() .where("jsonEmbedded").isNotNull() .limit(100) .find()); @@ -189,8 +189,8 @@ public void flattenedIsNotNull() { @Test public void filterStringValuedByString() { TypeFreak typeFreak = new TypeFreak(new TypeFreak.Id("b1p", 1), false, (byte) 0, (byte) 0, (short) 0, 0, 0, 0.0f, 0.0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new TypeFreak.Ticket("CLOUD", 100500)); - db.tx(() -> db.typeFreaks().insert(typeFreak)); - List lst = db.tx(() -> db.typeFreaks().query() + tx.run(db -> db.typeFreaks().insert(typeFreak)); + List lst = tx.call(db -> db.typeFreaks().query() .filter(fb -> fb.where("ticket").eq("CLOUD-100500")) .limit(1) .find()); @@ -202,8 +202,8 @@ public void filterStringValuedByString() { public void filterStringValuedByStruct() { TypeFreak.Ticket ticket = new TypeFreak.Ticket("CLOUD", 100500); TypeFreak typeFreak = new TypeFreak(new TypeFreak.Id("b1p", 1), false, (byte) 0, (byte) 0, (short) 0, 0, 0, 0.0f, 0.0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, ticket); - db.tx(() -> db.typeFreaks().insert(typeFreak)); - List lst = db.tx(() -> db.typeFreaks().query() + tx.run(db -> db.typeFreaks().insert(typeFreak)); + List lst = tx.call(db -> db.typeFreaks().query() .filter(newFilterBuilder(TypeFreak.class) .where("ticket").eq(ticket) .build()) @@ -215,10 +215,10 @@ public void filterStringValuedByStruct() { @Test public void embeddedNulls() { - db.tx(() -> db.typeFreaks().insert( + tx.run(db -> db.typeFreaks().insert( new TypeFreak(new TypeFreak.Id("b1p", 1), false, (byte) 0, (byte) 0, (short) 0, 0, 0, 0.0f, 0.0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null) )); - List lst = db.tx(() -> db.typeFreaks().query() + List lst = tx.call(db -> db.typeFreaks().query() .filter(fb -> fb.where("embedded.a.a").eq("myfqdn")) .limit(1) .find()); @@ -232,9 +232,9 @@ public void simpleIdIn() { Project notInOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); Project p3 = new Project(new Project.Id("uuid001"), "ZZZ"); - db.tx(() -> db.projects().insert(p1, p2, notInOutput, p3)); + tx.run(db -> db.projects().insert(p1, p2, notInOutput, p3)); - db.tx(() -> { + tx.run(db -> { List page = db.projects().query() .limit(100) .filter(fb -> fb.where("id").in("uuid777", "uuid001", "uuid002")) @@ -250,9 +250,9 @@ public void complexIdIn() { Complex c2 = new Complex(new Complex.Id(999_999, 14L, "BBB", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_000, 13L, "CCC", Complex.Status.FAIL)); Complex c4 = new Complex(new Complex.Id(999_000, 12L, "DDD", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4)); - db.tx(() -> { + tx.run(db -> { List page = db.complexes().query() .limit(100) .filter(fb -> fb @@ -276,9 +276,9 @@ public void complexUnixTimestampRelational() { Complex c1 = new Complex(new Complex.Id(999_999, now.toEpochMilli(), "AAA", Complex.Status.OK)); Complex c2 = new Complex(new Complex.Id(999_999, nowPlus1.toEpochMilli(), "BBB", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_000, nowPlus2.toEpochMilli(), "CCC", Complex.Status.FAIL)); - db.tx(() -> db.complexes().insert(c1, c2, c3)); + tx.run(db -> db.complexes().insert(c1, c2, c3)); - db.tx(() -> { + tx.run(db -> { List page = db.complexes().query() .limit(100) .filter(fb -> fb.where("id.a").in(999_999, 999_000).and("id.b").gte(now).and("id.b").lt(nowPlus2)) @@ -297,9 +297,9 @@ public void complexUnixTimestampIn() { Complex c1 = new Complex(new Complex.Id(999_999, now.toEpochMilli(), "AAA", Complex.Status.OK)); Complex c2 = new Complex(new Complex.Id(999_999, nowPlus1.toEpochMilli(), "BBB", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_000, nowPlus2.toEpochMilli(), "CCC", Complex.Status.FAIL)); - db.tx(() -> db.complexes().insert(c1, c2, c3)); + tx.run(db -> db.complexes().insert(c1, c2, c3)); - db.tx(() -> { + tx.run(db -> { List page = db.complexes().query() .limit(100) .filter(fb -> fb.where("id.a").in(999_999, 999_000).and("id.b").in(now, nowPlus2)) @@ -314,9 +314,9 @@ public void or() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project notInOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, notInOutput)); + tx.run(db -> db.projects().insert(p1, p2, notInOutput)); - db.tx(() -> { + tx.run(db -> { List page = db.projects().query() .where("id").eq("uuid002") .or("id").eq("uuid777") @@ -331,9 +331,9 @@ public void notOr() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project inOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, inOutput)); + tx.run(db -> db.projects().insert(p1, p2, inOutput)); - db.tx(() -> { + tx.run(db -> { List page = db.projects().query() .limit(100) .filter(not(newFilterBuilder(Project.class) @@ -349,9 +349,9 @@ public void notRel() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project inOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, inOutput)); + tx.run(db -> db.projects().insert(p1, p2, inOutput)); - db.tx(() -> { + tx.run(db -> { List page = db.projects().query() .limit(100) .filter(not(newFilterBuilder(Project.class) @@ -367,9 +367,9 @@ public void notIn() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project inOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, inOutput)); + tx.run(db -> db.projects().insert(p1, p2, inOutput)); - db.tx(() -> { + tx.run(db -> { List page = db.projects().query() .limit(100) .filter(not(newFilterBuilder(Project.class) @@ -422,9 +422,9 @@ public void listByNamesWithUnderscores() { "CUSTOM NAMED COLUMN", null ); - db.tx(() -> db.typeFreaks().insert(tf)); + tx.run(db -> db.typeFreaks().insert(tf)); - db.tx(() -> { + tx.run(db -> { List page = db.typeFreaks().query() .limit(50) .where("customNamedColumn").eq("CUSTOM NAMED COLUMN") @@ -439,13 +439,14 @@ public void whereAndEquivalence1() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project inOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, inOutput)); + tx.run(db -> db.projects().insert(p1, p2, inOutput)); - assertThat(db.tx(() -> db.projects().query() + List found = tx.call(db -> db.projects().query() .and("id").in(p1.getId(), inOutput.getId()) .where("name").in(p2.getName()) .find() - )).isEmpty(); + ); + assertThat(found).isEmpty(); } @Test @@ -453,13 +454,14 @@ public void whereAndEquivalence2() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project inOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, inOutput)); + tx.run(db -> db.projects().insert(p1, p2, inOutput)); - assertThat(db.tx(() -> db.projects().query() + List found = tx.call(db -> db.projects().query() .where("id").in(p1.getId(), inOutput.getId()) .where("name").in(p2.getName()) .find() - )).isEmpty(); + ); + assertThat(found).isEmpty(); } @Test @@ -467,13 +469,14 @@ public void whereAndEquivalence3() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project inOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, inOutput)); + tx.run(db -> db.projects().insert(p1, p2, inOutput)); - assertThat(db.tx(() -> db.projects().query() + List found = tx.call(db -> db.projects().query() .and("id").in(p1.getId(), inOutput.getId()) .and("name").in(p2.getName()) .find() - )).isEmpty(); + ); + assertThat(found).isEmpty(); } @Test @@ -481,13 +484,14 @@ public void whereAndEquivalenceWithOr1() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project inOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, inOutput)); + tx.run(db -> db.projects().insert(p1, p2, inOutput)); - assertThat(db.tx(() -> db.projects().query() + List found = tx.call(db -> db.projects().query() .or("name").eq(p1.getName()) // funny way to write WHERE name='...' .where("id").eq(p2.getId()) // funny way to write ...AND id='...' .find() - )).isEmpty(); + ); + assertThat(found).isEmpty(); } @Test @@ -495,13 +499,14 @@ public void whereAndEquivalenceWithOr2() { Project p1 = new Project(new Project.Id("uuid002"), "AAA"); Project inOutput = new Project(new Project.Id("uuid333"), "WWW"); Project p2 = new Project(new Project.Id("uuid777"), "XXX"); - db.tx(() -> db.projects().insert(p1, p2, inOutput)); + tx.run(db -> db.projects().insert(p1, p2, inOutput)); - assertThat(db.tx(() -> db.projects().query() + List found = tx.call(db -> db.projects().query() .or("name").eq(p1.getName()) // funny way to write WHERE name='...' .and("id").eq(p2.getId()) // funny way to write ...AND id='...' .find() - )).isEmpty(); + ); + assertThat(found).isEmpty(); } @Test @@ -510,9 +515,9 @@ public void startsWith() { Complex c2 = new Complex(new Complex.Id(999_999, 15L, "UUU", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 15L, "KKK", Complex.Status.OK)); Complex c4 = new Complex(new Complex.Id(999_999, 15L, "AAA", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4)); - db.tx(() -> { + tx.run(db -> { List page = db.complexes().query() .limit(3) .filter(fb -> fb @@ -530,9 +535,9 @@ public void doesNotStartWith() { Complex c2 = new Complex(new Complex.Id(999_999, 15L, "UUU", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 15L, "KKK", Complex.Status.OK)); Complex c4 = new Complex(new Complex.Id(999_999, 15L, "AAA", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4)); - db.tx(() -> { + tx.run(db -> { List page = db.complexes().query() .limit(3) .filter(fb -> fb diff --git a/repository-test/src/main/java/tech/ydb/yoj/repository/test/sample/TestDb.java b/repository-test/src/main/java/tech/ydb/yoj/repository/test/sample/TestDb.java index 55ce5c6f..3b4f4e41 100644 --- a/repository-test/src/main/java/tech/ydb/yoj/repository/test/sample/TestDb.java +++ b/repository-test/src/main/java/tech/ydb/yoj/repository/test/sample/TestDb.java @@ -1,6 +1,4 @@ package tech.ydb.yoj.repository.test.sample; -import tech.ydb.yoj.repository.db.TxManager; - -public interface TestDb extends TxManager, TestEntityOperations { +public interface TestDb extends TestEntityOperations { } diff --git a/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/YdbRepositoryIntegrationTest.java b/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/YdbRepositoryIntegrationTest.java index 6c327b90..8df7cb5b 100644 --- a/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/YdbRepositoryIntegrationTest.java +++ b/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/YdbRepositoryIntegrationTest.java @@ -18,7 +18,6 @@ import lombok.experimental.Delegate; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; -import org.assertj.core.api.Assertions; import org.junit.ClassRule; import org.junit.Test; import tech.ydb.common.transaction.TxMode; @@ -47,6 +46,7 @@ import tech.ydb.yoj.databind.schema.Column; import tech.ydb.yoj.databind.schema.GlobalIndex; import tech.ydb.yoj.databind.schema.ObjectSchema; +import tech.ydb.yoj.repository.BaseDb; import tech.ydb.yoj.repository.db.Entity; import tech.ydb.yoj.repository.db.EntitySchema; import tech.ydb.yoj.repository.db.IndexOrder; @@ -55,6 +55,7 @@ import tech.ydb.yoj.repository.db.RecordEntity; import tech.ydb.yoj.repository.db.Repository; import tech.ydb.yoj.repository.db.RepositoryTransaction; +import tech.ydb.yoj.repository.db.ScopedTxManager; import tech.ydb.yoj.repository.db.StdTxManager; import tech.ydb.yoj.repository.db.TableDescriptor; import tech.ydb.yoj.repository.db.Tx; @@ -64,12 +65,10 @@ import tech.ydb.yoj.repository.db.exception.RetryableException; import tech.ydb.yoj.repository.db.exception.UnavailableException; import tech.ydb.yoj.repository.db.list.ListRequest; -import tech.ydb.yoj.repository.db.list.ListResult; import tech.ydb.yoj.repository.db.readtable.ReadTableParams; import tech.ydb.yoj.repository.test.RepositoryTest; import tech.ydb.yoj.repository.test.entity.TestEntities; import tech.ydb.yoj.repository.test.sample.TestDb; -import tech.ydb.yoj.repository.test.sample.TestDbImpl; import tech.ydb.yoj.repository.test.sample.model.Bubble; import tech.ydb.yoj.repository.test.sample.model.ChangefeedEntity; import tech.ydb.yoj.repository.test.sample.model.IndexedEntity; @@ -120,6 +119,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatList; +import static org.assertj.core.api.Assertions.assertThatObject; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; import static tech.ydb.yoj.repository.db.EntityExpressions.newFilterBuilder; @@ -195,14 +196,14 @@ protected YdbConfig getRealYdbConfig() { @Test public void useClosedReadTableStream() { - db.tx(() -> { + tx.run(db -> { db.projects().save(new Project(new Project.Id("1"), "p1")); db.projects().save(new Project(new Project.Id("2"), "p2")); - }); ReadTableParams params = ReadTableParams.builder().useNewSpliterator(true).build(); - Stream readOnlyStream = db.readOnly().run(() -> db.projects().readTable(params)); + // FIXME + Stream readOnlyStream = tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().readTable(params)); assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> readOnlyStream.forEach(System.err::println) @@ -217,7 +218,7 @@ public void throwConversionExceptionOnSerializationProblem() { ); assertThatExceptionOfType(ConversionException.class) - .isThrownBy(() -> db.tx(() -> db.table(NonSerializableEntity.class).insert(nonSerializableEntity))); + .isThrownBy(() -> tx.call(db -> db.table(NonSerializableEntity.class).insert(nonSerializableEntity))); } @Test @@ -226,11 +227,11 @@ public void readYqlListAndMap() { new WithUnflattenableField.Id("id_yql_list"), new WithUnflattenableField.Unflattenable("Hello, world!", 100_500) ); - db.tx(() -> db.table(WithUnflattenableField.class).insert(entity)); - db.tx(() -> { + tx.run(db -> db.table(WithUnflattenableField.class).insert(entity)); + tx.run((_1, txControl) -> { EntitySchema schema = EntitySchema.of(WithUnflattenableField.class); var tableDescriptor = TableDescriptor.from(schema); - List result = ((YdbRepositoryTransaction) Tx.Current.get().getRepositoryTransaction()) + List result = ((YdbRepositoryTransaction) txControl.getRepositoryTransaction()) .execute(new YqlStatement<>(tableDescriptor, schema, ObjectSchema.of(GroupByResult.class)) { @Override public String getQuery(String tablespace) { @@ -274,9 +275,9 @@ static class Struct { @Test public void readViewFromCache() { TypeFreak tf1 = newTypeFreak(0, "AAA1", "bbb"); - db.tx(() -> db.typeFreaks().insert(tf1)); + tx.run(db -> db.typeFreaks().insert(tf1)); - db.tx(() -> { + tx.run(db -> { TypeFreak.View foundView1 = db.typeFreaks().find(TypeFreak.View.class, tf1.getId()); TypeFreak.View foundView2 = db.typeFreaks().find(TypeFreak.View.class, tf1.getId()); // should be the same object, because of cache @@ -286,13 +287,14 @@ public void readViewFromCache() { @Test public void scanMoreThenMaxSize() { - db.tx(() -> { + tx.run(db -> { db.projects().save(new Project(new Project.Id("1"), "p1")); db.projects().save(new Project(new Project.Id("2"), "p2")); }); assertThatExceptionOfType(YdbRepositoryException.class) - .isThrownBy(() -> db.scan().withMaxSize(1).run(() -> { - db.projects().findAll(); + .isThrownBy(() -> tx.scan().withMaxSize(1).run(() -> { + // FIXME + BaseDb.current(TestDb.class).projects().findAll(); })) .satisfies(e -> assertThat(e).hasCauseInstanceOf(ResultTruncatedException.class)); } @@ -302,22 +304,23 @@ public void transactionLevel() { Project expected = new Project(new Project.Id("RO"), "readonly"); SessionManager sessionManager = ((YdbRepository) this.repository).getSessionManager(); - TestDb db = new TestDbImpl<>(this.repository); + //TestDb db = new TestDbImpl<>(this.repository); Session firstSession = sessionManager.getSession(); firstSession.close(); - db.tx(() -> db.projects().save(expected)); + tx.run(db -> db.projects().save(expected)); // Reuse the same session in RO transaction ensureSameSessionId(sessionManager, firstSession); - Project actual = db.readOnly().run(() -> db.projects().find(expected.getId())); + // FIXME + Project actual = tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().find(expected.getId())); assertThat(actual).isEqualTo(expected); // Reuse the same session in RW transaction ensureSameSessionId(sessionManager, firstSession); - actual = db.tx(() -> db.projects().find(expected.getId())); + actual = tx.call(db -> db.projects().find(expected.getId())); assertThat(actual).isEqualTo(expected); ensureSameSessionId(sessionManager, firstSession); @@ -328,21 +331,24 @@ public void snapshotTransactionLevel() { Project expected1 = new Project(new Project.Id("SP1"), "snapshot1"); Project expected2 = new Project(new Project.Id("SP2"), "snapshot2"); - db.tx(() -> db.projects().save(expected1)); - db.tx(() -> db.projects().save(expected2)); + tx.run(db -> db.projects().save(expected1)); + tx.run(db -> db.projects().save(expected2)); - Project actual1 = db.tx(() -> db.projects().find(expected1.getId())); + Project actual1 = tx.call(db -> db.projects().find(expected1.getId())); assertThat(actual1).isEqualTo(expected1); - Project actual2 = db.readOnly().run(() -> db.projects().find(expected2.getId())); + // FIXME + Project actual2 = tx.readOnly().run(() -> BaseDb.current(TestDb.class).projects().find(expected2.getId())); assertThat(actual2).isEqualTo(expected2); - db.readOnly() + tx.readOnly() .withStatementIsolationLevel(IsolationLevel.SNAPSHOT) .run(() -> { - Project actualSnapshot1 = db.projects().find(expected1.getId()); + // FIXME + Project actualSnapshot1 = BaseDb.current(TestDb.class).projects().find(expected1.getId()); assertThat(actualSnapshot1).isEqualTo(expected1); - Project actualSnapshot2 = db.projects().find(expected2.getId()); + // FIXME + Project actualSnapshot2 = BaseDb.current(TestDb.class).projects().find(expected2.getId()); assertThat(actualSnapshot2).isEqualTo(expected2); }); } @@ -351,9 +357,9 @@ public void snapshotTransactionLevel() { public void truncatedDefault() { var queryImplementation = TestYdbRepository.createRepositorySettings().queryImplementation(); if (queryImplementation instanceof QueryImplementation.TableService) { - testRowLimitEnforced(db); + testRowLimitEnforced(tx); } else if (queryImplementation instanceof QueryImplementation.QueryService) { - testRowLimitNotEnforced(db); + testRowLimitNotEnforced(tx); } else { throw new UnsupportedOperationException("Unknown query implementation: <" + queryImplementation.getClass() + ">"); } @@ -368,9 +374,9 @@ public void truncatedExplicitTableService() { .build(), ydbEnvAndTransport.getGrpcTransport() ); - TestDb db = new TestDbImpl<>(ydbRepository); + ScopedTxManager tx = new ScopedTxManager<>(ydbRepository, TestDb.class); - testRowLimitEnforced(db); + testRowLimitEnforced(tx); } @Test @@ -382,13 +388,13 @@ public void truncatedExplicitQueryService() { .build(), ydbEnvAndTransport.getGrpcTransport() ); - TestDb db = new TestDbImpl<>(ydbRepository); + ScopedTxManager tx = new ScopedTxManager<>(ydbRepository, TestDb.class); - testRowLimitNotEnforced(db); + testRowLimitNotEnforced(tx); } @SneakyThrows - private void testRowLimitEnforced(TestDb db) { + private void testRowLimitEnforced(ScopedTxManager tx) { int rowLimit = YdbEnvAndTransportRule.TABLESERVICE_ROW_LIMIT; int maxPageSizeBiggerThatReal = rowLimit + 1; @@ -400,24 +406,24 @@ private void testRowLimitEnforced(TestDb db) { } ListRequest bigListRequest = builder.build(); ListRequest smallListRequest = ListRequest.builder(Project.class).pageSize(100).build(); - db.tx(() -> db.projects().save(new Project(new Project.Id("id"), "name"))); + tx.run(db -> db.projects().save(new Project(new Project.Id("id"), "name"))); - db.tx(() -> db.projects().list(smallListRequest)); - db.tx(() -> db.projects().list(bigListRequest)); - db.tx(() -> db.projects().findAll()); + tx.run(db -> db.projects().list(smallListRequest)); + tx.run(db -> db.projects().list(bigListRequest)); + tx.run(db -> db.projects().findAll()); - db.tx(() -> IntStream.range(0, maxPageSizeBiggerThatReal) + tx.run(db -> IntStream.range(0, maxPageSizeBiggerThatReal) .forEach(i -> db.projects().save(new Project(new Project.Id("id_" + i), "name")))); - db.tx(() -> db.projects().list(smallListRequest)); + tx.run(db -> db.projects().list(smallListRequest)); assertThatExceptionOfType(ResultTruncatedException.class) - .isThrownBy(() -> db.tx(() -> db.projects().list(bigListRequest))) + .isThrownBy(() -> tx.run(db -> db.projects().list(bigListRequest))) .satisfies(te -> { assertThat(te.getMaxResultRows()).isEqualTo(rowLimit); assertThat(te.getRowCount()).isGreaterThanOrEqualTo(rowLimit); }); assertThatExceptionOfType(ResultTruncatedException.class) - .isThrownBy(() -> db.tx(() -> db.projects().findAll())) + .isThrownBy(() -> tx.run(db -> db.projects().findAll())) .satisfies(te -> { assertThat(te.getMaxResultRows()).isEqualTo(rowLimit); assertThat(te.getRowCount()).isGreaterThanOrEqualTo(rowLimit); @@ -425,7 +431,7 @@ private void testRowLimitEnforced(TestDb db) { } @SneakyThrows - private void testRowLimitNotEnforced(TestDb db) { + private void testRowLimitNotEnforced(ScopedTxManager tx) { ListRequest.Builder builder = ListRequest.builder(Project.class); // (KQP_MAX_RESULT_ROWS)+1K @@ -437,30 +443,30 @@ private void testRowLimitNotEnforced(TestDb db) { } ListRequest bigListRequest = builder.build(); ListRequest smallListRequest = ListRequest.builder(Project.class).pageSize(100).build(); - db.tx(() -> db.projects().save(new Project(new Project.Id("id"), "name"))); + tx.run(db -> db.projects().save(new Project(new Project.Id("id"), "name"))); - db.tx(() -> db.projects().list(smallListRequest)); - db.tx(() -> db.projects().list(bigListRequest)); - db.tx(() -> db.projects().findAll()); + tx.run(db -> db.projects().list(smallListRequest)); + tx.run(db -> db.projects().list(bigListRequest)); + tx.run(db -> db.projects().findAll()); - db.tx(() -> IntStream.range(0, pageSize) + tx.run(db -> IntStream.range(0, pageSize) .forEach(i -> db.projects().save(new Project(new Project.Id("id_" + i), "name")))); - db.tx(() -> db.projects().list(smallListRequest)); - Assertions.>assertThat(db.tx(() -> db.projects().list(bigListRequest))).satisfies(listResult -> { + tx.run(db -> db.projects().list(smallListRequest)); + assertThatObject(tx.call(db -> db.projects().list(bigListRequest))).satisfies(listResult -> { assertThat(listResult.getEntries()).hasSize(pageSize); assertThat(listResult.isLastPage()).isFalse(); }); - assertThat(db.tx(() -> db.projects().findAll())).hasSize(pageSize + 1); + assertThatList(tx.call(db -> db.projects().findAll())).hasSize(pageSize + 1); } @Test public void inSingleElementListOptimizedToEq() { Project expected1 = new Project(new Project.Id("SP1"), "snapshot1"); Project expected2 = new Project(new Project.Id("SP2"), "snapshot2"); - db.tx(() -> db.projects().insert(expected1, expected2)); + tx.run(db -> db.projects().insert(expected1, expected2)); - List found = db.tx(() -> db.projects().query() + List found = tx.call(db -> db.projects().query() .where("id").in(expected1.getId()) .find()); assertThat(found).singleElement().isEqualTo(expected1); @@ -470,9 +476,9 @@ public void inSingleElementListOptimizedToEq() { public void notInSingleElementListOptimizedToNeq() { Project expected1 = new Project(new Project.Id("SP1"), "snapshot1"); Project expected2 = new Project(new Project.Id("SP2"), "snapshot2"); - db.tx(() -> db.projects().insert(expected1, expected2)); + tx.run(db -> db.projects().insert(expected1, expected2)); - List found = db.tx(() -> db.projects().query() + List found = tx.call(db -> db.projects().query() .where("id").notIn(expected1.getId()) .find()); assertThat(found).singleElement().isEqualTo(expected2); @@ -521,18 +527,18 @@ public void usesTupleAsParamTest() { var id2 = new Bubble.Id("b", "c"); var id3 = new Bubble.Id("b", "a"); - db.tx(() -> { + tx.run(db -> { db.bubbles().insert(new Bubble(id1, "oldA", "oldB", "oldC")); db.bubbles().insert(new Bubble(id2, "oldA", "oldB", "oldC")); db.bubbles().insert(new Bubble(id3, "oldA", "oldB", "oldC")); }); - db.tx(() -> this.db.bubbles().updateSomeFields(Set.of(id1, id2), "newA", "newB")); + tx.run(db -> db.bubbles().updateSomeFields(Set.of(id1, id2), "newA", "newB")); - db.tx(() -> { - var first = this.db.bubbles().find(id1); - var second = this.db.bubbles().find(id2); - var third = this.db.bubbles().find(id3); + tx.run(db -> { + var first = db.bubbles().find(id1); + var second = db.bubbles().find(id2); + var third = db.bubbles().find(id3); assertThat(first.getFieldA()).isEqualTo("newA"); assertThat(first.getFieldB()).isEqualTo("newB"); @@ -552,15 +558,15 @@ public void usesTupleAsParamTest() { public void updateInSingleKey() { var id1 = new IndexedEntity.Id("1"); var id2 = new IndexedEntity.Id("2"); - db.tx(() -> { + tx.run(db -> { db.indexedTable().insert(new IndexedEntity(id1, "11", "111", "1111")); db.indexedTable().insert(new IndexedEntity(id2, "22", "222", "2222")); }); - db.tx(() -> db.indexedTable().updateSomeFields(Set.of(id1, id2), "4", "5")); - db.tx(() -> { - var v1 = this.db.indexedTable().find(id1); - var v2 = this.db.indexedTable().find(id2); + tx.run(db -> db.indexedTable().updateSomeFields(Set.of(id1, id2), "4", "5")); + tx.run(db -> { + var v1 = db.indexedTable().find(id1); + var v2 = db.indexedTable().find(id2); assertThat(v1).isEqualTo(new IndexedEntity(id1, "11", "4", "5")); assertThat(v2).isEqualTo(new IndexedEntity(id2, "22", "4", "5")); @@ -570,10 +576,8 @@ public void updateInSingleKey() { @Test public void predicateWithBoxedValues() { var p = new Project(new Project.Id("abcdefg"), "hijklmnop"); - db.tx(() -> { - db.projects().save(p); - }); - db.tx(() -> { + tx.run(db -> db.projects().save(p)); + tx.run(db -> { var table = (YdbTable) db.table(Project.class); var res = table.find(YqlPredicate.where("id").eq(p.getId())); assertThat(res) @@ -585,10 +589,8 @@ public void predicateWithBoxedValues() { @Test public void predicateWithMultipleBoxedId() { var m = new MultiWrappedEntity(new MultiWrappedEntity.Id(new MultiWrappedEntity.StringWrapper("string-id")), "payload", null); - db.tx(() -> { - db.multiWrappedIdEntities().save(m); - }); - db.tx(() -> { + tx.run(db -> db.multiWrappedIdEntities().save(m)); + tx.run(db -> { assertThat(db.multiWrappedIdEntities().query().where("id").eq(m.id()).findOne()).isEqualTo(m); assertThat(db.multiWrappedIdEntities().query().where("id").eq(m.id().itIsReallyString()).findOne()).isEqualTo(m); assertThat(db.multiWrappedIdEntities().query().where("id").eq(m.id().itIsReallyString().value()).findOne()).isEqualTo(m); @@ -607,10 +609,8 @@ public void predicateWithMultipleBoxedPayload() { "fakefakefake", new MultiWrappedEntity.OptionalPayload(new MultiWrappedEntity.StringWrapper("real-payload")) ); - db.tx(() -> { - db.multiWrappedIdEntities().save(m); - }); - db.tx(() -> { + tx.run(db -> db.multiWrappedIdEntities().save(m)); + tx.run(db -> { assertThat(db.multiWrappedIdEntities().query().where("optionalPayload").eq(m.optionalPayload()).findOne()).isEqualTo(m); assertThat(db.multiWrappedIdEntities().query().where("optionalPayload").eq(m.optionalPayload().wrapper()).findOne()).isEqualTo(m); assertThat(db.multiWrappedIdEntities().query().where("optionalPayload").eq(m.optionalPayload().wrapper().value()).findOne()).isEqualTo(m); @@ -624,7 +624,7 @@ public void predicateWithMultipleBoxedPayload() { @Test public void testSelectDefault() { - db.tx(() -> db.indexedTable().insert(e1, e2)); + tx.run(db -> db.indexedTable().insert(e1, e2)); executeQuery("DECLARE $pred_0_version_id AS String;\n" + "SELECT `version_id`, `key_id`, `value_id`, `valueId2` " + "FROM `ts/table_with_indexes` " + @@ -636,7 +636,7 @@ public void testSelectDefault() { @Test public void testSelectIndex1Default() { - db.tx(() -> db.indexedTable().insert(e1, e2)); + tx.run(db -> db.indexedTable().insert(e1, e2)); executeQuery("DECLARE $pred_0_key_id AS String;\n" + "SELECT `version_id`, `key_id`, `value_id`, `valueId2` " + "FROM `ts/table_with_indexes` " + @@ -650,7 +650,7 @@ public void testSelectIndex1Default() { public void testSelectIndex1WithoutFields() { // No exception at this point, but should be? - db.tx(() -> db.indexedTable().insert(e1, e2)); + tx.run(db -> db.indexedTable().insert(e1, e2)); executeQuery("DECLARE $pred_0_version_id AS String;\n" + "SELECT `version_id`, `key_id`, `value_id`, `valueId2` " + "FROM `ts/table_with_indexes` VIEW `key_index` " + @@ -663,7 +663,7 @@ public void testSelectIndex1WithoutFields() { @Test public void testSelectIndex1WithEmptyIndex() { - db.tx(() -> db.indexedTable().insert(e1, e2)); + tx.run(db -> db.indexedTable().insert(e1, e2)); executeQuery("DECLARE $pred_0_key_id AS String;\n" + "SELECT `version_id`, `key_id`, `value_id`, `valueId2` " + "FROM `ts/table_with_indexes` " + @@ -675,7 +675,7 @@ public void testSelectIndex1WithEmptyIndex() { @Test public void testSelectIndex1WithIndex() { - db.tx(() -> db.indexedTable().insert(e1, e2)); + tx.run(db -> db.indexedTable().insert(e1, e2)); executeQuery("DECLARE $pred_0_key_id AS String;\n" + "SELECT `version_id`, `key_id`, `value_id`, `valueId2` " + "FROM `ts/table_with_indexes` VIEW `key_index` " + @@ -688,7 +688,7 @@ public void testSelectIndex1WithIndex() { @Test public void testSelectIndex2Default() { - db.tx(() -> db.indexedTable().insert(e1, e2)); + tx.run(db -> db.indexedTable().insert(e1, e2)); executeQuery("DECLARE $pred_0_value_id AS String;\n" + "DECLARE $pred_1_valueId2 AS String;\n" + "SELECT `version_id`, `key_id`, `value_id`, `valueId2` " + @@ -701,7 +701,7 @@ public void testSelectIndex2Default() { @Test public void testSelectIndex2WithIndex() { - db.tx(() -> db.indexedTable().insert(e1, e2)); + tx.run(db -> db.indexedTable().insert(e1, e2)); executeQuery("DECLARE $pred_0_value_id AS String;\n" + "DECLARE $pred_1_valueId2 AS String;\n" + "SELECT `version_id`, `key_id`, `value_id`, `valueId2` " + @@ -717,7 +717,7 @@ public void testSelectIndex2WithIndex() { public void testSelectIndex2WithFirstFieldOnly() { // No exception at this point, but should be? - db.tx(() -> db.indexedTable().insert(e1, e2)); + tx.run(db -> db.indexedTable().insert(e1, e2)); executeQuery("DECLARE $pred_0_value_id AS String;\n" + "SELECT `version_id`, `key_id`, `value_id`, `valueId2` " + "FROM `ts/table_with_indexes` VIEW `value_index` " + @@ -732,7 +732,7 @@ public void testSelectIndex2WithFirstFieldOnly() { public void testSelectIndex2WithSecondFieldOnly() { // No exception at this point, but should be? - db.tx(() -> db.indexedTable().insert(e1, e2)); + tx.run(db -> db.indexedTable().insert(e1, e2)); executeQuery("DECLARE $pred_0_valueId2 AS String;\n" + "SELECT `version_id`, `key_id`, `value_id`, `valueId2` " + "FROM `ts/table_with_indexes` VIEW `value_index` " + @@ -745,7 +745,7 @@ public void testSelectIndex2WithSecondFieldOnly() { @Test public void testBuildStatementPartsWithGlobalIndex() { - db.tx(() -> db.indexedTable().insert(e1, e2)); + tx.run(db -> db.indexedTable().insert(e1, e2)); var filter = newFilterBuilder(IndexedEntity.class) .where("valueId2").eq("value2.1") @@ -777,12 +777,12 @@ public void complexInPredicate() { )); } var searchingBubbles = bubbles.subList(0, bubbles.size() / 2); - db.tx(() -> db.bubbles().insertAll(bubbles)); + tx.run(db -> db.bubbles().insertAll(bubbles)); var searchingIds = searchingBubbles.stream() .map(Bubble::getId) .collect(Collectors.toList()); - db.tx(() -> { + tx.run(db -> { var table = (YdbTable) db.bubbles(); var foundBubbles = table.find( YqlPredicate.where("id").in(searchingIds) @@ -796,18 +796,18 @@ public void bulkInserts() { var id1 = new Bubble.Id("a", "b"); var id2 = new Bubble.Id("c", "d"); - db.tx(() -> { - db.bubbles().bulkUpsert( - List.of(new Bubble(id1, "oldA", "oldB", "oldC"), new Bubble(id2, "oldA", "oldB", "oldC")), - BulkParams.DEFAULT - ); - }); + tx.run(db -> db.bubbles().bulkUpsert( + List.of(new Bubble(id1, "oldA", "oldB", "oldC"), new Bubble(id2, "oldA", "oldB", "oldC")), + BulkParams.DEFAULT + )); - db.readOnly().run(() -> { - var first = this.db.bubbles().find(id1); + tx.readOnly().run(() -> { + // FIXME + var first = BaseDb.current(TestDb.class).bubbles().find(id1); assertThat(first).isNotNull(); assertThat(first.getFieldA()).isEqualTo("oldA"); - assertThat(this.db.bubbles().find(id2)).isNotNull(); + // FIXME + assertThat(BaseDb.current(TestDb.class).bubbles().find(id2)).isNotNull(); }); } @@ -821,9 +821,9 @@ public void testTransactionTakesTimeoutFromGrpcContext() { } private void testTransactionTakesTimeoutFromGrpcContext(int timeoutMin) { - db.withTimeout(Duration.ofMinutes(timeoutMin)).tx(() -> { - RepositoryTransaction transaction = Tx.Current.get().getRepositoryTransaction(); - var testTransaction = (TestYdbRepository.TestYdbRepositoryTransaction) transaction; + tx.withTimeout(Duration.ofMinutes(timeoutMin)).run((_1, txControl) -> { + RepositoryTransaction transaction = txControl.getRepositoryTransaction(); + var testTransaction = (YdbRepositoryTransaction) transaction; var actualTimeout = testTransaction.getOptions().getTimeoutOptions().getTimeout(); assertThat(actualTimeout.toMinutes()).isEqualTo(timeoutMin); }); @@ -910,9 +910,9 @@ public void complexIdLtYsingYqlPredicate() { Supabubble2 sa = new Supabubble2(new Supabubble2.Id(new Project.Id("naher"), "bubble-A")); Supabubble2 sb = new Supabubble2(new Supabubble2.Id(new Project.Id("naher"), "bubble-B")); Supabubble2 sc = new Supabubble2(new Supabubble2.Id(new Project.Id("naher"), "bubble-C")); - db.tx(() -> db.supabubbles2().insert(sa, sb, sc)); + tx.run(db -> db.supabubbles2().insert(sa, sb, sc)); - assertThat(db.tx(() -> db.supabubbles2().findLessThan(sc.getId()))).containsOnly(sa, sb); + assertThatList(tx.call(db -> db.supabubbles2().findLessThan(sc.getId()))).containsOnly(sa, sb); } private void executeQuery(String expectSqlQuery, List expectRows, List> parts) { @@ -925,7 +925,7 @@ private void executeQuery(String expectSqlQuery, List expectRows, assertThat(sqlQuery).isEqualTo(expectSqlQuery); // Check we use index and query was not failed - var actual = db.tx(() -> ((YdbTable) db.indexedTable()).find(parts)); + var actual = tx.call(db -> ((YdbTable) db.indexedTable()).find(parts)); assertThat(actual).isEqualTo(expectRows); } @@ -1017,7 +1017,7 @@ public void creatingRepositoryDoesNotConnect() { @Test public void ydbTransactionCompatibility() { - db.tx(() -> { + tx.run(db -> { // No db tx or session yet! var sdkTx = ((YdbRepositoryTransaction) Tx.Current.get().getRepositoryTransaction()).toSdkTransaction(); assertThatIllegalStateException().isThrownBy(sdkTx::getSessionId); @@ -1043,8 +1043,9 @@ public void ydbTransactionCompatibility() { var isolationLevel = entry.getKey(); var txMode = entry.getValue(); - db.readOnly().withStatementIsolationLevel(isolationLevel).run(() -> { + tx.readOnly().withStatementIsolationLevel(isolationLevel).run(() -> { // No db tx or session yet! + // FIXME - use txControl when available var sdkTx = ((YdbRepositoryTransaction) Tx.Current.get().getRepositoryTransaction()).toSdkTransaction(); assertThatIllegalStateException().isThrownBy(sdkTx::getSessionId); assertThat(sdkTx.getId()).isNull(); @@ -1052,7 +1053,8 @@ public void ydbTransactionCompatibility() { assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(sdkTx::getStatusFuture); // Perform any read - session and tx ID appear - db.projects().countAll(); + // FIXME + BaseDb.current(TestDb.class).projects().countAll(); sdkTx = ((YdbRepositoryTransaction) Tx.Current.get().getRepositoryTransaction()).toSdkTransaction(); assertThat(sdkTx.getSessionId()).isNotNull(); // Read transactions might have no ID or might have an ID, depending on your YDB version (that's what YDB returns, folks!) @@ -1067,9 +1069,9 @@ public void unordered() { // YDB tends to return data in index-order, not "by PK ascending" order, if we don't force the result order IndexedEntity ie1 = new IndexedEntity(new IndexedEntity.Id("abc"), "z", "v1-1", "v1-2"); IndexedEntity ie2 = new IndexedEntity(new IndexedEntity.Id("def"), "y", "v2-1", "v2-2"); - db.tx(() -> db.indexedTable().insert(ie1, ie2)); + tx.run(db -> db.indexedTable().insert(ie1, ie2)); - var results = db.tx(() -> db.indexedTable().query() + var results = tx.call(db -> db.indexedTable().query() .where("keyId").gte("a") .limit(2) .index(IndexedEntity.KEY_INDEX, IndexOrder.UNORDERED) @@ -1080,15 +1082,15 @@ public void unordered() { @Test public void multipleTablesSameEntitySameTransaction() { UniqueProject ue = new UniqueProject(new UniqueProject.Id("id1"), "valuableName", 1); - db.tx(() -> db.table(UniqueProject.class).save(ue)); + tx.run(db -> db.table(UniqueProject.class).save(ue)); - List findFirstTableThenSecond = db.tx(() -> { + List findFirstTableThenSecond = tx.call(db -> { var p1 = db.table(UniqueProject.class).find(ue.getId()); var p2 = db.table(TestEntities.SECOND_UNIQUE_PROJECT_TABLE).find(ue.getId()); return Stream.of(p1, p2).filter(Objects::nonNull).toList(); }); - List findSecondTableThenFirst = db.tx(() -> { + List findSecondTableThenFirst = tx.call(db -> { var p1 = db.table(TestEntities.SECOND_UNIQUE_PROJECT_TABLE).find(ue.getId()); var p2 = db.table(UniqueProject.class).find(ue.getId()); return Stream.of(p1, p2).filter(Objects::nonNull).toList(); @@ -1100,15 +1102,15 @@ public void multipleTablesSameEntitySameTransaction() { @Test public void multipleTablesSameEntitySameTransactionQueryView() { UniqueProject ue = new UniqueProject(new UniqueProject.Id("id1"), "valuableName", 1); - db.tx(() -> db.table(UniqueProject.class).save(ue)); + tx.run(db -> db.table(UniqueProject.class).save(ue)); - List findFirstTableThenSecond = db.tx(() -> { + List findFirstTableThenSecond = tx.call(db -> { var n1 = db.table(UniqueProject.class).find(UniqueProject.NameView.class, ue.getId()); var n2 = db.table(TestEntities.SECOND_UNIQUE_PROJECT_TABLE).find(UniqueProject.NameView.class, ue.getId()); return Stream.of(n1, n2).filter(Objects::nonNull).toList(); }); - List findSecondTableThenFirst = db.tx(() -> { + List findSecondTableThenFirst = tx.call(db -> { var n1 = db.table(TestEntities.SECOND_UNIQUE_PROJECT_TABLE).find(UniqueProject.NameView.class, ue.getId()); var n2 = db.table(UniqueProject.class).find(UniqueProject.NameView.class, ue.getId()); return Stream.of(n1, n2).filter(Objects::nonNull).toList(); @@ -1119,7 +1121,7 @@ public void multipleTablesSameEntitySameTransactionQueryView() { @Test public void queryStatsCollectionMode() { - db.tx(() -> { + tx.run(db -> { for (int i = 0; i < 1_000; i++) { var ue = new UniqueProject(new UniqueProject.Id("id" + i), "valuableName-" + i, i); db.table(UniqueProject.class).save(ue); @@ -1133,7 +1135,8 @@ public void queryStatsCollectionMode() { .readOnly() .noFirstLevelCache() .withStatementIsolationLevel(IsolationLevel.SNAPSHOT) - .run(() -> db.table(UniqueProject.class).query() + // FIXME + .run(() -> BaseDb.current(TestDb.class).table(UniqueProject.class).query() .where("id").in(List.of( new UniqueProject.Id("id501"), new UniqueProject.Id("id502"), @@ -1173,14 +1176,14 @@ public void customQueryTracingFilter() { Configurator.setLevel(YdbRepositoryTransaction.class.getCanonicalName(), Level.TRACE); try { var filterCallCount = new AtomicInteger(); - var txMgr1 = db.withName("with-query-tracing") + var txMgr1 = tx.withName("with-query-tracing") .withTracingFilter((_1, _2, _3, _4) -> { filterCallCount.incrementAndGet(); return true; }) .immediateWrites(); var project3 = new ToStringCountingProject(new ToStringCountingProject.Id("id3"), "name-3", 3); - txMgr1.tx(() -> { + txMgr1.run(db -> { db.table(ToStringCountingProject.class).insert(new ToStringCountingProject(new ToStringCountingProject.Id("id1"), "name-1", 1)); db.table(ToStringCountingProject.class).insert(new ToStringCountingProject(new ToStringCountingProject.Id("id2"), "name-2", 2)); db.table(ToStringCountingProject.class).insert(project3); @@ -1193,7 +1196,8 @@ public void customQueryTracingFilter() { .readOnly() .noFirstLevelCache() .withStatementIsolationLevel(IsolationLevel.SNAPSHOT); - var found = txMgr2.run(() -> db.table(ToStringCountingProject.class).find(new ToStringCountingProject.Id("id3"))); + // FIXME + var found = txMgr2.run(() -> BaseDb.current(TestDb.class).table(ToStringCountingProject.class).find(new ToStringCountingProject.Id("id3"))); assertThat(found).isEqualTo(project3); // Allow only 1 call to toString() in logging of debug result, but not the second one in TRACE: @@ -1234,7 +1238,7 @@ public void transactionalTopicWrites() { var data = CommonConverters.serializeOpaqueObjectValue(Project.class, project).getBytes(UTF_8); try { - db.immediateWrites().tx(() -> { + tx.immediateWrites().run(db -> { db.projects().save(project); var transaction = (YdbRepositoryTransaction) Tx.Current.get().getRepositoryTransaction(); @@ -1279,7 +1283,7 @@ public void transactionalTopicWritesRetryThenCommit() { var retried = new AtomicBoolean(); try { - db.immediateWrites().tx(() -> { + tx.immediateWrites().run(db -> { db.projects().save(project); var transaction = (YdbRepositoryTransaction) Tx.Current.get().getRepositoryTransaction(); @@ -1327,7 +1331,7 @@ public void transactionalTopicWritesRollbackReadNothing() { var data = CommonConverters.serializeOpaqueObjectValue(Project.class, project).getBytes(UTF_8); try { - assertThatIllegalStateException().isThrownBy(() -> db.immediateWrites().tx(() -> { + assertThatIllegalStateException().isThrownBy(() -> tx.immediateWrites().call(db -> { db.projects().save(project); var transaction = (YdbRepositoryTransaction) Tx.Current.get().getRepositoryTransaction(); diff --git a/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/list/YdbListingIntegrationTest.java b/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/list/YdbListingIntegrationTest.java index 7b9a7dd5..51759795 100644 --- a/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/list/YdbListingIntegrationTest.java +++ b/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/list/YdbListingIntegrationTest.java @@ -32,7 +32,7 @@ public void idPredicatesAreProperlyOrdered() { Complex c2 = new Complex(new Complex.Id(999_999, 15L, "UUU", Complex.Status.OK)); Complex c3 = new Complex(new Complex.Id(999_999, 15L, "KKK", Complex.Status.OK)); Complex c4 = new Complex(new Complex.Id(999_000, 15L, "AAA", Complex.Status.OK)); - db.tx(() -> db.complexes().insert(c1, c2, c3, c4)); + tx.run(db -> db.complexes().insert(c1, c2, c3, c4)); FilterExpression filter = newFilterBuilder(Complex.class).where("id.b").eq(15L).and("id.a").eq(999_999).build(); ListRequest listRequest = ListRequest.builder(Complex.class).pageSize(3).filter(filter).build(); @@ -40,8 +40,8 @@ public void idPredicatesAreProperlyOrdered() { YqlPredicate predicate = YqlListingQuery.toYqlPredicate(filter); assertThat(predicate.toYql(EntitySchema.of(Complex.class))).isEqualTo("(`id_a` = ?) AND (`id_b` = ?)"); - db.tx(() -> { - ListResult page = listComplex(listRequest); + tx.run(db -> { + ListResult page = db.complexes().list(listRequest); assertThat(page).containsExactly(c3, c2, c1); assertThat(page.isLastPage()).isTrue(); }); diff --git a/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/merge/YqlQueryMergerIntegrationTest.java b/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/merge/YqlQueryMergerIntegrationTest.java index c3c1b806..8ee4dbef 100644 --- a/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/merge/YqlQueryMergerIntegrationTest.java +++ b/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/merge/YqlQueryMergerIntegrationTest.java @@ -3,10 +3,10 @@ import org.junit.ClassRule; import org.junit.Test; import tech.ydb.yoj.repository.db.Repository; +import tech.ydb.yoj.repository.db.ScopedTxManager; import tech.ydb.yoj.repository.test.RepositoryTestSupport; import tech.ydb.yoj.repository.test.entity.TestEntities; import tech.ydb.yoj.repository.test.sample.TestDb; -import tech.ydb.yoj.repository.test.sample.TestDbImpl; import tech.ydb.yoj.repository.test.sample.model.Project; import tech.ydb.yoj.repository.ydb.TestYdbRepository; import tech.ydb.yoj.repository.ydb.YdbEnvAndTransportRule; @@ -20,17 +20,17 @@ public class YqlQueryMergerIntegrationTest extends RepositoryTestSupport { @ClassRule public static final YdbEnvAndTransportRule ydbEnvAndTransport = new YdbEnvAndTransportRule(); - protected TestDb db; + protected ScopedTxManager tx; @Override public void setUp() { super.setUp(); - this.db = new TestDbImpl<>(this.repository); + this.tx = new ScopedTxManager<>(this.repository, TestDb.class); } @Override public void tearDown() { - this.db = null; + this.tx = null; super.tearDown(); } @@ -41,7 +41,7 @@ protected final Repository createRepository() { @Test public void insertAfterFindByIdWorks() { - db.tx(() -> { + tx.run(db -> { Project.Id id = new Project.Id(RandomUtils.nextString(10)); assertThat(db.projects().find(id)).isNull(); diff --git a/repository/src/main/java/tech/ydb/yoj/repository/db/DelegatingTxManager.java b/repository/src/main/java/tech/ydb/yoj/repository/db/DelegatingTxManager.java index 246a4ac7..1fe1a449 100644 --- a/repository/src/main/java/tech/ydb/yoj/repository/db/DelegatingTxManager.java +++ b/repository/src/main/java/tech/ydb/yoj/repository/db/DelegatingTxManager.java @@ -18,10 +18,13 @@ * doRunTx()} * * + * @param This class's type. This allows to have {@code TxManager} configuration calls return specific subtype + * instead of just {@code TxManager} + * * @see #wrapTxBody(Supplier) * @see #doRunTx(Supplier) */ -public abstract class DelegatingTxManager implements TxManager { +public abstract class DelegatingTxManager> implements TxManager { protected final TxManager delegate; protected DelegatingTxManager(@NonNull TxManager delegate) { @@ -61,7 +64,7 @@ protected Supplier wrapTxBody(Supplier supplier) { * @param delegate transaction manager to delegate to * @return wrapped {@code delegate} */ - protected abstract TxManager createTxManager(TxManager delegate); + protected abstract SELF createTxManager(TxManager delegate); @Override public final void tx(Runnable runnable) { @@ -77,102 +80,102 @@ public final T tx(Supplier supplier) { } @Override - public final TxManager withName(String name, String logContext) { + public final SELF withName(String name, String logContext) { return createTxManager(this.delegate.withName(name, logContext)); } @Override - public final TxManager withName(String name) { + public final SELF withName(String name) { return createTxManager(this.delegate.withName(name)); } @Override - public final TxManager withLogContext(String logContext) { + public final SELF withLogContext(String logContext) { return createTxManager(this.delegate.withLogContext(logContext)); } @Override - public final TxManager separate() { + public final SELF separate() { return createTxManager(this.delegate.separate()); } @Override - public TxManager delayedWrites() { + public final SELF delayedWrites() { return createTxManager(this.delegate.delayedWrites()); } @Override - public final TxManager immediateWrites() { + public final SELF immediateWrites() { return createTxManager(this.delegate.immediateWrites()); } @Override - public final TxManager noFirstLevelCache() { + public final SELF noFirstLevelCache() { return createTxManager(this.delegate.noFirstLevelCache()); } @Override - public final TxManager failOnUnknownSeparateTx() { + public final SELF failOnUnknownSeparateTx() { return createTxManager(this.delegate.failOnUnknownSeparateTx()); } @Override - public final TxManager withMaxRetries(int maxRetries) { + public final SELF withMaxRetries(int maxRetries) { return createTxManager(this.delegate.withMaxRetries(maxRetries)); } @Override - public final TxManager withDryRun(boolean dryRun) { + public final SELF withDryRun(boolean dryRun) { return createTxManager(this.delegate.withDryRun(dryRun)); } @Override - public final TxManager withLogLevel(TransactionLog.Level level) { + public final SELF withLogLevel(TransactionLog.Level level) { return createTxManager(this.delegate.withLogLevel(level)); } @Override - public final TxManager withLogStatementOnSuccess(boolean logStatementOnSuccess) { + public final SELF withLogStatementOnSuccess(boolean logStatementOnSuccess) { return createTxManager(this.delegate.withLogStatementOnSuccess(logStatementOnSuccess)); } @Override - public final TxManager withTimeout(@NonNull Duration timeout) { + public final SELF withTimeout(@NonNull Duration timeout) { return createTxManager(this.delegate.withTimeout(timeout)); } @Override - public final TxManager withQueryStats(@NonNull QueryStatsMode queryStats) { + public final SELF withQueryStats(@NonNull QueryStatsMode queryStats) { return createTxManager(this.delegate.withQueryStats(queryStats)); } @Override - public TxManager withFullQueryTracing() { + public final SELF withFullQueryTracing() { return createTxManager(this.delegate.withFullQueryTracing()); } @Override - public TxManager noQueryTracing() { + public final SELF noQueryTracing() { return createTxManager(this.delegate.noQueryTracing()); } @Override - public TxManager withTracingFilter(@NonNull QueryTracingFilter tracingFilter) { + public final SELF withTracingFilter(@NonNull QueryTracingFilter tracingFilter) { return createTxManager(this.delegate.withTracingFilter(tracingFilter)); } @Override - public final TxManager withVerboseLogging() { + public final SELF withVerboseLogging() { return createTxManager(this.delegate.withVerboseLogging()); } @Override - public final TxManager withBriefLogging() { + public final SELF withBriefLogging() { return createTxManager(this.delegate.withBriefLogging()); } @Override - public final TxManager noLogging() { + public final SELF noLogging() { return createTxManager(this.delegate.noLogging()); } diff --git a/repository/src/main/java/tech/ydb/yoj/repository/db/ScopedTxManager.java b/repository/src/main/java/tech/ydb/yoj/repository/db/ScopedTxManager.java new file mode 100644 index 00000000..cefd27fa --- /dev/null +++ b/repository/src/main/java/tech/ydb/yoj/repository/db/ScopedTxManager.java @@ -0,0 +1,49 @@ +package tech.ydb.yoj.repository.db; + +import lombok.NonNull; +import tech.ydb.yoj.ExperimentalApi; +import tech.ydb.yoj.repository.BaseDb; + +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + +@ExperimentalApi(issue = "https://github.com/ydb-platform/yoj-project/issues/197") +public final class ScopedTxManager extends DelegatingTxManager> { + private final D db; + + public ScopedTxManager(@NonNull Repository repository, @NonNull Class dbClass) { + this(new StdTxManager(repository), dbClass); + } + + public ScopedTxManager(@NonNull TxManager delegate, @NonNull Class dbClass) { + this(delegate, BaseDb.current(dbClass)); + } + + private ScopedTxManager(@NonNull TxManager delegate, @NonNull D db) { + super(delegate); + this.db = db; + } + + public R call(@NonNull Function function) { + return tx(() -> function.apply(db)); + } + + public T call(@NonNull BiFunction function) { + return tx(() -> function.apply(db, Tx.Current.get())); + } + + public void run(@NonNull Consumer consumer) { + tx(() -> consumer.accept(db)); + } + + public void run(@NonNull BiConsumer consumer) { + tx(() -> consumer.accept(db, Tx.Current.get())); + } + + @Override + protected ScopedTxManager createTxManager(TxManager delegate) { + return new ScopedTxManager<>(delegate, this.db); + } +}