advertisement
Premier Club Log In/Registration
  Include Code  Search Tips
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   SKILLBUILDING  |   TIP BANK  |   SOURCEBANK  |   FORUMS  |   NEWSLETTERS
Browse DevX
Download the Code for this Article
Partners & Affiliates
advertisement
advertisement
advertisement
Average Rating: 4/5 | Rate this item | 7 users have rated this item.
Email this articleEmail this article
 
Persistence Pays Offs: Advanced Mapping with JPA
In this follow-up to an earlier article on the Java Persistence API, learn how to use JPA to map inheritance, one-to-many, and many-to-many relationships. And learn to use the query language (JPQL) to query concrete and abstract classes. 

advertisement
he Java Persistence API is a new addition to the Java specification that updates Java's method of performing object/relational mapping. In my first article on JPA, I explained the basics of JPA, how to map a one-to-one relationship, and how to query simple objects. In this article I'll move on to discuss some advanced features. Unlike with Entity Beans 2.x, JPA allows inheritance and can implement it in various ways (SINGLE_TABLE, TABLE_PER_CLASS, and JOINED). Because JPA supports coding-by-exception, developers needn't use additional technologies to accomplish advanced ORM. However, if you want, you can always use Java's new annotations to customize mappings and relationships. I'll also show you how to query complex object graphs with inheritance and collections using JQPL.


In this article I'm going to build on the example application that I created for the first article: a customer address book for a fictional music company called Watermelon. To begin, I'll imagine that Watermelon has some new requirements. There are now two types of customers: individuals and companies. Individuals are customers who have a first name, a last name, and a date of birth; companies have a name, a contact name, and a number of employees. This will give me the opportunity to map inheritance with JPA.

Customers have a home address but Watermelon also needs to define a set of delivery addresses. Each of these addresses can be used on the weekend or/and in the evenings. I will implement this information by adding tags to an address. To do all this I will use one-to-many and many-to-many relationships with JPA. Figure 1 shows the business model to implement.

What You Need
First of all, you need to have a JDK 1.5 installed. This article uses MySQL 5 and TopLink Essentials as the implementation of JPA. To compile and execute the code that comes with this article you need Ant 1.7. Test classes are written in JUnit 4.1. If you are not familiar with the new JUnit 4 syntax check this article. Before running the code, you need to create a database called watermelonDB in MySQL with the root user and no password, i.e. mysql> create database watermelonDB;

Author's Note: As in the previous article I will use TopLink Essentials as a JPA provider and MySQL as a relational database.

Inheritance with JPA
As I said, Watermelon deals with two types of customers: individuals and companies. In the model I can describe this using an abstract Customer class from which inherits Individual and Company classes. Customer has an ID, a telephone number, an email address, and a method to validate telephone numbers (see Callback annotations). It also holds the relationships to Address. Both Individual and Company adds specific attributes and behaviors (e.g. a method to calculate the age of an individual).

Figure 1. Watermelon’s address book object model is shown.
Inheritance is a built-in notion in object languages in general and in Java in particular. But storing this hierarchical information in a relational structure is not easy to do. The proof is that, until now, inheritance hasn't been well handled in the EJB persistence domain. Thanks to JPA, polymorphism and inheritance are now possible in a Java EE and Java SE application. Contrary to associations, which are straightforward to map, with inheritance the developer has to choose between several strategies:

  • A single table per class hierarchy (SINGLE_TABLE) is the default. That means that the three classes Customer, Individual, and Company are flattened into one table only (t_customer, for example). All the attributes of the three classes are mapped down to a single table. This approach is the best in terms of performance, since only a single table is involved. On the other hand, concrete class attributes cannot be mapped into not null columns (as all the subclasses are stored in the same main table).
  • A table per concrete entity class (TABLE_PER_CLASS). All properties of the concrete class, including inherited properties, are mapped to columns of one table. In my example I would only have a t_company and t_individual table. Attributes from the abstract class Customer would be copied into these two tables. If this option provides poor support for polymorphic relationships, it is also still optional in the JPA 1.0 specification. TopLink doesn't support it yet, Hibernate does.
  • A table per class (JOINED). In this strategy the root of the class hierarchy (Customer) as well as each subclass is represented by a separate table. In the Watermelon example I will have a common base table t_customer with an ID, telephone number, and email address. Subclass tables (t_company and t_individual) will then be joined with the root through their primary key. This option provides support for polymorphic relationships and that's the option I will use to map inheritance for the Watermelon address book.
The choice of the mapping strategy has to be done only once in the root entity using the @javax.persistence.Inheritance annotation. It is specified on the Customer class like this:

