Source of truth
If the current context specifies a local path to the Jimmer project's source code, the source code implementation should be used as the baseline. If in other cases you need to consult Jimmer's specific implementation, its code repository is https://github.com/babyfish-ct/jimmer
Project conventions (XiaomiAlbumSyncer)
- The server side is Solon + Kotlin + Jimmer, not Spring; service classes use the
@Managedannotation, dependencies are injected via the constructor or@Injectinjection - Entities:
server/src/main/kotlin/com/coooolfan/xiaomialbumsyncer/model/ - DTO:
server/src/main/dto/<Entity>.dto, KSP generates at...model.dto - The query entry point is the injected
KSqlClient, within the project it is customary to usesql.executeQuery(Entity::class) { ... }/sql.executeDelete/sql.saveCommand(input, SaveMode.X), rather thancreateQuery(...).execute() - When a Controller returns an entity, use the
Fetcher<T>constant in the companion +@FetchByfixed projection, the Service accepts thefetcher: Fetcher<T>parameter - After modifying entities/DTOs, you need to run
cd server && ./gradlew clean compileKotlinto verify that the KSP-generated code compiles - The database table structure is managed by Flyway migrations (migrations under
server/src/main/resources), adding new fields requires synchronously adding a migration script
Core concepts
Entity definition characteristics
- Use interface to define entities, rather than class
- Entities aredynamic objectsandimmutable objects
- Only getters, no setters
- Implementation is generated at compile time by KSP (Kotlin) or AnnotationProcessor (Java)
- Property not set ≠ property is null
DSL query characteristics
- Strongly typed SQL DSL, providing compile-time safety guarantees
- Dynamic predicates: decide whether to add SQL conditions based on parameters
- Dynamic table joins: unused joins are automatically ignored, conflicting joins are automatically merged
- Implicit subqueries: create subqueries based on collection associations, automatically generating parent-child association conditions
Language selection
Jimmer provides a specialized API style for Kotlin
- Entity mapping: refer to
references/entity-mapping.md - DSL query: refer to
references/dsl-queries.md - Entity template: refer to
assets/EntityTemplate.kt
Key annotations quick reference
@Entity- marks an entity type@MappedSuperclass- marks a mapped superinterface, reuses common fields@Embeddable- marks an embedded value object@Id- marks the primary key@GeneratedValue- primary key generation strategy@Key- business key@ManyToOne/@OneToMany/@ManyToMany/@OneToOne- association mapping@ManyToManyView- exposes a many-to-many view through an intermediate entity@IdView- association id view@JoinColumn- custom foreign key column name@JoinTable- custom intermediate table@Column- custom column name (only for non-association fields)@Table- custom table name@Version- optimistic lock version field@LogicalDeleted- logical delete field@Formula/@Transient- computed property?(Kotlin) - marks a nullable property
Naming conventions
- Entity name → table name:
BookStore→BOOK_STORE - Property name → column name:
firstName→FIRST_NAME - Many-to-one foreign key:
store→STORE_ID
Workflow
-
When defining an entity:
- Use interface rather than class
- Refer to the entity templates and mapping documents corresponding to the project language
- When the intermediate table has business fields such as sorting, time, and source, define an intermediate entity and use
@ManyToManyViewto expose a read-only many-to-many view - When you need to query directly by foreign key or write to input, prioritize adding to the association
@IdView - Refer to
assets/EntityTemplate.ktandreferences/entity-mapping.md - Refer to
references/dto.mdto complete the DTO definition
-
When writing queries:
- Prefer using dynamic predicates (Kotlin:
`ilike?`/`between?`/`valueIn?`) - Use dynamic table joins to handle single-value association queries
- Use implicit subqueries to handle collection associations, avoiding duplicate results and broken pagination caused by collection JOINs
- When the API returns entities, use fetcher/DTO to control the projection; do not rely on the default serialization shape
- Refer to the DSL query documents corresponding to the project language
- Refer to
references/dsl-queries.md
- Prefer using dynamic predicates (Kotlin:
-
When encountering problems:
- Check whether the entity is defined using an interface
- Check the association mapping's
mappedByconfiguration - Check whether the dynamic predicate parameters are correct
- Confirm whether the table join path is correct