Skip to content

feat: extract tzatziki-db (JDBC) and tzatziki-jpa (plain JPA) from tzatziki-spring-jpa - #868

Open
alexandrepa wants to merge 35 commits into
mainfrom
tzatziki-db
Open

alexandrepa wants to merge 35 commits into
mainfrom
tzatziki-db

Conversation

@alexandrepa

@alexandrepa alexandrepa commented Apr 29, 2026 •

Copy link
Copy Markdown
Collaborator

Why

Database steps were only available through tzatziki-spring-jpa, which requires a Spring context and Spring Data repositories. Projects without Spring, or using plain JDBC/JPA, could not reuse the table and entity steps.

What

tzatziki-spring-jpa is split into three layered modules. Each one reuses the steps of the module below it.

Module Depends on Provides
tzatziki-db (new) tzatziki-core JDBC table steps (the <table> table will contain / contains / contains nothing / is the <table> table content), trigger toggling, database cleanup (DatabaseCleaner, autoclean, schemasToClean) and a DbBackend SPI with a default JdbcBackend
tzatziki-jpa (new) tzatziki-db, Hibernate 6 Entity steps (the <Entity> entities will contain / contain …) and JpaBackend. JpaBackend routes table steps to the entity whose @Table matches, and falls back to JDBC for tables without an entity (e.g. join tables)
tzatziki-spring-jpa tzatziki-jpa, tzatziki-spring Repository steps and ordered reads. SpringJpaBackend extends JpaBackend: inserts go through the Spring Data repositories, and reads go through Spring's shared EntityManager
graph LR
  core[tzatziki-core] --> db[tzatziki-db]
  db --> jpa[tzatziki-jpa]
  jpa --> sjpa[tzatziki-spring-jpa]
  spring[tzatziki-spring] --> sjpa
Loading

Backward compatibility

  • Feature files: every existing step sentence keeps working unchanged. Table and entity steps now live in DatabaseSteps and JpaSteps.
  • Glue code: the SpringJPASteps public API (constructor, step methods, autoclean, schemasToClean) is kept as @Deprecated(forRemoval = true) delegates.
  • Classpath: tzatziki-core stays the first dependency, so its Cucumber runner overrides (SkipStepException handling for if … => guards) still win over cucumber-core.

Validation

  • Module tests: tzatziki-db (26), tzatziki-jpa (15), tzatziki-spring-jpa (36), on PostgreSQL via Testcontainers.

