Table: MOVIE
{ID: 3, TITLE: Avengers: Infinity War},
Table: SUPER_HERO
{ID: 2, NAME: Thor},
{ID: 4, NAME: Iron Man},
Table: SUPERHERO_MOVIES
{SUPERHERO_ID: 2, MOVIE_ID: 3},
{SUPERHERO_ID: 4, MOVIE_ID: 3},
As you can see, the SUPERHERO_MOVIES
join table contains the mappings of Ironman and Thor to "Avengers: Infinity War", but mappings to "The Avengers" have been successfully removed. The only "magic" in this code is gaining access to the underlying database Connection
from the EntityManager
.
The EntityManager
class provides an unwrap()
method to which you can specify a class type. In this case, we specify the underlying Hibernate Session.class
, which provides a doWork()
method to which you can provide a class that implements Hibernate's Work
interface. The Work
interface has a single method, named execute()
, to which Hibernate passes a Connection
instance. In the example application I've used Java 8 lambdas to simplify the implementation, but it's good to know how the persistence code works under the hood.
Listing 6 shows the persistence.xml
file for the example application.
Listing 6. persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="SuperHeroes" transaction-type="RESOURCE_LOCAL">
<!-- Persistence provider -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- Entity classes -->
<class>com.geekcap.javaworld.jpa.model.Movie</class>
<class>com.geekcap.javaworld.jpa.model.SuperHero</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:moviedb" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="show_sql" value="true"/>
<property name="hibernate.temp.use_jdbc_metadata_defaults" value="false"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
</properties>
</persistence-unit>
</persistence>
You can build and run the example app from your IDE, or from the command-line using Maven, as shown:
$ mvn clean install
$ cd target/
$ java -jar jpa-example-2-1.0-SNAPSHOT.jar
Conclusion
This two-part tutorial has introduced you to using JPA with Hibernate. In Part 1 you learned how to define entities and their relationships, and you were introduced to the role of the EntityManager
in JPA. We created a simple domain model with two entities in a many-to-one relationship. We then created two corresponding repositories and an example application that pulled it all together.
In Part 2 we've explored a more complicated persistence relationship type: the bidirectional, many-to-many relationship. We once again created two entities and two repositories, and pulled them together in an example application. The many-to-many relationship also allowed us to explore the tradeoffs of different cascade type strategies, and I've demonstrated why I believe PERSIST
is the safest strategy for most use cases.
This tutorial gives you plenty to think about in relation to JPA, but there's more to learn! Be sure to check out my Java tips on inheritance relationships in JPA and composite primary keys in JPA. Understanding the implications of cascading strategies and the tradeoffs in using different inheritance strategies comes in handy because these are common job interview questions. More importantly, your familiarity with these persistence solutions will make you a much better Java programmer.
This story, "Java persistence with JPA and Hibernate, Part 2: Many-to-many relationships" was originally published by JavaWorld.