@Entity
@Table(name = "t_customer")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DISC", discriminatorType = DiscriminatorType.STRING, length = 5)
public abstract class Customer {

  @Id
  @GeneratedValue
  private Long id;
  @Column(length = 15)
  protected String telephone;
  @Column(name = "e_mail")
  protected String email;
  // constructors, getters, setters
}
To work, the JOINED strategy (as well as the SINGLE_TABLE) needs additional information: a discriminator column. This column, situated in the root table, enables the persistent provider to identify which instance of the concrete class is stored (is it an individual or a company that is stored in the t_customer table?). Without any annotations the coding-by-exception will create a column named DTYPE of varchar(31) which value is defaulted to the concrete class name.

In the code above, I've decided to name the discriminator column DISC and to give it a length of 5. I can do this using the @javax.persistence.DiscriminatorColumn annotation. The content of this column can be customized in each concrete class by using the @DiscriminatorValue annotation. The value has to be unique and serves to identify the concrete class. The coding-by-exception will default to the class name, but I've decided to change it to indiv and comp (see Table 1).

Table 1. Individual and Company Class Extending Customer
Individual class Company class
@Entity
@Table(name = "t_individual")
@DiscriminatorValue("indiv")

public class Individual extends Customer {
@Column(nullable = false)
private String firstname;
@Column(nullable = false, length = 30)
private String lastname;
@Column(name = "date_of_birth")
@Temporal(TemporalType.DATE)
private Date dateOfBirth;
@Transient
private Integer age;
// constructors, getters, setters
}
@Entity
@Table(name = "t_company")
@DiscriminatorValue("comp")

public class Company extends Customer {
@Column(nullable = false)
private String name;
private String contactName;
@Column(name = "nb_employees")
private Integer numberOfEmployees;
// constructors, getters, setters
}

Notice that each of the three entities is mapped to its own table (using the @Table annotation). As explained in the previous article, the EntityManager and the properties set in the persistent unit will automatically create these tables. The result of a JOINED strategy is the DDLs and the integrity constraints between the three tables, as shown in Table 2.

Table 2. DDLs of t_customer, t_individual and t_company
Customer Individual Company
mysql> desc t_customer

+-----------------+-------------+
| Field | Type                  |
+-----------------+-------------+
| ID              | bigint(20)  |
| DISC            | varchar(5)  |
| e_mail          | varchar(255)|
| TELEPHONE       | varchar(15) |
| home_address_fk | bigint(20)  |
+-----------------+-------------+

mysql> desc t_individual
+---------------+-------------+
| Field | Type |
+---------------+-------------+
| ID | bigint(20) |
| LASTNAME | varchar(30) |
| FIRSTNAME | varchar(255)|
| date_of_birth | date |
+---------------+-------------+
mysql> desc t_company
+--------------+--------------+
| Field | Type |
+--------------+--------------+
| ID | bigint(20) |
| CONTACTNAME | varchar(255) |
| NAME | varchar(255) |
| nb_employees | int(11) |
+--------------+--------------+

            

ALTER TABLE t_individual ADD CONSTRAINT FK_t_individual_ID FOREIGN KEY (ID) REFERENCES t_customer (ID)
ALTER TABLE t_company ADD CONSTRAINT FK_t_company_ID FOREIGN KEY (ID) REFERENCES t_customer (ID)


Once you've chosen the strategy to map inheritance, you can CRUD any of these concrete classes using the EntityManager like any other entity. In the following code, I create an instance of a Company and an Individual class that I persist into a database.


public void createCustomers() {
  // Gets an entity manager
  EntityManagerFactory emf = Persistence.createEntityManagerFactory("watermelonPU");
  EntityManager em = emf.createEntityManager();
  EntityTransaction trans = em.getTransaction();

  // Instantiates a company object
  Company company = new Company("Universal", "Mr Universe", 50000, "+15488454", "info@universal.com");
  company.setHomeAddress(new Address("World Street", "New York", "7448", "UK"));

  // Instantiates an individual object
  calendar.set(1940, 10, 9);
  Individual individual = new Individual("John", "Lennon", "+441909", "john@lenon.com", calendar.getTime());
  individual.setHomeAddress(new Address("Abbey Road", "London", "SW14", "UK"));

  // Persists both customers
  trans.begin();
  em.persist(company);
  em.persist(individual);
  trans.commit();

  // Closes the entity manager and the factory
  em.close();
  emf.close();
}
Querying a hierarchical model is also possible with JPQL. I can query the concrete classes as well as the root abstract class.

JPQL Query Comments
SELECT c FROM Customer c Returns all the customers (individuals and companies)
SELECT c FROM Company c Returns all the companies
SELECT i FROM Individual i Returns all the individuals

