Why Return to SQL in the Age of Vibe Coding?
0. Introduction
While reviewing AI-generated JPA code, I repeatedly found myself asking: when does this query actually execute?
JPA was productive when people wrote the code themselves. But when reviewing code AI generates without sufficient context, implicit behavior raises verification costs.
This article explores why Exposed can be a reasonable choice from three perspectives: I/O predictability, memory usage, and the type system.
1. I/O predictability
Much latency comes from I/O. CPU bottlenecks are less common than we might assume.
1.1. JPA lazy loading
Lazy loading is convenient: follow an entity graph and queries happen automatically. That convenience assumes the developer has the relationships clearly in mind.
AI may lack that context. Iterating over List<Order> and calling order.getMember().getName() looks natural. At runtime, though, it can trigger hundreds of database round trips: N+1.
Problems:
- Accumulated RTT: even a fast database has nonzero network cost, increasing latency.

- Connection-pool exhaustion: in an OSIV setup, business logic can run while a connection remains borrowed. Under load, CPU may sit idle while the pool is exhausted.
Fetch Join, BatchSize, and EntityGraph can address this. But deciding where to apply them requires context, and finding implicit costs in AI-written code is tiring.
1.2. Explicit joins in Exposed
The Exposed DSL doesn't use proxy-based implicit loading. Fetching related data requires explicitly writing the join.
// Exposed: I/O costs are visible in the code
(Orders innerJoin Members)
.slice(Orders.id, Members.name)
.select { Orders.date greaterEq today }
.map { ... }
Visible costs: code gets longer, but
innerJoinvisibly signals a join query.Review efficiency: no need to hunt through YAML or entity annotations. A reviewer can identify excessive joins directly in the query code.
2. Memory usage

How data is loaded into memory affects server resource consumption.
2.1. JPA's persistence context
JPA provides a first-level cache that makes working with the database feel like manipulating Java collections. That comes at a cost.
Snapshot overhead: dirty checking retains snapshots on the heap when loading managed data. A 1 GB load can occupy more than 2 GB, contributing to
OutOfMemoryErroror frequent full GC in large batches.Flush costs: at transaction completion, current state is compared field by field with snapshots of managed objects. Larger datasets increase this O(N) comparison work and CPU usage.
2.2. Exposed's DSL approach
Exposed also offers DAO APIs and an EntityCache. Its DSL approach avoids that entity-management overhead.
Direct mapping: results from
Table.select { ... }map from JDBCResultSetinto DTOs or data classes, without entity snapshots or dirty checking.Predictable resources: memory follows the retrieved data more directly, making resource use easier to reason about and control in cloud environments.
3. The type system
Finding AI-generated mistakes at runtime is late. Catching them at compile time is preferable.
3.1. JPA's runtime checks
With JPA, including JPQL and QueryDSL, valid code syntax isn't enough. Entity state and configuration must also be correct.
- Suppose AI accesses a
FetchType.LAZYassociation outside a transaction. - Compilation succeeds. The build succeeds.
- Production traffic then triggers a
LazyInitializationException.
Preventing that requires mentally simulating JPA's lifecycle during review.
3.2. Compile-time checks in Exposed
Exposed uses Kotlin's type system when constructing SQL.
Structural constraints: a join comparing
UserTable.name(String) withTeamTable.id(Long) can produce a compile error because the types differ.A first filter: compilation checks the DSL's column references and type relationships, letting reviewers spend less attention on query construction and more on business logic.
4. Trade-off
Exposed isn't the answer for every situation.
Dynamic queries: the JPA ecosystem has QueryDSL. Complex dynamic queries in Exposed can require custom extension functions or DSL wrappers to stay readable.
Table-centered thinking: JPA supports object-oriented domain modeling. Exposed centers database tables. Complex domains with substantial entity collaboration may suit JPA better.
Compared with jOOQ: jOOQ is another type-safe SQL option. Licensing considerations and Java-oriented code-generation setup can add friction; Exposed can be lighter in a Kotlin environment.
AI training-data bias: LLMs are familiar with the widely used JPA ecosystem. Exposed has less training material, so requests for unusual window functions or GroupConcat syntax can produce invented DSL APIs. Providing syntax guidance initially has a cost.
5. Conclusion
JPA has hidden complexity behind useful abstractions. When AI generates the code, some of that hidden complexity becomes a review risk.
Exposed brings more of it to the surface. Query execution and I/O costs are more visible in code, and the compiler provides an initial layer of validation.
If you're working with AI, Exposed is worth considering.