There are many ways to optimize association fetching, depending on the business case and the amount of data required:
1. Enable the second-level cache and hope that associated instances will mostly be found in the cache. There are situations when a cache can't (and shouldn't) be used, so you can tune this on a per-class and per-association basis.
2. Lazy load and Eager load associated entities. The first means that an associated entity is only loaded when access for the first time, the latter that a "prefetch" is executed. An eager fetch query tries to retrieve as much data as possible (or sensible) in a single query. There are several options how both strategies can be used as default (for graph navigation) and at runtime in HQL/Criteria queries.
3. Optimizations using batch fetching (not to be confused with
JDBC batch operations) for "similar" collections and entities.
4. Some more minor optimization techniques.