And I can use root class attributes on my queries. For example, the email address is defined in the Customer class. I can use this attribute in queries involving the Customer class as well as Individual and Company.

SELECT c FROM Customer c WHERE c.email LIKE '%.com' All customers with .com email address
SELECT c FROM Company c WHERE c.email LIKE '%.com' All companies with .com email address
SELECT i FROM Individual i WHERE i.email LIKE '%.com' All individuals with .com email address

Of course, I cannot use child class attributes to query the root class. For example, the attribute first name is just defined in Individual. I can't use it to query customers or companies.

SELECT c FROM Customer c WHERE c.firstname='John' Will not work because customer has no first name
SELECT c FROM Company c WHERE c.firstname='John' Will not work because company has no first name
SELECT i FROM Individual i WHERE i.firstname='John' Returns the individuals which first name is 'John'

In the address book, companies have a number of employees. JPQL has a set of arithmetic functions that you can use in a query.

SELECT MAX (c.numberOfEmployees) FROM Company c Returns the maximum number of employees
SELECT MIN (c.numberOfEmployees) FROM Company c Returns the minimum number of employees
SELECT SUM (c.numberOfEmployees) FROM Company c Returns the sum of employees

I can also use restrictions on these numbers by applying inferior, superior, or equal functions, as well as between to get a range of employees.

SELECT c FROM Company c WHERE c.numberOfEmployees > 10000 Returns companies that have more than 10000 employees
SELECT c FROM Company c WHERE c.numberOfEmployees BETWEEN 10000 AND 50000 Returns companies that between 10000 and 50000 employees

  Next Page: One-to-Many Relationships


Page 1: IntroductionPage 2: One-to-Many Relationships
advertisement
Advertising Info  |   Member Services  |   Permissions  |   Contact Us  |   Help  |   Feedback  |   Site Map  |   Network Map  |   About


JupiterOnlineMedia

internet.comearthweb.comDevx.commediabistro.comGraphics.com

Search:

Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

Jupitermedia Corporate Info


Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers

Solutions
Whitepapers and eBooks
IBM Whitepaper: Innovative Collaboration to Advance Your Business
Internet.com eBook: Real Life Rails
Avaya Article: Call Control XML - Powerful, Standards-Based Call Control
Tripwire Whitepaper: Seven Practical Steps to Mitigate Virtualization Security Risks
Internet.com eBook: The Pros and Cons of Outsourcing
Go Parallel Article: Scalable Parallelism with Intel(R) Threading Building Blocks
Internet.com eBook: Best Practices for Developing a Web Site
IBM CXO Whitepaper: The 2008 Global CEO Study "The Enterprise of the Future"
Avaya Article: Call Control XML in Action - A CCXML Auto Attendant
Go Parallel Article: James Reinders on the Intel Parallel Studio Beta Program
IBM CXO Whitepaper: Unlocking the DNA of the Adaptable Workforce--The Global Human Capital Study 2008
Adobe Acrobat Connect Pro: Web Conferencing and eLearning Whitepapers
Go Parallel Article: Getting Started with TBB on Windows
HP eBook: Storage Networking , Part 1
MORE WHITEPAPERS, EBOOKS, AND ARTICLES
Webcasts
Go Parallel Video: Intel(R) Threading Building Blocks: A New Method for Threading in C++
HP Video: Is Your Data Center Ready for a Real World Disaster?
Microsoft Partner Portal Video: Microsoft Gold Certified Partners Build Successful Practices
HP On Demand Webcast: Virtualization in Action
Go Parallel Video: Performance and Threading Tools for Game Developers
Rackspace Hosting Center: Customer Videos
Intel vPro Developer Virtual Bootcamp
HP Disaster-Proof Solutions eSeminar
HP On Demand Webcast: Discover the Benefits of Virtualization
MORE WEBCASTS, PODCASTS, AND VIDEOS
Downloads and eKits
Microsoft Download: Silverlight 2 Software Development Kit Beta 2
30-Day Trial: SPAMfighter Exchange Module
Red Gate Download: SQL Toolbelt
Iron Speed Designer Application Generator
Microsoft Download: Silverlight 2 Beta 2 Runtime
MORE DOWNLOADS, EKITS, AND FREE TRIALS
Tutorials and Demos
IBM IT Innovation Article: Green Servers Provide a Competitive Advantage
Microsoft Article: Expression Web 2 for PHP Developers--Simplify Your PHP Applications
Featured Algorithm: Intel Threading Building Blocks - parallel_reduce
MORE TUTORIALS, DEMOS AND STEP-BY-STEP GUIDES