Java Persistence Lab
Short Description
Java JPA + Hibernate Tuturial. How to map Java classes with jpa and Hibernate....
Description
Page 1
Java Persistence Lab
Java Persistence Architecture Java Persistence Architecture (JPA) is a specification for how to persist objects. It provides a standard API and set of services. JPA provides services for saving objects, associations, and inheritance relationships in a database, in a way that is independent of the database and the implementation of JPA. When objects are persisted to a database, or recreated from a database, JPA can save/restore associated objects, and guarantee object uniqueness. JPA version 2 can be used in two contexts: in an enterprise application (JavaEE) that runs in a container, or as part of a stand-alone application using Java SE. JPA is only one choice for object persistence. Other choices are Java Data Objects (JDO), which has a broader scope, and object-relational mapping (ORM) frameworks such as Hibernate. Hibernate provides the basis for many JPA features (such as criteria queries) and itself now implements JPA. Knowing JPA is useful because it is so widely used (Google App Engine supports JPA), and the concepts can be applied to learning JDO, Hibernate, or other frameworks. This lab covers the basics of JPA 2.0 in a stand-alone Java SE application.
Required Software 1. Java SE 1.6.0 SDK (version 1.5 should work as well). 2. MySQL or Derby database. 3. EclipseLink 2.x, an implementation of JPA 2.0. 4. Eclipse or NetBeans. Eclipse Helios and Indigo have good support for JPA, NetBeans 7 has reasonable support. These are not essential: you use JPA with any Java development tools. See end of this document for help installing this software.
The World Database World is a sample database provided by MySQL. The data is from the 1990s, but it's still useful. There are 3 tables as shown below. It's not a great database design: the Continent attribute is an enumeration of Strings, the Region is a String, and Language is a String. COUNTRY table
CITY table
Code Capital Code2 Continent GNP LifeExpectancy LocalName Name Population Region SurfaceArea
ID CountryCode District Name Population
COUNTRYLANGUAGE CountryCode Language isOfficial Percentage
Page 2
Java Persistence Lab
1. Object Persistence Concepts Object persistence means saving information about objects and their associations even after execution ends, e.g. to non-volatile storage. Persistence includes saving and updating objects and associations, searching for saved object data and recreating objects from storage, and guaranteeing that objects retain their uniqueness even after being saved and recreated. Persistence is a set of related responsibilities that isn't closely related to the "domain" logic of your application, and it's a service that's needed by many applications. Hence, it makes sense to implement persistence services as reusable software that can be used by many applications (a framework).
2. How JPA Works The Java Persistence Architecture provides an abstraction that hides the details of saving, updating, querying, and deleting objects in persistence storage. An EntityManager provides services that your application uses to query, save, update, or delete persistent objects, called entities. EntityManager is just an interface. A concrete EntityManager is provided by an implementation of JPA, called a Persistence Provider. JPA provides an Entity Manager Factory that creates a concrete instance of EntityManager based on configuration data. The architecture looks similar to the way JDBC DriverManager creates a Connection object from a url. In this lab, we'll use EclipseLink as the persistence provider. Other providers are Hibernate, OpenJPA, TopLink Essentials, and DataNucleus.
Persistent Storage
The Java code for the above sequence is: final String PERSISTENCE_UNIT = "world"; // name as in persistence.xml factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT); EntityManager em = factory.createEntityManager(); Query query = em.createQuery( "SELECT c FROM City c WHERE c.name='Bangkok'" ); List results = query.getResultList(); System.out.printf("Found %d cities.\n", cities.size()); for( City city: results ) System.out.println( city );
Page 3
Java Persistence Lab
EntityManager provides persistence operations such as find(...), persist(Entity), and remove(Entity). The createQuery method uses a String in Java Persistence Query Language (JPQL): SELECT c FROM City c WHERE c.name = 'Bangkok' This looks like SQL but is written in terms of classes and attributes, not tables. "City c" means a City object c, like in Java. The query returns objects (entities), as shown in the query.getResultList( ).
3. JPA Basics In JPA, a class whose objects are persisted is called an Entity. An entity class is mapped to one or more tables in a database. In some frameworks objects can also be persisted to other forms of storage, including XML files, LDAP directory, or even an Excel spreadsheet. You have to describe the Entities to JPA, such as what attribute is the primary key, what attributes to save/restore, and associations with other objects. There are two ways to describe entities:
annotations added to source code
XML mapping file containing the same information, but in XML
Both ways have advantages and disadvantages. You will use both in this lab.
4. Structure of a Java JPA Project For a project named "WorldJPA" the structure will look like this: WorldJPA/ src/ META-INF/ persistence.xml orm.xml world / domain/ bin/ ... Referenced Libraries/ eclipselink.jar javax.persistence_2.xxx.jar mysql-connector-java-5.1.7.jar
the project base directory source directory meta-information directory JPA configuration file O-R mapping file (optional) base package for our source code package for domain objects generated class files. The name may be "build/classes" or other. JPA 2.x provider (EclipseLink) JPA API MySQL JDBC connector
Exercise: Create a Database Connection (Optional but Helpful) Eclipse and Netbeans let you store information about database connections. If you do this, you can explore the database from within the IDE, and apply database connection parameters (url, user, password) to a new JPA project. The IDE can also generate Java code from database tables. NetBeans 7.0: 1. On the left side of workbench, click the Services tab. 2. Right click on Database and choose "New Connection" 3. Select a driver (Java DB (Embedded) is Derby). Change driver JAR if needed. 4. Click Next. Enter database URL, user name, and password. If your database doesn't require a user or password (e.g. Derby), use empty strings for user and password.
Java Persistence Lab
Page 4
5. Test the connection and fix any problems. Eclipse Helios (with Eclipse Data Tools plugin): Eclipse Data Tools let you connect to a database and explore it. You can perform queries, edit schema, and create Java code from schema. The tools are part of the Eclipse JavaEE bundle, and may already be installed on your machine. If your Eclipse has a perspective named "Database Development" then the tools are already installed. If not, you can install them from the Eclipse update site. To create a database connection: 1. In Window -> View, open the "Database Source Explorer" view. Right click on "Database Connections" and choose New. 2. Choose connector type (MySQL, Derby, etc). Eclipse provides only the "profile" for connectors. You must supply a JDBC connector JAR for the database, or create/use a Library for it. Click Next. 3. Enter database name or url, user, and password. Test the connection. 4. Click Next to view summary. Note the database URL. Click Finish
Page 5
Java Persistence Lab
Exercise: Create a Project for World with JPA Prerequisite: Install EclipseLink. It is convenient to create a library in the IDE. NetBeans 7.0 includes EclipseLink 2.2; NetBeans users can use that or (if you prefer) install a newer version of EclipseLink. You can use JPA in any Java Project You don't need to create a JPA project as described here, and you are not required to have the IDE's JPA plug-in. You can create the META-INF directory and the required files yourself (easier: copy them from another project). The plug-in makes it easier, but it is not required. To use JPA you need a JPA Provider such as EclipseLink. (See references for other JPA providers.) Eclipse: 1. If you have the Dali JPA extension installed, create a new "JPA Project" named WorldJPA. Eclipse will create the META-INF directory and persistence.xml. If you don't have JPA support, create a Java project. Then create a META-INF directory in your src directory. Inside META-INF, create a persistence.xml file using the example in this document. 2. Add the EclipseLink library or JARs (in directory eclipselink/jlib): to your project: eclipselink.jar javax.persistence_2.x.jar 3. Edit persistence.xml and verify that EclipseLink is the provider: org.eclipse.persistence.jpa.PersistenceProvider
4. Add a JDBC driver to your project as a Library or external JAR. For MySQL, the JAR file is mysql-connector-java-5.x.x-bin.jar For Derby embedded server, the JAR is derby.jar
Eclipse Dali JPA Extensions Eclipse has two extensions for JPA: Dali Java Persistence Tools and Dali EclipseLink Support (optional). These extensions are not required to use JPA, but they can reduce typing. The extensions also enable you to generate Java code from database schema and to synchronize files in a JPA project. To install the Dali extensions in Eclipse Helios or Indigo: 1.Click Help → Install New Software 2. In the "Work with" box select Helios (or Indigo if you use Eclipse Indigo). 3. Check the boxes for the Dali tools and click "Install".
Page 6
Java Persistence Lab NetBeans 7.0 1. Create a new Java project named WorldJPA. Any name is OK, but don't use your JDBC project. 2. Create a persistence unit. Right click on the project and choose New → Other → Persistence and choose Persistence Unit. Click Next and enter a persistence unit name (usually the database name), Persistence Library (the persistence provider), and database connection. Choose "None" for table generation strategy. ("Create" means JPA will create the schema in a new database)
Netbeans creates META-INF/persistence.xml (if this file exists, it adds a new persistence unit). 3. Verify that NetBeans added the EclipseLink jars (eclipselink-2.x.jar and javax.persistence-2.0.jar) to your project in the Libraries folder. If not (or if you want to use a newer version of EclipseLink) then add them to the project by right-clicking on the project's Libraries folder. 4. Add a JDBC driver for your database. Right click on the Libraries folder and choose Add JAR or Add Library (if you have a library for the driver). NetBeans includes JDBC libraries for some databases, or you can use your own. Check persistence.xml The persistence.xml that NetBeans creates may contain this line:
For this lab we already have a database schema, so delete this line.
Java Persistence Lab
Page 7
4.1 The Persistence Configuration File: persistence.xml JPA configuration information is in the file named persistence.xml. This file should be in a META-INF directory on your project source (src) folder. persistence.xml specifies persistence information specific to your project, shown in bold type: org.eclipse.persistence.jpa.PersistenceProvider world.domain.City
Note: In the xml header you can delete the long xsi:schemaLocation if you add eclipselink_orm_2_0.xsd to your IDE's Schema catalog, so the IDE can validate the XML. This avoids connecting to the Internet to get the schema. persistence version="2.0" (or newer). Make sure this is not 1.0. persistence-unit name="world" a persistence unit is a group of related entities. You can use any meaningful name; usually it is the database name. transaction-type="RESOURCE_LOCAL" for a stand-alone application. provider
is the implementation of JPA your are using. We are using EclipseLink.
class
(zero or more times) are names of Entity classes.
properties JPA 2.0 specifies standard property names for URL, JDBC driver, user, and password. JPA providers can add optional properties, which don't begin with "javax.persistence". persistence.xml can specify more than one persistence-unit, each in its own section. It may also specify: (zero or more times) the name of a file containing object mapping info as XML.
This file is usually named orm.xml in the META-INF directory, but you can use any name and location. Some developers prefer to put it in the same source package as classes being mapped. META-INF/orm.xml
Page 8
Java Persistence Lab
5. Entity Classes An entity class is a class whose objects are persisted. To be an Entity using JPA, a class must satisfy: 1. POJO rules:
class has a non-private no-argument constructor (default constructor)
get/set methods for persistent attributes, using Java standard naming convention
2. Class must not be final. Persisted variables and their get/set methods are not final, either. 3. Class must define an ID attribute that uniquely identifies each instance. This usually is the primary key of the table in a database. The ID can be a compound attribute. Both abstract and concrete classes can be entities. An entity class can extend another class. If an entity class will be used in a collection, then you should also specify: 4. Meaningful equals() and hashCode() methods. For persisted objects, the id attribute is a good choice. For transient (non-persisted) objects, the choice depends on your application.
5.1 Entities for the World Project There are three tables in the World database, each of which will map to a Java class. For now, we'll define two entity classes: city table
City id: Integer country: Country district: String name: String population: int country
ID CountryCode District Name Population
capital
Country code: String capital: City continent: String gnp: double lifeExpectancy:float name: String population: int region: String surfaceArea: float
country table Code Capital Continent GNP LifeExpectancy Name Population Region SurfaceArea
Notice that City contains a reference to a Country object, whereas the city table contains a foreign key reference to a row in the country table. Similarly, a Country object contains a capital City reference, whereas the country table has a foreign key reference to a row in the city table.
Java Persistence Lab
Page 9
Exercise: Create the City class (without a Country) Create a package named world.domain for our domain objects. Implement the City class. Don't include a country or countrycode attribute. Follow the rules for entity classes given above (a default constructor, get/set methods, equals using id). Write a toString(). package world.domain; public class City { private Integer id; private String name; private String district; private int population; /** required default constructor */ public City( ) { } //TODO write get/set methods and equals. //TODO write a toString
5.2 Define Entity Mapping for City using XML Entity mapping tells JPA how to map object attributes to table columns. It be specified in XML or using Annotations in Java source code. XML mapping was used long before annotations and is still used by many frameworks. So, it is educational to learn XML mapping first. Edit persistence.xml to specify that world.domain.City is an entity class and specify the name of an XML mapping file. Add two lines to persistence.xml as shown in bold type: org.eclipse.persistence.jpa.PersistenceProvider META-INF/orm.xml world.domain.City remainder of file as before
Next create an orm.xml file in the META-INF directory. orm.xml is where we define how to persist City objects. The xsi:schemaLocation string should be on one line.
Page 10
Java Persistence Lab
Each section specifies how to persist one entity class. The meta-data='true" is optional; it means that this file contains all the persistence info for City, so annotations in the City class (if any) are ignored.
define how to map attributes of City to columns in a database table.
declares the attribute id is a unique identifier for persisted objects. says that the id attribute maps to a column named ID in the database. This isn't required: JPA can automatically map attributes to columns when the names are the same ignoring case.
means that the database will generate the value of the id.
define an attribute that can be saved as a field in the database, using standard mapping between Java types and SQL types. we can specify the table name, but the default is a table with the same name as the class (JPA ignores case of letters to determine a matching table name). Since the column names are the same as the attribute names (except for case of letters), we don't have to specify them. We can, however, specify column names if we want.
5.3 Demo Program using JPA We will write a program to find cities by name in the database, and print matches on the console. Here is an example run: Name of city to find: Bangkok Bangkok, Bangkok pop. 6,320,174 Name of city to find: Los Angeles Los Angeles, Bíobío pop. 158,215 Los Angeles, California pop. 3,694,820 The code follows the sequence diagram in the section "How JPA Works". Create a JPADemo class in the world package. Our JPA code needs an EntityManagerFactory, so we will create a singleton accessor method for this: package world; public class JPADemo { public static final String PERSISTENCE_UNIT = "world"; private static EntityManagerFactory factory = null;
Persistence unit name must match the name in persistence.xml
public static EntityManagerFactory getFactory() { if (factory == null) factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT); return factory; }
Java Persistence Lab
Page 11
Next, write a findCity( ) method that finds cities by name. There may be more than one match, so this method should return a collection. We will use a List since the JPA Query methods returns a List. /** Find cities with a given name. Returns all matching cities. */ public static List findCity(String name) { EntityManager em = getFactory().createEntityManager(); Query query = em.createQuery("SELECT c FROM City c WHERE c.name= :name"); query.setParameter("name", name); List cities = query.getResultList(); em.close(); return cities; }
This query string uses Java Persistence Query Language (JPQL). The :name is a named parameter . The query.setParameter(name, value) method inserts an actual value for the named parameter. This is similar to using ? in an SQL PreparedStatement, but safer since values are substituted by name. Write a citySearch() method to prompt user for city name, call findCity, and display the results. Invoke citySearch from the class's main method. /** User interface for finding cities by name. */ public static void citySearch( ) { Scanner console = new Scanner(System.in); while(true) { System.out.print("Name of city to find: "); String name = console.nextLine().trim(); if (name.length() == 0) break; List cities = findCity(name); for(City city: cities) showCity( city ); } } //TODO write a showCity( City ) method to print city information //TODO invoke citySearch from the class's main method.
Exercise: Run the program and correct any problems Typically you will get exceptions the first few times you run a JPA program. Most exceptions are caused by configuration or typing errors, such as: •
JDBC driver for your database not loaded. This could be due to forgetting to add the JAR file to the project, specifying the wrong driver class name in persistence.xml, or a mistake in the URL. For example: "jdbc.mysql://localhost/world" (should be jdbc:mysql).
•
Can't connect to database because user or password is incorrect. If using Derby, a common problem is that a Derby database can have only one active connection. If you connected to Derby using some other tool (including the IDE's database browser), you must close that connection.
•
Attribute name not consistent between Java source, orm.xml, and database field names.
•
get/set method name doesn't match attribute name.
Note: the Exception output is usually very long. Start from the first exception and read down the list. Focus on (a) the Description of the exception, (b) the location. The first location inside your code is usually the cause.
Java Persistence Lab
Page 12
6. Entity Relationships Objects usually contain references to other objects. A persistence framework needs to preserve these relationships. For example, a City refers to the Country it is part of. A Country has a reference to its capital City. There are 4 types of associations:
One-to-one
One-to-many
Many-to-one
Many-to-many
In addition, relationships can be one-sided (unidirectional) or two-sided (bidirectional). One-sided: a Country has a reference to its capital City. Two-sided: a City refers to its Country, and the Country maintains a collection of its Cities.
6.1 One-to-One Associations A Country object has a reference to its capital City. This is determined by the capital field in the country table. No other Country object will refer to the same City, so it is a one-to-one association. To implement this association in Java we need: 1. a Country class containing a City reference named capital 2. mapping info for persisting Country objects to the country table. 3. a one-to-one (or many-to-one) mapping of the capital attribute of Country to a City object. You will do each of these in the following exercises.
Exercise: Create a Country class Create a Country class in world.domain with the attributes shown in the UML diagram (above). The Country class omits some of the fields in the country table, for simplicity. Later you'll see how to automatically generate a complete Country class from the database schema. The Country class must obey the rules for entity classes: default constructor, get/set methods, and an equals method that compares countries using the code. A toString method is also useful. package world.domain; public class Country { private String code; private City capital; private String name; ... //TODO default constructor //TODO get/set methods for attributes //TODO equals() and toString() }
Java Persistence Lab
Page 13
Exercise: Add entity mapping for Country to orm.xml In orm.xml we need to specify how to persist attributes of Country. We can omit the "basic" attributes that map to table columns when the attribute and column have the same name (ignoring case of letters)! JPA can determine the mapping itself. For example, the name attribute (String) will be automatically mapped to the Name field (CHAR(n) or VARCHAR(n)) of the country table. We do need to specify: •
primary key and how it is generated. For Country, the application supplies the country code.
•
one-to-one association of capital to a City object using the city ID.
•
declare that Country is an entity in persistence.xml
1. Add these lines to orm.xml: The element specifies the column name and width for the country code. This is not required. refers to the capital attribute of Country. The specifies that the column named "Capital" should be joined to the ID of the city table. This is like the SQL: SELECT * FROM Country JOIN City ON Country.Capital = City.ID It is unfortunate (and confusing) that the capital attribute of Country has the same name as the field. A more standard field name for this would by Capital_ID. If the field name was Capital_ID we could have omitted the and use the JPA defaults. 2. In persistence.xml add world.domain.Country as an entity class
View more...
Comments