Mapping a composite key in JPA with @EmbeddedId
Why @Embedded objects?
JPA sits in the uncomfortable middle between managing relations stored in the database and providing object-oriented programming facilities. The general rule is that you annotate your classes with @Entity
and each class will correspond to a separate table, with while each field of the class will correspond to a column in that table.
The @Embedded
and @Embeddable
annotation exists to allow you to define two separate classes without using two separate tables in the database. You annotated a class with @Embeddable
instead of @Entity
, and then add a field of the type of the @Embeddable
class to another class annotated with @Entity
. Then the fields of the @Embeddable
class will be mapped to column in the table associated with the class annotated with @Entity
.
The reason that this annotation is not well-known is that it is much more frequent for two related classes to be backed by two separate tables with a foreign key relationship. In that case, the relevant JPA annotations are @ManyToOne
and @OneToMany
, as you will no doubt know.
@EmbeddedId and composite keys
@EmbeddedId
is also used in conjunction with @Embeddable
and works exactly the same way as @Embedded
, except that if you use @EmbeddedId
instead of @Embedded
, the columns corresponding to the fields of the class annotated with @Embeddable
will be interpreted by a JPA implementation as the composite key for that table.
JPA attempts to offer constructs which make sense both from an object oriented and relational perspective. The more perplexing parts of the JPA specification arise when the correspondence between the object oriented and the relational side breaks down. Using @EmbeddedId
just to indicate a composite key forces you to create a new class which is going to be completely useless from the object-oriented programming standpoint.
It can become even more confusing because there is another way to designate composite primary keys in JPA, using the @IdClass
annotation, which results in the fields corresponding to the composite primary key being defined twice: once on the main class and once on the class whose purpose is to hold the fields making up the composite key: but let’s not cover that approach in this post.
Hitting JPA’s limits
JPA does allow you to map almost any database design to Java types. In this regard, the specification can be considered a success. On the other hand, the resulting object system can be weird and filled with objects whose purpose is only to map a database table. An example of this is the way composite keys are declared with the @EmbeddedId
annotation.
I am often led to wonder whether it is not often preferable to avoid mapping a class to a table: one of the clear alternatives would be to concentrate on operating on SQL statements instead. The most famous library following this approach in Java is MyBatis; but many Scala libraries, e.g. ScalaQuery, Squeryl and Anorm (sorry about the lack of documentation on the last one), are headed in as imilar direction.