Lew said:
[ SNIP ]
I know from experience in large-scale projects that treating your entity
layer like a data layer can cause huge inefficiencies to emerge. If you
use entities as part of the object model, Hibernate (OpenJPA, TopLink)
are usually pretty good about caching and only hitting the database when
they really need to.
Although on the subject of caching and other things which are outside
JPA, the usual caveat applies - read the documentation very carefully.
Make sure the documentation is for exactly the software version that you
have. Check with technical forums and blogs. *And* confirm that things
that you expect to work actually do.
As an example, an application that we are currently fixing for a client
uses Toplink Essentials. Although the medium-term plan is to move to
EclipseLink, right now we have to improve things with TLE. We have found
that (and I recognize that many of these are reasonably well-known):
1) JPA javax.persistence.Query setFirstResult() and setMaxResults()
work...sort of. In TLE the full set of results is still returned from
the database, and the Toplink implementation classes underlying
javax.persistence.Query use JDBC methods like absolute() to filter what
they build into the persistence context. So don't necessarily plan on
using these JPA methods for an efficient paging strategy;
2a) Caching does not work on queries. Period. As opposed to EM.find(),
which checks the cache first, in TLE the use of queries (all queries)
will always result in a hit to the database first. There are some
efficiencies insofar as when results come back, if an instance of an
entity is discovered in the cache that will be used instead, as opposed
to being rebuilt, but that's it.
You can get around this to some extent by using the native TLE API, and
obviously if using EclipseLink there are cache hints that allow you to
both cache static queries (same SQL, same bind parameters) and also
returned result sets;
2b) If you _have_ gotten down into Toplink Essentials API land (which is
not unlikely if you are performance tuning), be cognizant of what query
types cooperate with the cache and which do not.
3) Don't forget about database caching (e.g. Oracle RAM data buffers).
As a developer (or even tester) who uses certain known test data, you
may delude yourself into thinking that your DB access code is pretty
zippy. But for a real user whose data is varying, it's not.
4) Lazy fetching: IOW, LAZY fetch _hints_ (and I stress the word hints)
on 1:1 and M:1 relationships. TLE/Toplink/EclipseLink use ASM to modify
the entity class bytecode to enable lazy instantiation of these
relationships. At least with TLE, check your entity classes (if working
in a container) to see if weaving actually kicked in. You may be surprised.
5) Serialization of EntityManager implementation (e.g. for clustering):
for TLE/Toplink 10g, and I think for Toplink 11/EclipseLink also, this
just won't work. Similarly, extended persistence contexts will only work
on the same JVM. The recommended way of implementing EM serialization
using TLE/TL/EL is to write a wrapper that stores necessary entity
information for a persistence context. Hibernate, I believe, does
support serialization because Hibernate Sessions are Serializable, IIRC.
This is just an example of ORM-specific issues. There are many more.
As another example of performance mismatches between your entity layer
and your business logic/user interface, that can be caused by a
simplistic use of JPA (so ORM-agnostic), analyze your queries if you're
using full entities in them. Especially if you're using joins in your
JPQL. If there are no compelling performance reasons to change from full
object notation in your JPQL, leave it alone. Full objects are cleaner.
But occasionally you'll find that a full object JPQL query is generating
dozens or hundreds (or worse) SELECT statements where strictly speaking
you ought to be able to get all your data with *one*. This may well be
the result of entity relationships that you cannot lightly change, and
it can happen even without joins. If the performance hit is significant,
one can always consider JPQL constructor expressions. These are brutally
effective and do give you a sensible Java business object - not just a
row wrapper - to use. You also avoid using native SQL.
AHS