Ignoring joints, a typical query essentially hits one table and returns back subset of the records in that single table. With JPA, we are not hitting tables but we are rather dealing with entities. What is the difference? An entity might map to a single table, multiple tables, or it could be involved in some kind of inheritance relationship.
What happens when we perform a query on an entity type that serves as a base class? It turns out that the actual “work” to make this happen is very simple. This tutorial along with the exercises gives you all the experience you’ll need to figure out so-called polymorphic queries.
Introduction
title: JPA_Tutorial_4_Introduction
—
So far, every query we’ve used has returned a single kind of result. In this tutorial we change things up a bit by moving from only using Books in our Library to working with Books and Dvd’s, both of which inherit from Resource. When we search for a Resource we might get back Books, Dvd’s, or both. This makes our queries polymorphic.
Introduce the Resource class and make the appropriate refactorings
Introduce a new type, Dvd
Setup
title: JPA_Tutorial_4_Setup
—
These instructions assume you are starting with the solutions mentioned above. If you finished JPA_Tutorial_3_A_Mini_Application, you have a few options:
Recommended Copy that project and follow the tutorial against the copy
Directly modify that project
Setup Basic Project
If you start with the jar file, here are the steps to get started:
In Eclipse, create a new Project: File:New:Project
Select Java Project and click Next
Enter a project name, e.g. JpaTutorial4, and click Finish
Open up the provided jar file and copy the contents into your project directory. For example, if your workspace directory is C:\workspaces\JpaAndEjb3** and your project name is **JpaTutorial4, then extract the contents of the jar file into *C:\workspaces\JpaAndEjb3\JpaTutorial4* making sure to overwrite .classpath and .project.
In Eclipse, select your project and refresh it (right-click, refresh)
Start Database Server
This set of source files assumes you’ve setup and started a Hypersonic server. The instructions
to do so are here. Note that these instructions are both for configuring
your database and configuring QuantumDB. If you are not using the QuantumDB plug-in, just pay attention
to the sections on Start Your Database and JPA in JSE Settings.
Verify Your Project Works
Select your project in Eclipse
Right-click, select Run As:JUnit
All tests should pass. Please verify that the do before you continue.
V3 Requirements: Different Kinds of Resources
title: JPA_Tutorial_4_Different_Kinds_of_Resources
—
What happens when you perform a query on a type that has subclasses? That’s the purpose of this tutorial’s transformations. In JPA_Tutorial_3_A_Mini_Application, we assumed Patrons could only check out books. Now they can checkout Books or DVDs (once we’ve got two different kinds of resources, adding a third is not a big deal).
It turns out support for inheritance in queries (as well as JPA) is built in. In fact, you do not actually need to do anything other than have one entity inherit from another entity to get everything to work. There are three ways to represent inheritance:
One table for all classes in the hierarchy (the default)
One table for each concrete class
Table with subclasses (this is something that is optional to support in the current JPA spec.)
For now we’ll stick with the default setting. Why? Which option you choose will impact performance, the database schema, how normalized your database is, but it will not affect how you write your code.
Step one we need to update our basic system. To do this we’ll do the following:
Introduce a new entity type called Resource
Make the book entity inherit from the Resource entity
Move attributes and methods from book that apply to all resources up to the Resource class
Change the BookDao to be a ResourceDao
Re-introduce a stripped down BookDao to support searching by ISBN (which we’ll assume apply to books but not DVD’s)
Update all the methods that take books and replace them with resources (where appropriate)
Update the methods returning Book and have them instead return Resource (and List --> List)
Update all references to Book and replace them with Resource
Update all the comments that talk about books to talk about resources
You get the idea, it’s a lot of work to make this change. That’s why we’ll do this first and make sure all of our tests pass before we actually add a second kind of resource.
Note the source code for all of these changes is at the bottom of this page.
The Updated Entities
Resource.java
It turns out that this change touched all of the entities. So here are the rest of the entities changed to reflect this new base class.
Book.java
This class lost a lot of its methods as they were moved up to to Resource.
Fine.java
We need to change all of Book references to instead be Resource.
LoanId.java
Change the bookId to resourceId, update the names queries that refer to book to instead refer to Resource.
Loan.java
Loan refers to resource instead of book. This includes its join columns and named queries.
Patron.java
The changes to Patron are a little less extreme. Other than some comments referring to books (you can find them), one method changed:
The exceptions
Rename all of the exceptions with “Book” in their name. Replace “Book” with “Resource”
Rename all of the variables names bookId –> resourceId
How can you easliy go about doing this? Use the refactor factor in Eclipse. Select an exception class, right-click and select Refactor:Rename and enter a new name. You can also do the same thing by selecting the attribute name, right-click, refactor:rename and enter the new name. Make sure to select the bottom two selections regarding renaming the getter and setter.
The Dao’s
BookDao.java
Most of the functionality that was in BookDao is now in ResourceDao.java. Why is this? Or better yet, why is the a BookDao at all? Look at the one method and answer the question for yourself (or ask).
Here’s an updated version of BookDao:
Library.java
The Library has several changes. First, most references to Book have been replaced with Resource. Second, it now has 4 dao’s instead of 3 (BookDao became ResourceDao and there’s a new BookDao).
LoanDao.java
This class no longer knows about books, it only knows about resources.
ResourceDao.java
Many of the BookDao functions are now here and they work with Resources intead of with Books (or rather they work with both but the interface deals with Resources).
The Tests
BookDaoTest.java
Removed (or moved to ResourceDaoTest, take your pick).
LibraryTest.java
Updated to work primarily with Resources instead of Books. Also, added additional initialization code since the library now has four dao’s instead of 3.
PatronDao.test
Only a comment changes in this one. If you use Eclipse’s refactoring feature, it automatically gets updated.
ResourceDaoTest.java
The renamed and updated BookDaoTest.java. This class still builds books (they are currently the only kind of concrete subclass of entity).
title: JPA_Tutorial_4_Adding_a_Second_Kind_of_Resource
—
Considering the amount of work required to get ready to add a resource, you might think that creating a hierarchy and then performing so-called polymorphic queries is difficult. It isn’t. The difficulty of moving towards supporting a hierarchy is that we started with a concrete class and wanted to later introduce an abstract concept, the Resource.
In this final step for the tutorial, we’ll create a second kind of resource, a DVD and then write some basic tests.
Supporting DVD’s
The first basic test is creating a dvd and checking it out to a Patron. This is a test we add to LibraryTest.java.
First test: Checkout a Dvd
This test uses a new constant, CURRENT_PLUS_6, here’s the change to LibraryTest to support that.
To get this to compile, we need to add support in the library class for creating a DVD. Here’s that basic support:
Updating Library
Notice that we have a director and a dvd in this example. Here are those classes:
Director.java
Dvd.java
Resource.java
To get this to actually work, we need to use the @Inheritance annotation on resource. Here’s the change:
As mentioned before, there are three kinds of representations for hierarchy. We are using the so-called JOINED approach, which means there is a table per class. It turns out that we need to use this approach based on how we’ve defined Book. Why that is will be answered in a following assignment.
A Few More Tests
Here are a few more tests to verify basic functionality. These also belong in LibraryTest.java.
Summary: Inheritance
That’s pretty much it. If we had started with a base class called Resource, this work would have been trivial. Adding new subclasses is quite easy. Figuring out which representation may take a little experimentation, but it’s easy to change.
Queries are inherently polymorphic in nature if that makes sense for the given query. Your challenge is working with a base type insead of down-casting to a derived type.
V3 Assignments
title: JPA_Tutorial_4_Assignments
—
Director <–> DVD
Right now the relationship between Director and DVD is one-way. Make it bidirectional.
Experiment with Inheritance Types
We use InheritanceType.JOINED. There are two others, SINGLE_TABLE, TABLE_PER_CLASS. Try out both of those and see if you can figure out what is happening for each of them. Be sure to re-run all the unit tests after switching to each type.
When you switch to the SINGLE_TABLE approach, what happens with column nullability, the isbn column (from Book), and the Dvd class?
When you switch to the TABLE_PER_CLASS approach, why does the key generation strategy have to change to in order fix the ‘Cannot use identity column key generation’ error? What did you change to, and why?
Can you sum up the advantages/disadvantages of each approach?
Sub-resource queries
Create a query that returns only Books or only Dvds.
Draw Tables
Create a visual representation of the database tables generated. Consider using SQuirreL SQL Client or the Quantum DB plugin for eclipse.
Update UI
Update your UI to allow a user to add a new DVD and a Patron to check out dvd’s.
Name Equality
During the first offering of the class, one student asked about the Name object, which is used in an equals() and hashCode() method. It turns out that the unit tests written did not expose the fact that the Name class lacks these methosd.
Comments