1. Good Parts

First we will look at all the good things JPA offers to us. Why did I start to prefer JPA over a particular provider? Mind that this part is just an extended enumeration of various features, not a tutorial material.

The good parts are not absolute though. If we compare the JPA as a standard with a concrete ORM we’ll get the standardization, but there are related drawbacks. Comparing JPA with SQL is not quite appropriate as JPA covers much bigger stack. We may compare JPA with JDBC (read on the good parts), or we may compare Java Persistence Query Language (JPQL) with SQL to a degree.

JPA is standard

When we started with ORM, we picked Hibernate. There were not many other ORM options for Java, or at least they were not both high-profile and free. Yet even Hibernate creators are now glad when their tool is used via JPA interfaces:

Standard is not always necessarily good, but in case of JPA it is well established and respected. Now we can choose between multiple JPA 2.1 implementations or just deploy to any Java EE 7 application server. I’d not bet on the promise that we can easily swap one provider (or application server) for the other – my experiences are terrible in this aspect – but there is a common set of annotations for mapping our entities and the same core classes like EntityManager.

But standards also have their drawbacks, most typically that they are some common subset of the features offered in that area. JPA’s success is probably based on the fact that it emerged as a rock-solid subset of the features offered by major players in the Java ORM world. But starting this way it must have trailed behind them. JPA 1 did not offer any criteria API, can you imagine that? Now JPA 2.1 is somewhere else. The features cover nearly anything we need for typical applications talking to relational databases.

Even if something is missing we may combine it with proprietary annotations of a particular ORM provider – but only to a necessary degree. And perhaps we can switch those to JPA in its next version. In any case, JPA providers allow us to spice up JPA soup with their specifics easily. That way JPA does not restrain us.

Database independence

Let’s face it – it’s not possible to easily switch from one database to another when we have invested in one – especially if we use stored procedures or anything else beyond simple plain SQL. But JPA is not the problem here, on the contrary. It will require much less (if any) changes and the application will work on.

One of the best examples of this is pagination. There is a standard way how to do it since SQL:2008 using FETCH FIRST clause (along with OFFSET and ORDER BY) but only newer database versions support this. Before that the pagination required anything from LIMIT/OFFSET combo (e.g. PostgreSQL) to outer select (for Oracle before 12c). JPA makes this problem go away as it uses specific dialect to generate the SQL. This portion of the database idiosyncrasies is really something we can live without and JPA delivers.

Hence, if we start another project and go for a different database our application programmers using JPA probably don’t have to learn specifics of its SQL. This cuts both ways though. I’ve heard that “you don’t have to learn SQL” (meaning “at all”) is considered a benefit of the JPA, but every time programmers didn’t understand SQL they didn’t produce good results.

Natural SQL-Java type mapping

JPA can persist anything that is so called persistable type. These are:

  • entity classes (annotated with @Entity) – these loosely matches to database tables;
  • mapped superclasses (@MappedSuperclass) – used when we map class hierarchies;
  • embeddable classes (@Embeddable) – used to group multiple columns into a value object;

These were all types on the class level, more or less matching whole database tables or at least groups of columns. Now we will move to the field level:

  • simple Java types (optionally annotated with @Basic) like primitive types, their wrappers, String, BigInteger and BigDecimal;
  • temporal types like java.util.Date/Calendar in addition to types from java.sql package;
  • collections – these might be relations to other entities or embeddable types;
  • enum types – here we can choose whether it’s persisted by its name() or by ordinal().

Concepts in the first list are not covered by the JDBC at all – these are the most prominent features of ORM. But we can compare JDBC and JPA in how easy it is to get the value for any column as our preferred type. Here JDBC locks us to low-level types without any good conversion capabilities, not even for omnipresent types like java.util.Date. In JPA we can declare field as java.util.Date and just say that the column represents time, date or timestamp using @Temporal annotation. I feel no need to use java.sql package anymore.

Also enums are much easier, although this applies only for simple cases, not to mention that for evolving enums @Enumerated(EnumType.ORDINAL) is not an option (it should not be an option at all, actually). More in the chapter Mapping Java enum.

The bottom line is that mapping from SQL types to Java field types is much more natural with JPA than with JDBC. And we haven’t even mentioned two big guns JPA offers – custom type converters and large object support.

Convenient type conversion

Up to JPA 2.0 we could not define custom conversions, but JPA 2.1 changed this. Now we can simply annotate our field with @Convert and implement AttributeConverter<X, Y> with two very obvious methods:

  • Y convertToDatabaseColumn (X attribute)
  • X convertToEntityAttribute (Y dbData)

And we’re done! Of course, don’t forget to add annotation @Converter on the converter itself, just like we annotate other classes with @Entity or @Embeddable. Even better, we can declare converter as @Converter(autoApply = true) and we can do without @Convert on the field. This is extremely handy for java.time types from Java 8, because JPA 2.1 does not support those (it was released before Java SE 8, remember).

