Java Persistence API (JPA, for short) is a Java EE Specification for Object Relational Mapping (ORM, for short). It can be used in both Java SE and Java EE environments.
JPA, on its own, is a specification. It cannot run on its own. The API comes with interfaces and requires an implementation. There are various JPA providers who have implemented the JPA specification, such as Hibernate, EclipseLink, Apache OpenJPA, to name but a few. All Java EE complaint containers have a JPA provider already bundled so you, as a developer, must not worry on which JPA provider to use. The container will provide you with one. For the latest specification (currently at version 2.2 at this time of writing), you can visit JCP.org.
Now that we have an introduction out the way, and before I talk about my subject at hand, here is the number ONE fundamental concept of JPA that you must remember: Treat JPA as object modelling and object reference. I will explain later on why I say this.
Pre-requirements: To understand this post, you must have an intermediate knowledge of Java (Java annotations will be used here) and basic usage of JPA.
As you can see, we have 2 tables that references 2 other tables.
Now, in our example mentioned above, instead of creating an auto increment column as our table row primary key, we can combine the 2 foreign keys together to create a unique primary and guarantee uniqueness. The combination of columns of our foreign keys constitutes as a composite primary key in our RDBMS.
1) Using
One such approach is to use the
For example, using the
For
For
Things to remember when using
Object retrieval is as simple as:
If your entity primary key are not auto sequence field (accompanied with the
2) Using the
Using the example above:
For
Things to consider:
We, however have a scenario. Suppose that
How do we resolve such problem?
One approach will be to individually persist each entity in an order of parent/child relationship, like in this example:
As you can see, we persist each entity one step at a time. A
So, modifying the code above we get:
For
The
Enter
The
Notice this additional requirements to attributes that have the
The
In essence, with all the relationships declared and composite ID mapped, we can essentially allow our JPA provider to manage all our entity (new or already managed) prior to committing the SQL statements back to the RDBMS.
Finally, we can resolve our example by creating a new book and book title at the same time like so:
And voila!. ORM done right. The code is small and very simple to follow and read.
Happy coding. Please feel free to comment below about anything regarding this post. I am looking forward to your input/insight. :-)
JPA, on its own, is a specification. It cannot run on its own. The API comes with interfaces and requires an implementation. There are various JPA providers who have implemented the JPA specification, such as Hibernate, EclipseLink, Apache OpenJPA, to name but a few. All Java EE complaint containers have a JPA provider already bundled so you, as a developer, must not worry on which JPA provider to use. The container will provide you with one. For the latest specification (currently at version 2.2 at this time of writing), you can visit JCP.org.
Now that we have an introduction out the way, and before I talk about my subject at hand, here is the number ONE fundamental concept of JPA that you must remember: Treat JPA as object modelling and object reference. I will explain later on why I say this.
Pre-requirements: To understand this post, you must have an intermediate knowledge of Java (Java annotations will be used here) and basic usage of JPA.
Before we begin, a scenario.
You are tasked to create an online book store, a simplified version. This book store must list all the authors who took part in writing the book, the book in their various languages as well as the price of the book. Basically, you will have something like this:As you can see, we have 2 tables that references 2 other tables.
BookTitle
table has a 1 key that reference Book
(using the BOOK_ID
column) and another that reference Language
(using the LANGUAGE_ID
column). The same concept applies for BookAuthor
table.What are Composite Primary Keys?
A composite key (also known as a Composite Primary Key) in a RDBMS is a combination of 2 or more table columns used to specify a primary key of the DB table (to uniquely identify each row in the table). Uniqueness is guaranteed when the columns are combined.Now, in our example mentioned above, instead of creating an auto increment column as our table row primary key, we can combine the 2 foreign keys together to create a unique primary and guarantee uniqueness. The combination of columns of our foreign keys constitutes as a composite primary key in our RDBMS.
Composite Primary Keys in JPA
There are 2 approach to map your Composite Primary Keys in JPA:
1) Using @IdClass
annotation.
One such approach is to use the @IdClass
annotation. Each primary key field must be annotated with a @Id
annotation. Since your entity now has more than one @Id
annotation, JPA requires that you specify an ID class and assign it using @IdClass
annotation.For example, using the
BookTitle
, One will have to create a “composite” object that will be declared in your @IdClass
.For
BookTitle
class:For
BookTitleId
class:
Things to remember when using
@IdClass
annotation:
- The property/fields of your composite class must be identical to the property/fields of your entity.
- There are no need for getter and setter methods inside your composite ID class as they are used only for object retrieval.
- The composite ID class doesn’t, necessarily, be declared
Serializable
. - The
equals()
andhashCode()
method were overridden for unique identification of the object, such as in collections using hashing.
Object retrieval is as simple as:
If your entity primary key are not auto sequence field (accompanied with the
@GeneratedValue
annotation) you will have to programmatically assign an unique value on the property.
2) Using the @Embeddable
and @EmbeddedId
annotation.
Using the example above:
For
BookTitleId
class:
Things to consider:
- The composite ID class must be annotated with
Embeddable
annotation. - The composite ID class doesn’t, necessarily, be declared
Serializable
but it’s encouraged. - The composite ID class is mapped in your entity by using the
EmbeddedId
annotation. - It’s good practice to override the
equals()
andhashCode()
method for unique object identification (and uniqueness in hashing by the JVM) on the composite ID class.
id
on BookTitle
entity before persisting.We, however have a scenario. Suppose that
Book
and Language
entity had their ID auto generated on the RDBMS (their id
annotated with @GeneratedValue
respectively) and the BookTitle
composite primary key references the ID of the respective parent table. We want to persist a new Book with an existing language (Book id = unknown, Language id = "en"
). Then what value do we assign bookId
on BookTitleId
composite key class? There is no object reference that links Book
to bookId
on the Composite ID Class.How do we resolve such problem?
One approach will be to individually persist each entity in an order of parent/child relationship, like in this example:
As you can see, we persist each entity one step at a time. A
BookTitle
cannot exist without a Book
. So, the simplest idea will be to associate the relationships between/among them.The Entity Relationship
If you noticed so far, there is an entity relationship between aBookTitle
and a Book
and a BookTitle
with a Language
. That way, we just associate a BookTitle
to a Book
and the JPA provider will “manage” your entity in the persistence context prior to committing the data to the RDBMS.So, modifying the code above we get:
For
Book
class:
For BookTitle
class:
The
BookTitleId
composite ID class remains the same. There is a bi-directional relationship between Book
and BookTitle
(a book can have various titles per language, OneToMany
but you can only have one book title per book, ManyToOne
). Hence I have included addTitle
method on Book
entity. You will notice a particular annotation that I included on the BookTitle
entity and let’s tackle it.
Enter @MapsId
annotation.
The MapsId
Javadoc states:Designates aRemember when I said that Treat JPA as object modelling and object reference.? Well, now we have done it. TheManyToOne
orOneToOne
relationship attribute that provides the mapping for anEmbeddedId
primary key, an attribute within anEmbeddedId
primary key, or a simple primary key of the parent entity. Thevalue
element specifies the attribute within a composite key to which the relationship attribute corresponds. If the entity’s primary key is of the same Java type as the primary key of the entity referenced by the relationship, the value attribute is not specified.
MapsId
annotation states that the ID of the entity must be mapped to the attribute/field of the composite ID class. So you don’t map it to the SQL table column but to the property/attribute/field of the Composite ID class.
So the @MapsId("bookId")
will map to BookTitleId.bookId
class attribute.Notice this additional requirements to attributes that have the
MapsId
annotation:
The
insertable
,updatable
and nullable
are all false.- You need
nullable
to be false. This means that an entity must be provided. - You need
insertable
to be false. This means that the JPA provider will not include the column on a SQLINSERT
statement but on retrieval, an entity will be associated based on the column specified (in this case,Book
entity is associated on theBOOK_ID
DB table column). - You need
updatable
to be false. This means that the JPA provider will not include the column on a SQLUPDATE
statement but on retrieval, an entity will be associated based on the column specified (in this case,Book
entity is associated on theBOOK_ID
DB table column).
MapsId
can be used in attributes that has an entity association specified (using ManyToOne
or OneToOne
annotation).In essence, with all the relationships declared and composite ID mapped, we can essentially allow our JPA provider to manage all our entity (new or already managed) prior to committing the SQL statements back to the RDBMS.
Finally, we can resolve our example by creating a new book and book title at the same time like so:
And voila!. ORM done right. The code is small and very simple to follow and read.
Happy coding. Please feel free to comment below about anything regarding this post. I am looking forward to your input/insight. :-)
i think showing code should be more explicit
ReplyDeleteThat's weird, there is code but it doesn't seem to be displaying. Let me fix it.
Delete