Jimmer ORM Documentation

repository·main·Indexed 23 days ago

https://github.com/babyfish-ct/jimmer

An advanced ORM for the JVM (Java and Kotlin) designed to handle complex, nested, and recursive data structures of arbitrary shapes. Jimmer features compile-time DTO generation, advanced SQL optimization (including CTE and Recursive-CTE support), and a DDL Compiler for generating dialect-specific schema SQL via KSP or APT.

Tokens
3.6K
Snippets
3
Records
13
Agent score
82%

What's inside Jimmer

  1. Key Features of Jimmer ORM

    main

    Jimmer provides a comprehensive suite of features for JVM developers (Java & Kotlin):

    Querying Capabilities

    • Flexible APIs: Provides a robust Java DSL and an elegant Kotlin DSL.
    • Advanced SQL Support: Supports Derived Tables, CTE (Common Table Expressions), and Recursive-CTE. You can mix native SQL expressions into the DSL to use database-specific features.
    • SQL Optimization: Automatically removes unused table joins, merges logically equivalent joins, and merges logically equivalent implicit subqueries. It also optimizes count queries for pagination.

    DTO Management

    Jimmer uses a compile-time code generator to make DTOs extremely efficient. It supports three main types:

    • Output DTO: Used for the return values of complex queries.
    • Input DTO: Used as parameters for complex save operations.
    • Specification DTO: Used as parameters for complex queries.

    Graph Operations

    • Reading: Supports querying any graph structure without the N + 1 problem. Objects at any level can be partial, and self-referential properties can be queried recursively.
    • Writing: Supports saving any graph structure using database upsert (merge) capabilities. Multiple objects at any level are handled via batch DML operations. It also automatically translates constraint violation exceptions.

    Performance and Caching

    • Multi-level Caching: Supports multiple cache levels where each level can use different technologies. It caches not just objects, but also associations, computed values, and multiple views.
    • Consistency: Automatically maintains cache consistency.

    Integration

    • Fast support for GraphQL.
    • Client contract generation based on doc comments (OpenAPI, TypeScript).
  2. Core Concept: Reading and Writing arbitrary graph structures

    main

    Jimmer's fundamental philosophy is treating arbitrary data structures as a single unit for read and write operations, rather than just managing individual entity objects.

    • Jimmer entities are NOT POJOs: They are designed to express arbitrary shapes of data structures.
    • Reading: Jimmer creates and provides you with an infinitely flexible data structure (the shape you requested).
    • Writing: You create an infinitely flexible data structure and pass it to Jimmer to be saved.

    This approach differs from other technologies:

    • vs GraphQL: While GraphQL focuses on querying arbitrary shapes, Jimmer also focuses on how to write them. Jimmer also supports recursive queries on self-referential properties, which GraphQL does not.
    • vs JPA: In JPA, the shape of saved data is fixed by configurations like insertable, updatable, or cascade. In Jimmer, you can construct and save partial or complex data structures without prior planning. Jimmer is also the only ORM that supports nested projections based on DTOs.
    • vs MongoDB: Unlike MongoDB where document structures are often fixed by design, Jimmer allows you to plan and use any data structure shape for any business scenario on the fly.
  3. Understand the core concept of Jimmer

    main

    Jimmer's fundamental design philosophy is to read and write data structures of arbitrary shapes as a whole, rather than processing individual entity objects.

    Key distinctions include:

    • Jimmer entities are not POJOs: They are designed to express complex, infinitely flexible data structures.
    • Reading: Jimmer creates flexible data structures (including nested associations and computed values) and passes them to you.
    • Writing: You can construct a data structure of any shape and pass it to Jimmer to save. This allows for saving incomplete objects (only the fields you provide are updated) and handling complex hierarchical relationships without prior design of fixed DTOs.
    • Comparison with JPA: Unlike JPA, where saving requires configuring insertable, updatable, or cascade properties, Jimmer allows the data structure shape to be ever-changing. Jimmer also supports recursive queries on self-referencing properties and nested projections based on DTOs, which JPA's EntityGraph does not support easily.
  4. Understand the Jimmer DDL Snapshot model

    main

    The DDL compiler uses a snapshot model to maintain a durable schema baseline. This baseline is stored as a directory of per-table lockfiles under .jimmer-ddl/entity-table-snapshot/.

    Key Characteristics

    • Per-table lockfiles: Each file records one table schema hash, its encoded structural model, and the Jimmer entities mapped to that table. This allows multiple developers to work on different entities without causing Git merge conflicts in a single aggregate file.
    • Staging process: The compiler stages the next baseline under build/generated/jimmer-ddl/main/resources/.jimmer-ddl/entity-table-snapshot/.
    • Applying changes: After accepting a generated migration, you must manually mirror the contents of the staged directory to your durable .jimmer-ddl/entity-table-snapshot/ directory. This includes deleting lockfiles for tables that have been removed.

    Source Fingerprints

    If jimmerDdl.sourceFingerprint is provided, it is written to build/jimmer-ddl/source-fingerprint.properties. This is disposable build state and must not be committed to Git or copied into the structural snapshot.

  5. Handle destructive changes in DDL generation

    main

    By default, jimmerDdl.allowDestructiveChanges is set to false. This prevents the generator from emitting SQL that drops schema objects or renames tables.

    When false:

    • Removed columns remain in the staged structural snapshot so they can be dropped by a later explicitly enabled run.
    • Inferred table renames create the new table and preserve the old one, emitting a warning instead of performing a rename.

    WARNING: Setting jimmerDdl.allowDestructiveChanges=true permits generated migrations to drop schema objects and rename tables, which can cause irreversible data loss. Review the generated SQL and back up the database before applying it. Jimmer does not guarantee data preservation for destructive statements.

  6. Handling code generation with Apt/Ksp

    main

    Jimmer is a compile-time framework that relies on apt (Annotation Processing Tool) or ksp (Kotlin Symbol Processing).

    Standard Workflow

    For most changes (modifying Java/Kotlin code, entity types, or Web Controllers), you do not need a full manual compilation. Simply clicking the IDE's Run or Debug button will trigger the pre-compilation behaviors, and the automatically generated source code/resources will update automatically.

    Modifying DTOs only

    If you only modify DTO files without changing other Java/Kotlin source code in the project, the IDE might not trigger a full update. In this case, use one of these three methods:

    1. Use the companion DTO plugin.
    2. Perform a full compilation using maven or gradle commands, or use the IDE's Rebuild button.
    3. Delete the compilation output directory and then click the IDE's Run or Debug button.
  7. Profile Jimmer benchmarks with IDEA profiler

    main

    To inspect hot spots using an IDE profiler (like IntelliJ IDEA), you must run the benchmark without forking so the profiler can attach to the application JVM.

    1. Open benchmark-internal as a Gradle project.
    2. Run BenchmarkApplication.main directly.
    3. Use the following program arguments (setting fork count to 0): 20 5 5 0 one-shot
    4. Set the VM options to -Xms2g -Xmx2g.

    Note: This mode is intended for inspecting hot spots via profiling, not for publishing official benchmark numbers. For official measurements, use the forked gradle run command.

    20 5 5 0 one-shot
  8. Install Jimmer DDL Compiler via Gradle

    main

    Jimmer DDL Compiler is a compile-time DDL generator that converts Jimmer entity metadata into dialect-specific schema SQL. It can be used as a Kotlin KSP processor or a Java APT processor.

    Kotlin/KSP Setup

    Add the jimmer-ddl-compiler dependency using the ksp configuration and configure the required arguments in the ksp block.

    Java/APT Setup

    Add the jimmer-ddl-compiler dependency using the annotationProcessor configuration and pass the configuration via compilerArgs in the JavaCompile task.

    ### Kotlin/KSP
    ```kotlin
    dependencies {
        ksp("org.babyfish.jimmer:jimmer-ddl-compiler:<jimmer-version>")
    }
    
    ksp {
        arg("jimmerDdl.enabled", "true")
        arg("jimmerDdl.databaseType", "postgresql")
        arg("jimmerDdl.outputFormat", "flyway")
        arg("jimmerDdl.outputDir", "$projectDir/build/generated/jimmer-ddl/main/resources/db/migration")
        arg("jimmerDdl.version", "1001")
        arg("jimmerDdl.description", "jimmer_auto_ddl_generated")
    }

    Java/APT

    dependencies {
        annotationProcessor("org.babyfish.jimmer:jimmer-ddl-compiler:<jimmer-version>")
    }
    
    tasks.withType<JavaCompile>().configureEach {
        options.compilerArgs.add("-AjimmerDdl.enabled=true")
        options.compilerArgs.add("-AjimmerDdl.databaseType=postgresql")
    }
  9. Run Jimmer internal benchmarks

    main

    Jimmer's internal benchmarks measure runtime hot paths (SQL rendering, parameter binding, DefaultExecutor, and entity materialization) using a fixed-row JDBC stub to exclude database/driver costs.

    To run the standard 20-thread benchmark in a forked JVM, use the following command:

    gradle run --args="20 5 5 1 one-shot"

    Argument Format

    The arguments passed to --args follow this order:

    1. Thread count: Number of threads to use.
    2. Warmup iterations: Number of warmup cycles.
    3. Measurement iterations: Number of measurement cycles.
    4. Fork count: Number of JVM forks.
    5. Benchmark mode (optional): The specific path to measure.

    Benchmark Modes

    • one-shot (default): Covers the full production path from query construction through result materialization.
    • retained: Measures the narrower retained-query path.
    • materialization: Compares entity materialization against equivalent handwritten JDBC mapping, independent of query preparation. This mode reads batches of 1, 100, 1000, and 10000 rows. Results are normalized per row using @OperationsPerInvocation.
  10. Handling Code Generation in Jimmer

    main

    Jimmer is a compile-time framework that relies on Apt (for Java) or Ksp (for Kotlin). While Java IDEs support these technologies, you should be aware of how to trigger code generation in different scenarios:

    Standard Scenario

    Most changes involve modifying Java or Kotlin code (e.g., changing an entity type or a Web Controller). In these cases, simply clicking the Run or Debug button in your IDE will trigger the necessary pre-compilation, and the generated source files/resources will update automatically.

    DTO-only Changes

    If you modify only a DTO file without changing any other Java or Kotlin source code in the same project, the IDE might not trigger generation automatically. You have three options:

    1. Use the dedicated DTO plugin.
    2. Perform a full compilation using Maven or Gradle commands, or use the IDE's Rebuild button.
    3. Delete the build output directory of the affected project and then click the IDE's Run or Debug button.
  11. Configure Jimmer DDL Compiler options

    main

    The DDL compiler is configured using jimmerDdl.* arguments. Below are the available configuration options:

    OptionDefaultDescription
    jimmerDdl.enabledtrueEnables or disables generation.
    jimmerDdl.profilesemptyComma-separated profile names. Each profile can override options through jimmerDdl.profile.<name>.<option>.
    jimmerDdl.databaseTypeautoDialect code such as postgresql, mysql, h2, sqlite, sqlserver, oracle, dm, kingbase, or taos. auto resolves from JDBC URL when available.
    jimmerDdl.outputFormatflywayflyway writes V<version>__<description>.sql; plain writes <description>.sql.
    jimmerDdl.outputDirbuild/generated/jimmer-ddl/main/resources/db/migrationOutput directory for generated SQL.
    jimmerDdl.version1001Flyway version prefix.
    jimmerDdl.descriptionjimmer_auto_ddl_generatedOutput file description.
    jimmerDdl.includePackagesemptyOptional comma-separated package allow-list.
    jimmerDdl.excludePackagesemptyOptional comma-separated package deny-list.
    jimmerDdl.includeForeignKeystrueGenerates foreign key statements when the dialect supports them.
    jimmerDdl.includeIndexestrueGenerates index statements.
    jimmerDdl.includeCommentstrueGenerates comments.
    jimmerDdl.includeSequencestrueGenerates sequences.
    jimmerDdl.includeManyToManyTablestrueGenerates Jimmer many-to-many junction tables.
    jimmerDdl.compareDatabasetrueReads the configured database and emits diff SQL. If the database cannot be read, it falls back to offline DDL.
    jimmerDdl.allowDestructiveChangesfalseAllows destructive diff operations and inferred table renames. A column rename is emitted as an added new column plus a dropped old column.
    jimmerDdl.nullabilityRepairOnlyfalseLimits risky offline alteration planning to nullability repair.
    jimmerDdl.sourceFingerprintemptyOptional source fingerprint stored as build-local state outside the structural snapshot.
    jimmerDdl.jdbcUrl / jimmerDdl.jdbcUsername / jimmerDdl.jdbcPassword / jimmerDdl.jdbcSchema / jimmerDdl.jdbcDriveremptyExplicit JDBC settings used by database comparison.
    jimmerDdl.springResourcePathemptyOptional Spring resource path used to discover datasource settings.
    jimmerDdl.springProfilelocalSpring profile used when reading YAML datasource settings.