Large objects

Compared with JDBC, the JPA makes working with large objects (like SQL CLOB and BLOB types) a breeze. Just annotate the field with @Lob and use proper type, that is byte[] for BLOBs and String for CLOBs. We may also use Byte[] (why would we do that?) or Serializable for BLOBs and char[] or Character[] for CLOBs.

In theory we may annotate the @Lob field as @Basic(fetch=FetchType.LAZY) however this is a mere hint to the JPA provider and we can bet it will not be heard out. More about lazy on basic and to-one fields in a dedicated section.

Getting LOBs via JDBC from some databases may not be such a big deal, but if you’ve ever looped over LOB content using 4 KB buffer you will appreciate straightforward JPA mapping.

Flexible mapping

It better be flexible because it is the most important reason for the whole ORM concept. Field-to-column mapping can be specified as annotations on the fields or on the getters – or in XML files. Annotation placement implies access strategy, but it can be overridden using @Access on the class. In the book I’ll use field access mostly, but property access can be handy when we want to do something in get/setters. For instance, before JPA 2.1 we did simple conversions there, field was already converted and we used additional get/setters annotated as @Transient leaving those JPA related accessors only for JPA purposes.

Mentioning @Transient – sometimes we want additional fields in our JPA entities and this clearly tells JPA not to touch them. There are arguments how much or little should be in JPA entity, I personally prefer rather less than more – this on the other hand is criticized as anemic domain model – and we will return to this topic shortly later.

Unit of work

Unit of work is a pattern described in [PoEAA]. In Hibernate it was represented by its Session, in JPA more descriptive (and arguably more EE-ish) name EntityManager was chosen. To make things just a little tougher on newcomers, entity manager manages something called persistence context – but we make no big mistake if we treat entity manager and persistence context as synonyms. Nowadays EntityManager is typically injected where needed, and combined with declarative transactions it is very natural to use it. Funny enough, it’s not injected with standard @Inject but with @PersistenceContext.

To explain the pattern simply: Whatever we read or delete through it or whatever new we persist with it will be “flushed” at the end of a transaction and relevant SQL queries will get executed. We get all of this from EntityManager and while there is some ideal way how to use it, it is flexible enough and can be “bent”. We can flush things earlier if we need to force some order of SQL statements, etc.

Because unit of work remembers all the work related to it sometimes it is called “session cache” or “1st level cache”. This “cache” however does not survive the unit of work itself and talking about cache gives this pattern additional – and confusing – meaning.

After all the years of experience with Session and EntityManager it was actually really refreshing to read about the unit of work pattern, to see what operations are typical for it, to have it explicitly stated and also to read Fowler’s speculation how it will be used and implemented – all written back in 2002, and most of it still valid!

Declarative transactions

JPA has basic transaction support, but when it comes to declarative transactions they are part of Java Transaction API (JTA). JPA and JTA are seamlessly integrated from programmer’s point of view that we simply take @Transactional support for granted.

Programmatically we can get to the transaction using EntityManager’s getTransaction() method. This returns EntityTransaction implementation that allows us to check whether a transaction is active or not, begin one, commit or rollback. This is all good for simple cases while using transaction-type set to RESOURCE_LOCAL in our persistence.xml – typical for SE deployment. But in Java EE world (or when using Spring, even outside of application server) we’ll probably use @Transactional annotation for declarative transactional management, possibly to join a distributed transaction if necessary.

We will not cover transaction management, there are better resources and plenty of blogs. I can recommend [ProJPA2] which covers both @Transactional and @TransactionalAttribute annotations, and much more. A bit of a warning for Spring users. We can use their @Transactional annotation, this way we can even mark transaction as read-only when needed, but we cannot mix both @Transactional annotations at will as they work differently. It is advisable to use only Spring’s annotation in Spring environment.

Other JPA 2.1 goodies

We talked about custom converters already, but JPA 2.1 brought much more. Let’s cover the most interesting points quickly:

  • As 2.0 brought Criteria, 2.1 extended their usage for updates/deletes as well (although I’d use Querydsl for all these cases).
  • We can now call stored procedures in our queries.
  • We can map results into constructors and create custom DTO objects for specific queries (again, Querydsl has it too and it works for older JPAs).
  • Feature called entity graph allows us to define what relations to load in specific situations and then to fetch relations marked as lazy when you know up-front you will need them. I’m not covering this in the book.
  • There’s also an option to call any custom FUNCTION from JPQL. This means also native functions of a particular database if needed. While we limit ourselves to that vendor (or we need to rewrite it) it allows us to perform duties beyond and above JPA’s built-in functions.
  • JPA 2.1 also specifies properties that allow us to generate database schema or run SQL scripts, which unifies this configuration for various JPA providers.

For the whole list see either this part of Java Persistence wikibook or this post covering the features even better.