I mentioned performance monitoring tools, and EXPLAINs, but there's another option when you're dealing with Hibernate JPA and that's to turn on logging.
Hibernate logs can tell you a great deal about how Hibernate is dealing with your JPA code - including what actual SQL statements your JPA queries are being rendered as, how caching is being applied to statements and data, and much more. I spent WAY more time than I wanted to learning about the mayhem that can result from working with a database that supports fixed-length space-padded keys (DB2) and JDBC drivers that sometimes, but not always trim trailing spaces. For the record, I don't recommend fixed-length keys in databases where the key could be space-padded, but this particular product was ported from an IBM mainframe and fixed-length fields were once more efficient there. One of the reasons why you shouldn't optimize for implementation details before you actually observe a problem.
One thing you'll see from Hibernate JPA traces is that some decidedly non-intuitive things go on in there and they have a terminology all their own. Some of which you'll likely only see in Hibernate, and not, say, in OpenJPA. Which, again, is why premature low-level optimization is something we caution against. I once had to migrate a project from OpenJPA to Hibernate JPA because the OpenJPA implementation of the day wasn't supporting a critical feature for my app. Thanks to the miracle of
Maven and the fact that JPA is a
JEE standard, it only took me 15 minutes. If I'd loaded my app with Hibernate-specific optimizations, it would have been much harder.
Higher level optimization is much safer. As I said in the beginning, there's a cost for joins that's almost inevitable. In some cases, it may be useful to not actually define your JPA entities with the joins explicitly built-in, although this frequently can come back and bite you. Tuning lazy-fetch/eager-fetch can be very important, especially if you're like me and detach the entities before turning them over to the business logic. As a rule, the less data you transfer to/from your app to the DB server the happier the app will be and the less work you make the DB server do on its end, the happier the DBA will be. These goals are not mutually exclusive if your requests are well-designed. You'll also make the network admin happier if you minimize the amount of data going from DB server to app. Making the DBA happy mostly means using tools like EXPLAIN to pare down complex requests so that the non-applicable data gets removed as soon as possible in the request process. There's even a place for stored procedures sometimes, and I've seen too much grief come from stored procedures to recommend them casually.