JPA does have some gotchas and the learning curve is a little bit steep (not too steep, but just a wee bit).
You do need to know how JPA works internally a little bit (e.g.: EntityManager actually cache your objects unless you tell it specifically to "flush").
1) CriteriaBuilder API is the worst API I've ever seen. I've never seen an API that turns a simple task into more lines of code than this monstrosity. I can't imagine many people use this thing, instead they use JPQL losing type safety in the process.
2) JPQL is still wildly verbose and very difficult to intertwine with native SQL. As an example try to generate something like "SELECT * FROM `order` o ORDER BY RAND() LIMIT 5". That little call to RAND() makes generating this from JPA a huge pain.
3) Manual session life cycle management is terrible. It shouldn't be needed. This leads to all sorts of terrible hacks to get around it, ConversationScopes(CDI in general), opening transactions in the JSF RENDER_RESPONSE phase just so lazy loading works, etc, etc. Having used JavaEE stuff for awhile now I'm convinced a large portion of the cruft exists to support this model of session management. All the additional scopes and requisite CDI support seem to have all been created as workaround to just deal with session lifecycle.
>CriteriaBuilder API is the worst API I've ever seen.
It's not great, but it's type safe. Having said that, I do C# so I just use Linq but before Linq I was using CriteriaBuilder. It wasn't so bad because I was only using it in specific repository objects, not all over the code.
You do need to know how JPA works internally a little bit (e.g.: EntityManager actually cache your objects unless you tell it specifically to "flush").