What does @Transactional(readOnly = true) do in Spring?
answer
- Hint, not a lock
- FlushMode.MANUAL
- Connection.setReadOnly(true)
- Skips dirty-check + flush
- Optimization + replica routing
basics
~20 sIt marks a transaction as read-only: a hint that the method only reads data. Spring passes this to Hibernate and the JDBC driver so they can optimize, for example by skipping dirty-checking and the automatic flush.
solid answer
~40 s@Transactional(readOnly = true) declares that a transaction won't modify data. It is a hint, not a lock. Spring propagates the flag two ways: it may call Connection.setReadOnly(true) on the JDBC connection (drivers can optimize or route to a replica), and for Hibernate/JPA it sets the Session FlushMode to MANUAL, so Hibernate skips automatic dirty-checking and the flush before queries and at commit. The payoffs are less CPU spent snapshotting and comparing entities, plus a clear signal for read/write replica routing. Crucially it does NOT forbid writes at the JPA level: an explicit flush or native SQL can still persist changes. Use it on service methods that only query, especially those loading large object graphs.
code
java · 16 lines@Service
public class ReportService {
private final OrderRepository orders;
public ReportService(OrderRepository orders) {
this.orders = orders;
}
@Transactional(readOnly = true)
public List<OrderView> monthlyReport(int year, int month) {
// Only queries run here; Hibernate uses FlushMode.MANUAL,
// so no dirty-checking flush occurs at commit.
return orders.findByPeriod(year, month);
}
}go deeper
Know it is a hint for read-only work that lets Hibernate/JDBC optimize.
Know the two mechanisms: Connection.setReadOnly and Hibernate FlushMode.MANUAL.
Can explain dirty-checking avoidance and that it is not enforcement.
Frames it within replica routing, connection pools, and where real enforcement must live.
## What it is `@Transactional` is Spring's declarative transaction annotation. Its `readOnly` attribute (default `false`) declares that the annotated method performs only reads. `@Transactional(readOnly = true)` is a *hint* to the transaction infrastructure that no data will be modified. ## What actually happens under the hood When Spring's `PlatformTransactionManager` starts the transaction it reads `TransactionDefinition.isReadOnly()` and does two independent things: 1. **JDBC level** — the transaction manager may call `java.sql.Connection.setReadOnly(true)`. This is itself only a hint to the driver; some drivers optimize, some route the connection to a read replica, many ignore it. It usually does not throw on writes. 2. **Hibernate/JPA level** — when using `JpaTransactionManager` with Hibernate, Spring sets the Hibernate `Session`'s flush mode to `FlushMode.MANUAL`. That means Hibernate will not auto-flush pending changes before running queries, and will not flush at commit. Because no flush happens, entity modifications are never written (and Hibernate can also skip taking the dirty-checking snapshot, saving memory and CPU). ## Why it is an optimization In a normal read-write session Hibernate keeps a *loaded snapshot* of every managed entity and, at flush time, compares each field to detect changes (dirty checking) so it can emit UPDATE statements. In a read-only transaction there is nothing to write, so this bookkeeping is pure overhead. `readOnly = true` eliminates the flush and lets Hibernate avoid that work. ## What it is NOT It is not enforcement. It does not make the method reject writes at the ORM layer. It does not put the database into a read-only mode by itself. It is advisory. ## When to use it Put `readOnly = true` on service methods (or query-only repository facades) that only fetch data — reports, list endpoints, lookups. A common pattern is `@Transactional(readOnly = true)` on the class and override with `@Transactional` on the few write methods.
- Is readOnly=true enforced — will Spring stop you from saving an entity?No. At the JPA level it just sets FlushMode.MANUAL so auto-flush is skipped; it doesn't reject writes. An explicit session.flush(), a native/JPQL bulk update, or JDBC still writes.
- Name one concrete performance benefit.Hibernate skips the automatic flush and the dirty-checking snapshot/compare of managed entities, cutting CPU and memory for read-heavy transactions.
saying these in an interview costs you the question
- Saying readOnly=true throws an exception if you try to write
- Claiming it locks the rows or the table
- Thinking it is a database-level read-only mode Spring enforces