Architecture¶
LarkBatis splits its work into two distinct stages: a compile-time build phase and a lightweight runtime phase. Everything in the build phase is discarded once javac finishes; only clean, standard Java bytecode is packaged into your application.
flowchart LR
subgraph build["Build time (annotation processing)"]
A["Mapper interface<br/>@Select / @Insert"] --> F
B["Mapper XML<br/><select> <if> <foreach>"] --> F
F["Frontend<br/>Parse, Type-check, Constant-fold"] --> IR["MapperModel<br/>(Intermediate Representation)"]
IR --> E["Emitters<br/>JavaPoet"]
end
subgraph run["Runtime (~1,500 lines, zero reflection)"]
E --> G1["UserMapper$$Impl"]
E --> G2["UserRow"]
E --> G3["LarkBatisMappers"]
E --> G4["LarkBatisMapperConfiguration"]
G1 --> RT["larkbatis-runtime<br/>LarkBatisSession, JdbcCodec, SqlFragment"]
RT --> JDBC[("JDBC Driver")]
end
Repository & Module Structure¶
The project is divided across four repositories based on lifecycle boundaries:
| Repository | Modules | Role |
|---|---|---|
larkbatis |
larkbatis-annotations |
Compile-time annotations only (no runtime logic) |
larkbatis-runtime |
Core runtime (zero dependencies beyond standard JDBC) | |
larkbatis-processor |
Build-only annotation processor (javac) |
|
larkbatis-gradle-plugin |
Build-only Gradle plugin (io.github.larkbatis) |
|
larkbatis-maven-plugin |
Build-only Maven plugin | |
larkbatis-spring |
larkbatis-spring, -autoconfigure, -starter |
Spring transaction integration and Spring Boot auto-configuration |
Key architectural rule: Build-only modules (larkbatis-processor, Gradle/Maven plugins) are never included on an application's runtime classpath.
The Build Phase¶
Frontend Parsing¶
LarkBatis provides two frontends that feed into the same intermediate model:
- Annotation frontend: Analyzes Java mapper interfaces using standard
javax.annotation.processing.ProcessorAPIs. - XML frontend: Parses XML mapper files from directories provided by the build plugin.
Both frontends perform static analysis:
- Convert
#{}parameter references into typed positional JDBC bind parameters. - Validate parameter names against method signatures and object property paths at compile time.
- Parse
SELECTcolumn lists to hardcode indexed column reading. - Select typed
JdbcCodecread and write helpers based on declared Java types. - Compile
<if test="...">conditions into plain Java boolean expressions. - Constant-fold
<where>,<set>, and<trim>clauses into guarded string appends. - Inline static
<sql>/<include>fragments. - Compile
<foreach>loops into placeholder generator and parameter binding loops.
If any query shape or binding cannot be resolved at compile time, javac fails immediately with a clear error pointing to the method or XML line.
Intermediate Representation (IR)¶
MapperModel acts as the compiler boundary. It models statements, parameters, result column mappings, dynamic AST nodes, and generated key configurations. Golden snapshot tests verify the IR directly, ensuring frontend parsing changes are caught before bytecode emission.
Code Emitters¶
LarkBatis uses JavaPoet to emit clean Java source files:
| Emitter | Emitted Class | Description |
|---|---|---|
MapperImplEmitter |
UserMapper$$Impl |
Concrete mapper implementation executing JDBC calls |
RowReaderEmitter |
UserRow |
Static row reader for result mapping |
RegistryEmitter |
LarkBatisMappers |
Central mapper factory registry |
SpringConfigurationEmitter |
LarkBatisMapperConfiguration |
Spring @Configuration with @Bean definitions |
The Runtime Phase¶
larkbatis-runtime is intentionally tiny (~1,500 lines) and contains only essential abstractions:
LarkBatisSession: Acquires and releases database connections and translates JDBC exceptions.JdbcLarkBatisSession: Standalone session implementation managingLarkBatisTxtransactions.SpringLarkBatisSession: Spring integration delegating toDataSourceUtilsand Spring exception translators.JdbcCodec: Static, inlined read/write helpers with null handling for primitives, dates, and enums.SqlFragment: Safe wrapper for dynamic SQL text.LarkBatisSql: Utility helpers for query variant tracking and batch update calculations.RowReader<T>&StatementBinder: Functional interfaces used by the dynamic SQL escape hatch.LarkBatisException: Root unchecked exception hierarchy.
Why Build Plugins Are Used¶
Annotation processors cannot discover mapper XML files located in arbitrary directory structures via standard Filer APIs. Gradle and Maven plugins register mapper XML directories as compilation inputs and pass them cleanly to javac via -Alarkbatis.mapperDir. All code generation remains strictly inside javac. See Build Plugins.
Verification & Testing¶
LarkBatis employs a three-tier test suite to guarantee correctness:
- Emitter Specifications: Reference implementations written by hand to define expected generated code patterns.
- Golden Snapshots: Emitted code across a broad test suite is committed and diffed on changes.
- Differential Test Suite: Executes mappers against both standard MyBatis (runtime interpreter) and LarkBatis (generated code) using a recording JDBC
DataSource, verifying that generated SQL queries, parameter binds, and result mappings match identically. - Compile-Fail Test Suite: Validates that invalid XML tags, unsupported OGNL expressions, and unsafe
${}splices produce expected compile-time errors.