Docs

  • ADR: docs/adr/001-jpa-module-decomposition.md
  • New READMEs for tzatziki-db and tzatziki-jpa; tzatziki-spring-jpa README updated
  • add-cucumber-tests skill: new steps-db.md and steps-jpa.md references (with their repomix configs); steps-spring-jpa.md now covers only the Spring Data additions; SKILL.md tells which references to read for each module

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
77.2% Coverage on New Code (required ≥ 80%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

alexandrepa and others added 8 commits October 15, 2026 14:54
Re-apply the Jackson 3 support introduced on main (#927) after rebase:
Jackson3PersistenceUtil now lives in tzatziki-jpa, shares the registered
@transient annotations with PersistenceUtil, and JpaSteps registers the
module matching the active mapper. Align test dependency versions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
alexandrepa and others added 13 commits October 15, 2026 15:04
The 'the X repository will contain only' step no longer truncated the
table and triggers were not disabled anymore. Restore both behaviours
(re-enabling triggers in a finally block) and cover them with scenarios.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Downstream glue code calling SpringJPASteps methods (repository helpers,
table/entity steps, autoclean flag) no longer compiled after the module
split. Re-expose them as @deprecated(forRemoval) delegates to
DatabaseSteps/JpaSteps/SpringJpaBackend, and propagate autoclean and
schemasToClean to both JpaSteps and DatabaseSteps.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The previous comparator compared the proxy class to the requested
interface, which never matched, so a sub-interface repository (e.g.
SuperUserDataSpringRepository for UserDataSpringRepository) could be
picked depending on bean ordering, making tests flaky.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
SpringJpaBackend.saveAll used a manually created EntityManager with a
RESOURCE_LOCAL transaction, bypassing Spring's transaction manager (and
failing with JTA) as well as any custom repository logic. Prefer the
entity's CrudRepository like the pre-split module did, falling back to
the EntityManager only for entities without repository.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
JpaBackend.queryAll serialized entities with NON_DEFAULT inclusion, so
false, 0 and null columns disappeared and could not be asserted with the
table steps. Serialize with the full JSON mapper instead.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Once a JPA backend was registered, table steps on tables that are not
mapped to an entity (join or audit tables) failed, although tzatziki-db
alone supports them. Delegate those operations to a JdbcBackend.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- Re-enable triggers in a finally block for every insertion step, so a
  failing insertion no longer leaves triggers disabled for later steps.
- Remove JpaSteps' duplicated autoclean hook and configuration fields:
  DatabaseSteps already cleans every registered DataSource and is now
  the single owner of autoclean/schemasToClean.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- DatabaseCleaner quotes schema/table names read from JDBC metadata, so
  mixed-case or special-character tables are truncated and get their
  triggers toggled instead of failing.
- JdbcBackend.resolveColumnTypes splits schema.table into schema and table
  patterns (defaulting to the connection schema) and drops the duplicated
  lower-case fallback block.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…iki-jpa

Drop the hard-coded hibernate-core 7.0.0.Final / jakarta.persistence-api
3.2.0 versions so the Spring Boot BOM manages them, and use provided scope
so consumers keep the JPA implementation version of their application.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
EntityGraphUtils and JpaQueryUtils only looked at the first expected row,
so associations asserted on later rows stayed uninitialized (null after the
EntityManager closed). The expected structure of all rows is now merged, and
lazy associations nested in collection elements are initialized as well.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
volatile/synchronized/CopyOnWriteArrayList did not make the shared static
state safe for parallel scenarios (the locking on the CopyOnWriteArrayList
was not even coordinated with its own mutations). Revert to plain
collections as on main and document it in the ADR.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The InsertSpec/SelectSpec/CountSpec/TruncateSpec records, their builders
and SqlRenderer only served four statements built in two classes, and the
TruncateSpec options were unused. Keep SqlIdentifier for validation and
quoting, and render the statements with four static methods.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@alexandrepa alexandrepa changed the title WIP Tzatziki-db and tzatziki-jpa new modules feat: split tzatziki-spring-jpa into tzatziki-db, tzatziki-jpa and tzatziki-spring-jpa Oct 15, 2026
alexandrepa and others added 6 commits October 15, 2026 16:03
…n 2 paths

- SqlIdentifier: possessive quantifiers in the identifier regex (S5998)
- JpaBackend: ignore merge return value explicitly, compare Spring Data @table by class (S2201, S1872)
- Tests for deprecated SpringJPASteps delegates, DatabaseCleaner exclusion filter / default overloads and the Jackson 2 PersistenceUtil module

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Spring's shared EntityManager proxy rejects getTransaction(), so entities only known through it could never be written.
JPA entities are resolved through their LocalContainerEntityManagerFactoryBean, others through their Spring Data repository.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ttributed to the module

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- Reduce cognitive complexity of SpringJpaBackend.initialize, EntityGraphUtils.mergeShapes and JpaQueryUtils.initializeNestedAssociations
- Roll back transactions in a finally block instead of catching Error
- Read fields through Fields instead of setAccessible, set the default backend from a static method, use \w in SqlIdentifier

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ASteps delegates

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…i-spring-jpa

- Keep cucumber-picocontainer and tzatziki-jackson in compile scope in tzatziki-db/tzatziki-jpa and exclude cucumber-picocontainer in tzatziki-spring-jpa
- Do not strip unasserted columns in JdbcBackend.queryAll and JpaBackend.queryAll so 'contains exactly' detects extra columns
- Initialize lazy associations in SpringJPASteps.the_repository_contains via findAllWithExpectedFields
- Fall back to @entity name / simple class name in JpaBackend.resolveTableName and avoid Map.entry NPE
- Add SubGraph support for collection elements in EntityGraphUtils.addAttributesToGraph
- Support DATE, TIME and TIMESTAMP/TIMESTAMPTZ conversion in JdbcBackend
- Reset backend in DatabaseSteps.clearDataSources and fail with a clear IllegalStateException when no backend is registered
- Fix false-positive trigger test, cover new cases, add module READMEs and regenerate steps-spring-jpa.md skill reference

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
alexandrepa and others added 3 commits October 16, 2026 09:35
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…re-split APIs

- Table-level steps accept column names and schema-qualified table names (EntityTableMapping)
- The table content variable contains entities again for mapped tables (DbBackend.contentOf)
- Warn once per table when falling back to JDBC
- Restore the pre-split SpringJPASteps constructor as deprecated
- Increase db/jpa test coverage
- Document Hibernate 6.2+ and PostgreSQL requirements

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…pa split

- tzatziki-db: drop Sql/SqlIdentifier, JdbcTransactions and the column type
  conversion (untyped Types.OTHER parameters), one INSERT per row, single
  DataSource registry
- tzatziki-jpa: concrete JpaBackend (no interface/PlainJpaBackend), @table based
  table resolution (no EntityTableMapping/column aliases), fetch graph only
- tzatziki-spring-jpa: SpringJpaBackend extends JpaBackend, saves through the
  repository only, reads through the shared EntityManager

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Transaction-related blocking and runtime failures in JDBC fixtures and table resolution remain unresolved.

2 open findings
What changed in this PR

Splits persistence testing into JDBC, standalone JPA, and Spring Data layers, allowing applications to use database steps without depending on Spring.

Changes:

  • Introduces tzatziki-db and tzatziki-jpa, with shared backend and configuration APIs.
  • Refactors Spring integration while retaining deprecated compatibility delegates.
  • Adds regression coverage, module documentation, and build integration.
File Description
tzatziki-spring-jpa/​src/​test/​resources/​db/​migration/​V2__non_entity_table.sql Adds an unmapped table fixture.
tzatziki-spring-jpa/​src/​test/​resources/​com/​decathlon/​tzatziki/​steps/​spring-jpa.feature Expands Spring integration scenarios.
tzatziki-spring-jpa/​src/​test/​java/​com/​decathlon/​tzatziki/​utils/​SpringJpaBackendTest.java Tests backend registration and transactions.
tzatziki-spring-jpa/​src/​test/​java/​com/​decathlon/​tzatziki/​steps/​RepositoryUsageSteps.java Verifies repository invocation counts.
tzatziki-spring-jpa/​src/​test/​java/​com/​decathlon/​tzatziki/​steps/​LegacyApiSteps.java Exercises compatibility delegates.
tzatziki-spring-jpa/​src/​test/​java/​com/​decathlon/​tzatziki/​app/​dao/​VisibilityDataSpringRepository.java Instruments fixture saves.
tzatziki-spring-jpa/​src/​main/​java/​com/​decathlon/​tzatziki/​utils/​SpringJpaBackend.java Adds the Spring Data backend.
tzatziki-spring-jpa/​src/​main/​java/​com/​decathlon/​tzatziki/​utils/​EntityGraphUtils.java Removes the Spring-local graph utility.
tzatziki-spring-jpa/​src/​main/​java/​com/​decathlon/​tzatziki/​steps/​SpringJPASteps.java Delegates shared persistence operations.
tzatziki-spring-jpa/​README.md Documents shared configuration and transactions.
tzatziki-spring-jpa/​pom.xml Depends on the extracted JPA module.
tzatziki-jpa/​src/​test/​resources/​META-INF/​persistence.xml Defines standalone persistence units.
tzatziki-jpa/​src/​test/​resources/​com/​decathlon/​tzatziki/​steps/​jpa.feature Covers standalone entity and table operations.
tzatziki-jpa/​src/​test/​java/​com/​decathlon/​tzatziki/​utils/​PersistenceUtilTest.java Tests serialization filtering.
tzatziki-jpa/​src/​test/​java/​com/​decathlon/​tzatziki/​steps/​JpaTestSteps.java Configures standalone JPA fixtures.
tzatziki-jpa/​src/​test/​java/​com/​decathlon/​tzatziki/​JpaTest.java Adds the JPA scenario runner.
tzatziki-jpa/​src/​test/​java/​com/​decathlon/​tzatziki/​app/​model/​Shipment.java Adds a schema-qualified entity fixture.
tzatziki-jpa/​src/​test/​java/​com/​decathlon/​tzatziki/​app/​model/​Item.java Adds a related entity fixture.
tzatziki-jpa/​src/​test/​java/​com/​decathlon/​tzatziki/​app/​model/​Category.java Adds a collection relationship fixture.
tzatziki-jpa/​src/​test/​java/​com/​decathlon/​tzatziki/​app/​model/​Basket.java Adds a many-to-many fixture.
tzatziki-jpa/​src/​main/​java/​com/​decathlon/​tzatziki/​utils/​PersistenceUtil.java Makes transient filtering extensible.
tzatziki-jpa/​src/​main/​java/​com/​decathlon/​tzatziki/​utils/​JpaBackend.java Implements entity operations and JDBC fallback.
tzatziki-jpa/​src/​main/​java/​com/​decathlon/​tzatziki/​utils/​Jackson3PersistenceUtil.java Uses shared transient annotation registration.
tzatziki-jpa/​src/​main/​java/​com/​decathlon/​tzatziki/​utils/​EntityGraphUtils.java Merges expected relationship shapes.
tzatziki-jpa/​src/​main/​java/​com/​decathlon/​tzatziki/​steps/​JpaSteps.java Provides Spring-free entity steps.
tzatziki-jpa/​README.md Documents standalone JPA usage.
tzatziki-jpa/​pom.xml Defines the JPA module dependencies.
tzatziki-db/​src/​test/​resources/​com/​decathlon/​tzatziki/​steps/​db.feature Covers JDBC database steps.
tzatziki-db/​src/​test/​java/​com/​decathlon/​tzatziki/​utils/​JdbcBackendTest.java Tests transactions, defaults, and value conversion.
tzatziki-db/​src/​test/​java/​com/​decathlon/​tzatziki/​steps/​DatabaseTestSteps.java Supplies database fixtures and assertions.
tzatziki-db/​src/​test/​java/​com/​decathlon/​tzatziki/​steps/​DatabaseStepsTest.java Tests registration and step configuration.
tzatziki-db/​src/​test/​java/​com/​decathlon/​tzatziki/​DatabaseTest.java Adds the database scenario runner.
tzatziki-db/​src/​main/​java/​com/​decathlon/​tzatziki/​utils/​JdbcBackend.java Implements raw JDBC table operations.
tzatziki-db/​src/​main/​java/​com/​decathlon/​tzatziki/​utils/​InsertionMode.java Moves shared insertion modes.
tzatziki-db/​src/​main/​java/​com/​decathlon/​tzatziki/​utils/​DbBackend.java Defines the table-operation contract.
tzatziki-db/​src/​main/​java/​com/​decathlon/​tzatziki/​utils/​DatabaseConfiguration.java Shares legacy and new configuration fields.
tzatziki-db/​src/​main/​java/​com/​decathlon/​tzatziki/​utils/​DatabaseCleaner.java Replaces Spring JDBC cleanup with plain JDBC.
tzatziki-db/​src/​main/​java/​com/​decathlon/​tzatziki/​steps/​DatabaseSteps.java Centralizes table steps and trigger management.
tzatziki-db/​README.md Documents JDBC setup and behavior.
tzatziki-db/​pom.xml Defines the JDBC module dependencies.
skills/​add-cucumber-tests/​repomix/​repomix.spring-jpa.json Includes extracted step definitions.
skills/​add-cucumber-tests/​references/​steps-spring-jpa.md Refreshes the generated persistence reference.
report/​pom.xml Includes both modules in reporting.
README.md Lists standalone database modules and Kafka.
pom.xml Registers the new reactor modules.
docs/​adr/​001-jpa-module-decomposition.md Records the layered design and compatibility decisions.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tzatziki-db/src/main/java/com/decathlon/tzatziki/utils/JdbcBackend.java Outdated
Comment thread tzatziki-jpa/src/main/java/com/decathlon/tzatziki/utils/JpaBackend.java Outdated
alexandrepa and others added 2 commits October 16, 2026 11:41
tzatziki-spring-jpa only reached tzatziki-core transitively (via tzatziki-jpa
and tzatziki-db), so tzatziki-spring brought cucumber-core first and its
TestStep shadowed tzatziki-core's override: conditional guards
(if ... =>) failed with SkipStepException instead of skipping the step.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- JdbcBackend inserts empty rows with DEFAULT VALUES
- JpaBackend treats names that are not loadable classes (e.g. AUDIT_LOGS) as
  non-entity tables instead of failing with ClassNotFoundException

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@alexandrepa alexandrepa changed the title feat: split tzatziki-spring-jpa into tzatziki-db, tzatziki-jpa and tzatziki-spring-jpa feat: extract tzatziki-db (JDBC) and tzatziki-jpa (plain JPA) from tzatziki-spring-jpa Oct 16, 2026
alexandrepa and others added 3 commits October 16, 2026 11:48
steps-spring-jpa now only covers the Spring Data additions, like
steps-spring-kafka builds on steps-kafka.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants