Spring Data Relational

repository·main·Indexed 21 days ago

https://github.com/spring-projects/spring-data-relational

A module within the Spring Data family providing a simplified, opinionated approach to relational data access. It offers both blocking (JDBC) and non-blocking (R2DBC) repository support for SQL databases. Designed as a limited ORM, it intentionally excludes complex JPA features such as caching, lazy loading, and write-behind. Key capabilities include CRUD operations for Aggregates, custom queries via @Query, transparent auditing, persistence events, and MyBatis integration for JDBC users.

Tokens
29.9K
Snippets
61
Records
127
Agent score
74%

What's inside Spring Data Relational

  1. Overview of Spring Data R2DBC

    main

    Spring Data R2DBC applies Domain-Driven Design (DDD) principles to R2DBC database drivers. It provides high-level abstractions for storing and querying aggregates, primarily through a template approach or via Repository interfaces.

    Key features include:

    • Configuration Support: Java-based @Configuration classes for R2DBC driver instances.
    • R2dbcEntityTemplate: A central class for entity-bound operations, providing integrated object mapping between database rows and POJOs for common CRUD operations.
    • Object Mapping: Feature-rich mapping integrated with Spring's Conversion Service, supporting both annotation-based metadata and extensible formats.
    • Repository Support: Automatic implementation of Repository interfaces, including support for custom query methods.

    Recommendation: For most tasks, use R2dbcEntityTemplate for ad-hoc CRUD operations or the Repository support for standard data access patterns.

  2. Introduction to Spring Data JDBC and R2DBC

    main

    Spring Data JDBC and R2DBC provide repository support for the Java Database Connectivity (JDBC) and Reactive Relational Database Connectivity (R2DBC) APIs. They offer a consistent programming model for accessing SQL data sources, easing the development of data-driven applications.

    Key areas of focus include:

    • JDBC: Object mapping and repository support for synchronous JDBC-based applications.
    • R2DBC: Object mapping and repository support for reactive, non-blocking R2DBC-based applications.
    • Kotlin: Specific support and idiomatic features for Kotlin developers.
  3. What is Spring Data Relational?

    main

    Spring Data Relational is a module within the Spring Data family designed to simplify implementing repositories for SQL databases. It provides enhanced support for both blocking JDBC and non-blocking R2DBC data access layers.

    Unlike JPA, Spring Data Relational is designed to be a simple, limited, and opinionated ORM. To maintain conceptual simplicity, it does not offer features like caching, lazy loading, or write-behind.

  4. Kotlin extensions for Spring Data R2DBC

    main

    Spring Data R2DBC offers specific extensions for Kotlin developers to improve ergonomics:

    • Reified generics support: Available for DatabaseClient and Criteria to reduce boilerplate.
    • Coroutines support: Extensions for DatabaseClient are provided to enable asynchronous programming using Kotlin Coroutines (see kotlin/coroutines.adoc for details).
  5. How MyBatis statement naming conventions work

    main

    When a SqlSessionFactory is present in the application context, Spring Data JDBC checks if a matching MyBatis statement exists for each operation. If found, Spring Data uses that statement and its mapping instead of its default implementation.

    Statements are identified by a specific naming pattern: {FullyQualifiedEntityName}Mapper.{StatementKind}

    For example, to intercept an insertion for an entity of type org.example.User, you must define a statement named org.example.UserMapper.insert in your MyBatis configuration.

  6. How AOT Repositories work

    main

    AOT Repositories are an optimization that moves query method processing from runtime to build-time. Instead of analyzing query methods reflectively at application startup, Spring Data pre-generates implementation fragments for eligible methods.

    Key Details:

    • Implementation: The generated fragment follows the naming scheme <Repository FQCN>Impl__Aot and is placed in the same package as the repository interface.
    • Usage Warning: AOT repository classes are an internal optimization. Do not use them directly in your code, as their generation and implementation details may change.
    • Performance: This optimization significantly improves startup performance by reducing reflective analysis.
  7. How convention-based mapping works in Spring Data R2DBC

    main

    If you do not provide explicit mapping metadata (like annotations), MappingR2dbcConverter uses a set of default conventions:

    • Naming: The short Java class name is mapped to the table name, and field names are mapped to column names using a specific strategy (e.g., com.bigbank.SavingsAccount maps to SAVINGS_ACCOUNT, and firstName maps to FIRST_NAME). You can customize this with a NamingStrategy.
    • Identifiers: By default, derived table and column names are used with database-specific quotes (making them case-sensitive). To use plain (unquoted) identifiers, use R2dbcMappingContext.forPlainIdentifiers(...) or set R2dbcMappingContext.setForceQuote(false).
    • Nested Objects: Arbitrarily nested objects are not supported by convention. Use the @Embedded annotation for value objects that should map to columns in the same table.
    • Object Creation: The converter prefers a single non-zero-argument constructor whose argument names match the top-level column names. If no such constructor exists, it falls back to a zero-argument constructor. If multiple non-zero-argument constructors exist, an exception is thrown.
    • Fields vs Properties: The converter uses object fields directly; it does not use public JavaBean properties.
  8. Understand Domain-Driven Design (DDD) concepts in Spring Data JDBC

    main

    Spring Data JDBC is designed around Domain-Driven Design (DDD) principles, specifically the concepts of Aggregates, Aggregate Roots, and Repositories. Understanding these is critical because Spring Data JDBC's persistence model differs from traditional relational database patterns.

    Key Concepts

    • Aggregate: A cluster of domain objects (entities) that are treated as a single unit for data changes. An aggregate guarantees consistency for all its internal properties during atomic operations. For example, an Order and its OrderItems form an aggregate where the Order ensures the total count of items is always consistent with the actual items present.
    • Aggregate Root: Every aggregate has exactly one root entity. This is the only entity through which the aggregate can be manipulated. All atomic changes to the aggregate must occur via methods on this root.
    • Repository: An abstraction representing a collection of aggregates. In Spring Data JDBC, you should maintain one Repository per Aggregate Root.

    Relationship Rules

    1. Internal Consistency: Entities reachable from an aggregate root are considered part of that aggregate.
    2. Foreign Keys: Spring Data JDBC assumes that only the aggregate root holds a foreign key to tables storing non-root entities within the same aggregate. No other entity should point toward these non-root entities.
    3. Cross-Aggregate References: References between different aggregates are not guaranteed to be consistent immediately; they are eventually consistent.
    WARNING

    In the current implementation, Spring Data JDBC handles updates to entities referenced from an aggregate root by deleting and recreating them.

  9. Use differential mode to derive schema changes

    main

    In differential mode, the LiquibaseChangeSetWriter compares an existing Liquibase Database against your mapped entities to derive the necessary create or drop operations.

    By default, the writer is conservative: it will not drop any tables or columns unless you explicitly configure filters to allow it. This prevents accidental data loss during development.

  10. How back references (foreign keys) are named

    main

    All references within an aggregate result in a foreign key relationship in the database. By default, the foreign key column name is the table name of the referencing entity.

    • Composite IDs: If the referenced ID is an @Embedded id, the back reference uses multiple columns named <table-name>_<column-name> (e.g., PERSON_FIRST_NAME).
    • Customizing Naming:
      • You can use DefaultNamingStrategy.setForeignKeyNaming(ForeignKeyNaming.IGNORE_RENAMING) to use the entity name instead of the table name.
      • For List and Map, an additional column is required for the index or key, named using the foreign key column name plus a _KEY suffix.
      • For full control, implement NamingStrategy.getReverseColumnName(RelationalPersistentEntity<?> owner).
    • Annotation Override: For List and Map, you can use @MappedCollection(idColumn="your_column_name", keyColumn="your_key_column_name") to specify column names explicitly.