Here's my understanding of how NHibernate (and therefore probably Hibernate) does it. It has 4 caches:
- row cache: this caches DB rows. The cache key is TableName#id, the other entries are the row values.
- query cache: this caches the results returned for a particular query. The cache key is the query with parameters, the data is a list of the TableName#id row keys that were returned as query results.
- collections cache: this caches the child objects of any given parent (which NHibernate allows to be lazy-loaded.) So if you access myCompany.Employees, the employees collection will be cached in the collections cache. The cache key is CollectionName#entityId, the data is a list of the TableName#id row keys for the child rows.
- table update cache: a list of each table and when it was last updated. If a table was updated after the data was cached, the data is considered stale.
This is a pretty flexible solution, is very efficient space-wise, and guarantees that the data won't be stale. The disadvantage is that a single query can require several round-trips to the cache, which can be a problem if the cache server is on the network.