JPA Pitfalls / Mistakes

From my experience, both in helping teams and conducting training, here are some pitfalls/mistakes I have encountered that caused some problems in Java-based systems that use JPA.

Requiring a public no-arg constructor

Always using bi-directional associations/relationships

Using @OneToMany for collections that can become huge

Requiring a Public No-arg Constructor

Yes, a JPA @Entity requires a zero-arguments (or default no-args) constructor. But this can be made protected. You do not have to make it public. This allows better object-oriented modeling, since you are not forced to have a publicly accessible zero-arguments constructor.

The entity class must have a no-arg constructor. The entity class may have other constructors as well. The no-arg constructor must be public or protected. [emphasis mine]

If the entity being modeled has some fields that need to be initialized when it is created, this should be done through its constructor.

NOTE: Some JPA providers may overcome a missing no-arg constructor by adding one at build time.

Let's say we're modeling a hotel room reservation system. In it, we probably have entities like room, reservation, etc. The reservation entity will likely require start and end dates, since it would not make much sense to create one without the period of stay. Having the start and end dates included as arguments in the reservation's constructor would allow for a better model. Keeping a protected zero-arguments constructor would make JPA happy.

NOTE: Hibernate (a JPA provider) allows the zero-arguments constructor to be made private. This makes your JPA code non-portable to other JPA providers.

It also helps to add a comment in the zero-arguments constructor to indicate that it was added for JPA-purposes (technical infrastructure), and that it is not required by the domain (business rules/logic).

Although I could not find it mentioned in the JPA 2.1 spec, embeddable classes also require a default (no-args) constructor. And just like entities, the required no-args constructor can be made protected.

It's good to know that bi-directional associations are supported in JPA. But in practice, it becomes a maintenance nightmare. If order items do not have to know its parent order object, a uni-directional association would suffice (as shown below). The ORM just needs to know how to name the foreign key column in the many-side table. This is provided by adding the @JoinColumn annotation on the one-side of the association.

Making it uni-directional makes it easier since the OrderItem no longer needs to keep a reference to the Order entity.

Note that there may be times when a bi-directional association is needed. In practice, this is quite rare.

Here's another example. Let's say you have several entities that refer to a country entity (e.g. person's place of birth, postal address, etc.). Obviously, these entities would reference the country entity. But would country have to reference all those different entities? Most likely, not.

With accounts that have only a few transactions, there doesn't seem to be any problem. But over time, when an account contains thousands (if not millions) of transactions, you'll most likely experience out-of-memory errors. So, what's a better way to map this?

If you cannot ensure the maximum number of elements in the many-side of the association can all be loaded in memory, better use the @ManyToOne on the opposite side of the association.