Spring Data AOT Repository - Part 2
# Spring Data AOT Repositories — GA Release Overview
Concluding the [Road to GA](https://spring.io/blog/2025/09/02/road_to_ga_introduction) blog post series, let’s explore the **benefits** and **capabilities** of **Spring Data Ahead-of-Time (AOT) Repositories**.
---
## Background
In **May 2025**, Ahead-of-Time repositories were first introduced as a preview for:
- **JPA**
- **MongoDB**
via the [3rd Milestone of the next Spring Data generation](https://spring.io/blog/2025/05/16/spring-data-2025-1-0-M3-released).
These repositories use **AOT processing** to implement repository query methods with **actual source code**, leveraging each store’s specific nature.
Since then:
- **Issues resolved**
- **Community feedback integrated**
- Support **expanded to Apache Cassandra and JDBC**
---
## Supported Modules (Spring Data 2025.1 GA)
With the [2025.1.0 release](https://spring.io/blog/2025/11/14/spring-data-2025-1-goes-ga), AOT-generated repositories are available for:
- [Apache Cassandra](https://spring.io/projects/spring-data-cassandra)
- [JDBC](https://spring.io/projects/spring-data-jdbc)
- [JPA](https://spring.io/projects/spring-data-jpa)
- [MongoDB](https://spring.io/projects/spring-data-mongodb)
---
## Why It Matters
AOT Repositories improve **performance** and **startup times**, and open possibilities for:
- Automated code/content generation
- Integration workflows
- AI-assisted publishing (example: [AiToEarn官网](https://aitoearn.ai/) — multi-platform AI publishing & monetization)
---
## How Spring AOT Helps
Spring’s [AOT infrastructure](https://docs.spring.io/spring-framework/reference/7.0/core/aot.html):
- Provides **deep configuration insight**
- **Captures bean definitions**, types, and properties before runtime
- Enables correct query generation per database dialect (e.g., **Spring Data JDBC**)
**Example:** Accessing `JdbcDialect` during build generates optimized SQL for that database.
Generated code stays **store-specific**:
- `findByLastnameStartingWith` differs across **JDBC**, **JPA**, and **Apache Cassandra**.
---
### Example Implementations
#### Spring Data JDBC
public List findByLastnameStartingWith(String lastname) {
Criteria criteria = Criteria.where("lastname").like(escape(lastname) + "%");
StatementFactory.SelectionBuilder builder =
getStatementFactory().select(User.class).filter(criteria);
}
Extended snippet:
RowMapper rowMapper = getRowMapperFactory().create(User.class);
List result = (List) builder.executeWith((sql, paramSource) ->
getJdbcOperations().query(sql, paramSource, new RowMapperResultSetExtractor<>(rowMapper))
);
return (List) convertMany(result, User.class);
#### Spring Data JPA
public List findByLastnameStartingWith(String lastname) {
String queryString = "SELECT u FROM example.springdata.aot.User u WHERE u.lastname LIKE :lastname ESCAPE '\\'";
Query query = entityManager.createQuery(queryString);
query.setParameter("lastname", "%s%%".formatted(lastname));
return (List) query.getResultList();
}
#### Spring Data Apache Cassandra
public List findByLastnameStartingWith(String lastname) {
Query query = Query.query(Criteria.where("lastname").like(lastname + "%"));
ExecutableSelectOperation.TerminatingSelect select =
operations.query(User.class).matching(query);
return select.all();
}
---
## Enabling / Disabling AOT Repositories
**Enable AOT mode (required):**
spring.aot.enabled=true
Or run as **GraalVM Native Image**.
**Disable globally:**
spring.aot.repositories.enabled=false
**Disable per module:**
spring.aot.jdbc.repositories.enabled=false
---
## Debuggability
Benefits:
- Transparent **query execution insight**
- Ability to **set breakpoints** in generated code
- Valuable for diagnosing performance or logic issues
---
## Repository Metadata (JSON)
When in AOT mode, Spring Data generates a **JSON metadata file** (per repository) in the interface’s package — e.g., `UserRepository.json`.
**Contains:**
- Method names & signatures
- Target store module
- Actual queries
### Example — JDBC
{
"name": "example.springdata.UserRepository",
"module": "JDBC",
"type": "IMPERATIVE",
"methods": [
{
"name": "findByLastnameStartingWith",
"signature": "public abstract List UserRepository.findByLastnameStartingWith(String)",
"query": {
"query": "SELECT 'USER'.'ID' AS 'ID', 'USER'.'LAST_NAME' AS 'LAST_NAME', 'USER'.'FIRST_NAME' AS 'FIRST_NAME' FROM 'USER' WHERE 'USER'.'LASTNAME' LIKE :name"
}
}
]
}
**Disable metadata generation:**
spring.aot.repositories.metadata.enabled=false
---
## Practical Use Cases
- Increase **query awareness** during development
- Feed metadata into **automated documentation systems**
- Enhance tools for **debugging** & **query optimization**
---
## AOT Cache & Project Leyden
Advantages:
- JVM detects **pre-generated** repositories during [AOT Cache](https://openjdk.org/jeps/483) training
- Reduced startup time — classes load from **shared objects files**
Example log output:
[info][class,load] example.springdata.aot.UserRepositoryImpl__AotRepository source: shared objects file
Includes **bytecode generation** for:
- **Entity instantiation**
- **Property accessors**
[info][class,load] example.springdata.User__Instantiator_cu2hga source: shared objects file
[info][class,load] example.springdata.User__Accessor_cu2hga source: shared objects file
---
## Potential Downsides
- **Build time** increases due to analysis & JVM training
- **Dynamic flexibility reduced**
- Dialect-specific SQL fixed at build time
- Only **imperative** repositories supported (no reactive support yet)
---
## Try It Out
- Build & run with `spring.aot.enabled=true`
- Explore prepared demos in [Spring Data Examples](https://github.com/spring-projects/spring-data-examples)
- Fallback: run without the AOT flag to disable all AOT enhancements
---
## Conclusion
AOT repositories bring:
- **Faster startups**
- **Lower memory usage**
- **Transparent and debuggable code**
- Better **alignment with modern JVM optimizations**
**We welcome your feedback** on how you use AOT features.
---