What does getReferenceById return, and how does it differ from findById?
answer
- getReferenceById = lazy proxy, no SELECT yet
- findById = SELECT now, Optional
- EntityNotFoundException on lazy access (maybe post-tx)
- great for setting FK without loading parent
- replaces deprecated getOne/getById
basics
~10 sgetReferenceById returns a lazy proxy without querying the database; it only loads when you access a real property. findById runs a SELECT immediately and returns an Optional with the fully loaded entity (or empty).
solid answer
~50 sgetReferenceById(id) delegates to EntityManager.getReference(): it returns a lazy proxy that holds only the id and does not hit the database yet. The SELECT fires lazily the first time you touch a non-id property, and if no row exists you get an EntityNotFoundException — thrown at access time, possibly outside the repository call, even after the transaction if accessed lazily. It replaces the deprecated getOne/getById. findById(id) issues the SELECT immediately and returns Optional<T> (empty if absent), giving you a fully initialised entity and a clean not-found signal. Use getReferenceById as an optimisation when you only need the entity to set a foreign key on an association — you avoid a needless SELECT because Hibernate just needs the id for the FK column. Use findById whenever you actually need the entity's state or want an immediate, safe existence check.
code
java · 12 lines// Optimisation: attach FK without loading the parent row
@Transactional
public Order place(long customerId, Cart cart) {
Order order = new Order(cart);
// no SELECT on customer; proxy carries only the id
order.setCustomer(customerRepository.getReferenceById(customerId));
return orderRepository.save(order); // INSERT ... customer_id = ?
}
// Safe path when you actually need the data
Customer c = customerRepository.findById(customerId)
.orElseThrow(() -> new CustomerNotFoundException(customerId));go deeper
Know findById returns Optional after a SELECT; getReferenceById returns a lazy proxy.
Explain the FK-optimisation use case and the deferred EntityNotFoundException.
Discuss proxy semantics, LazyInitializationException after session close, and equality/unproxy pitfalls.
Weigh the SELECT-avoidance optimisation against error-handling clarity and set a team convention for when each is acceptable.
**The two methods.** - `findById(ID id)` → `Optional<T>`. Internally `EntityManager.find(...)`. Executes a `SELECT` right away. Returns `Optional.empty()` if the row doesn't exist — a safe, explicit not-found signal. The returned entity is fully managed and its (eager) state is populated. - `getReferenceById(ID id)` → `T`. Internally `EntityManager.getReference(...)`. Returns a **lazy proxy** (a Hibernate `HibernateProxy` / bytecode-enhanced stand-in) that carries only the identifier. **No SQL is executed** at this point. The database is queried lazily the first time you call a method that needs real state (any getter other than the id getter). `getById` and `getOne` are earlier names for the same thing and are deprecated in favour of `getReferenceById`. **Why a lazy reference is useful.** The classic case is setting an association without loading the parent. If you have an `Order` and want to attach it to an existing `Customer` with id 42, Hibernate only needs the customer's id to write the FK column `customer_id = 42`. Loading the whole `Customer` row would be wasted work: ```java Order order = new Order(); order.setCustomer(customerRepository.getReferenceById(42L)); // no SELECT orderRepository.save(order); // INSERT order with customer_id = 42 ``` **The big gotcha: EntityNotFoundException timing.** Because the proxy doesn't verify existence up front, passing a bogus id succeeds silently. The failure surfaces only when the proxy is *dereferenced* (a real property accessed) — and that can be: - inside your service (catchable), or - much later, e.g. during Jackson serialization in the web layer, or - **after the transaction/session has closed**, in which case you may instead get a `LazyInitializationException` rather than a clean `EntityNotFoundException`. This makes error handling harder than with `findById`, which fails fast and cleanly. **Other differences and notes.** - `findById` returning `Optional` forces you to handle absence explicitly; `getReferenceById` returns a non-null proxy, hiding absence until later. - If the entity is already in the persistence context (first-level cache), `getReferenceById` may return the managed instance directly and no proxy is involved. - Equality/`instanceof` checks can behave oddly with proxies; use `Hibernate.unproxy(...)` or access via the id when comparing types. - For a simple existence check prefer `existsById(id)` (a lightweight `SELECT count/1`) over loading via either method. **When to use which.** - **getReferenceById:** you only need the id to establish a relationship / FK, and you are confident the id is valid (e.g., it came from your own DB). Optimises away a SELECT. - **findById:** you need the entity's actual data, or you want an immediate, safe not-found result. This is the default, safe choice.
- You call getReferenceById with an id that doesn't exist. When and how do you find out?Not immediately. The proxy is returned fine; when you first access a real property, Hibernate tries to load it and throws EntityNotFoundException — possibly outside the repository call, or a LazyInitializationException if the session is already closed.
- What replaced getOne(), and why?getReferenceById() (getById was an interim rename). getOne was deprecated for clarity — the name didn't convey its lazy-reference semantics, which surprised developers expecting an immediate load.
saying these in an interview costs you the question
- Saying getReferenceById runs a SELECT immediately like findById
- Assuming it throws right away for a missing id
- Using getReferenceById then reading the entity's fields after the transaction has closed