`
frank127
  • 浏览: 10486 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

JPA notes 1

    博客分类:
  • JPA
阅读更多

 

Creating an Entity

Regular Java classes are easily transformed into entities simply by annotating them. In fact, by adding a couple of annotations, virtually any class with a no-arg constructor can become an entity. Let’s start by creating a regular Java class for an employee.

 

Listing 2-1 shows a simple Employee class.

 

Listing 2-1. Employee Class

 

 

public class Employee {
	private int id;
	private String name;
	private long salary;

	public Employee() {
	}

	public Employee(int id) {
		this.id = id;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public long getSalary() {
		return salary;
	}

	public void setSalary(long salary) {
		this.salary = salary;
	}
}

 

 

 

You may notice that this class resembles a JavaBean-style class with three properties: id, name, and salary. Each of these properties is represented by a pair of accessor methods to get and set the property, and is backed by a member field. Properties or member fields are the units of state within the entity that we want to persist.

 

 

To turn Employee into an entity, we first need to annotate the class with @Entity. This is primarily just a marker annotation to indicate to the persistence engine that the class is an entity.

The second annotation that we need to add is @Id. This annotates the particular field or property that holds the persistent identity of the entity (the primary key) and is needed so the provider knows

which field or property to use as the unique identifying key in the table. Adding these two annotations to our Employee class, we end up with pretty much the same class that we had before, except that now it is an entity.

 

Listing 2-2 shows the entity class.

 

 

 

@Entity
public class Employee {
	@Id private int id;
	private String name;
	private long salary;

	public Employee() {
	}

	public Employee(int id) {
		this.id = id;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public long getSalary() {
		return salary;
	}

	public void setSalary(long salary) {
		this.salary = salary;
	}
}

 

 When we say that the @Id annotation is placed on the field or property, we mean that the user can choose to annotate either the declared field or the getter method of a JavaBean-style property. Either field or property strategy is allowed, depending on the needs and tastes of the entity developer. We have chosen in this example to annotate the field because it is simpler; in general, this will be the easiest and most direct approach. We will discuss the details of annotating persistent state using field or property access in subsequent chapters.

 

 

 

Entity Manager

 

 

Entity managers are configured to be able to persist or manage specific types of objects, read and write to a given database, and be implemented by a particular persistence provider (or provider for short). It is the provider that supplies the backing implementation engine for the entire Java Persistence API, from the EntityManager through to implementation of the query classes and SQL generation.

All entity managers come from factories of type EntityManagerFactory. The configuration for an entity manager is templated from the EntityManagerFactory that created it, but it is defined separately as a persistence unit. A persistence unit dictates either implicitly or explicitly the settings and entity classes used by all entity managers obtained from the unique EntityManagerFactory instance bound to that persistence unit. There is, therefore, a one-to-one correspondence between a persistence unit and its concrete EntityManagerFactory.

Persistence units are named to allow differentiation of one EntityManagerFactory from another. This gives the application control over which configuration or persistence unit is to be used for operating on a particular entity.



 

 

Figure 2-1 shows that for each persistence unit there is an EntityManagerFactory and that many

entity managers can be created from a single EntityManagerFactory. The part that may come as a

surprise is that many entity managers can point to the same persistence context. We have talked only about an entity manager and its persistence context, but later on we will see that there may in fact be multiple references to different entity managers all pointing to the same group of managed entities. This will enable the control flow to traverse container components but continue to be able access the same persistence context.

 

 

Obtaining an Entity Manager

An entity manager is always obtained from an EntityManagerFactory. The factory from which it was obtained determines the configuration parameters that govern its operation. While there are shortcuts

that veil the factory from the user view when running in a Java EE application server environment, in

the Java SE environment we can use a simple bootstrap class called Persistence. The static createEntityManagerFactory() method in the Persistence class returns the EntityManagerFactory for

the specified persistence unit name. The following example demonstrates creating an EntityManagerFactory for the persistence unit named “EmployeeService”:

 

 

 

 

EntityManagerFactory emf =
Persistence.createEntityManagerFactory("EmployeeService");

 

 

 

The name of the specified persistence unit “EmployeeService” passed into the

createEntityManagerFactory() method identifies the given persistence unit configuration that determines such things as the connection parameters that entity managers generated from this factory will use when connecting to the database.

 

 

Now that we have a factory, we can easily obtain an entity manager from it. The following example demonstrates creating an entity manager from the factory that we acquired in the previous example:

 

 

EntityManager em = emf.createEntityManager();

 

 

With this entity manager, we are now in a position to start working with persistent entities.

 

Persisting an Entity

 

 

Persisting an entity is the operation of taking a transient entity, or one that does not yet have any persistent representation in the database, and storing its state so that it can be retrieved later. This is really the basis of persistence—creating state that may outlive the process that created it. We are going to start by using the entity manager to persist an instance of Employee. Here is a code example that does just that:

 

 

 

Employee emp = new Employee(158);
em.persist(emp);

 

 

 

Listing 2-3 shows how to incorporate this into a simple method that creates a new employee and persists it to the database.

 

Listing 2-3. Method for Creating an Employee

 

public Employee createEmployee(int id, String name, long salary) {
	Employee emp = new Employee(id);
	emp.setName(name);
	emp.setSalary(salary);
	em.persist(emp);
	return emp;
}

 

 

 

This method assumes the existence of an entity manager in the em field of the instance and uses it

to persist the Employee. Note that we do not need to worry about the failure case in this example. It will

result in a runtime PersistenceException being thrown, which will get propagated up to the caller.

 

Finding an Entity

 

 

Once an entity is in the database, the next thing one typically wants to do is find it again. In this section, we will show how an entity can be found using the entity manager. There is really only one line that we need to show:

 

 

Employee emp = em.find(Employee.class, 158);

 

 

 

We are passing in the class of the entity that is being sought (in this example, we are looking for an instance of Employee) and the id or primary key that identifies the particular entity (in our case we want to find the entity that we just created). This is all the information needed by the entity manager to find the instance in the database, and when the call completes, the employee that gets returned will be a managed entity, meaning that it will exist in the current persistence context associated with the entity manager. Passing in the class as a parameter also allows the find method to be parameterized and return an object of same type that was passed in, saving the caller an extra cast.

 

 

What happens if the object has been deleted or if we supplied the wrong id by accident? In the event that the object was not found, then the find() call simply returns null. We would need to ensure that a null check is performed before the next time the emp variable is used

 

 

 

 

 

The code for a method that looks up and returns the Employee with a given id is now trivial and shown in Listing 2-4.

 

Listing 2-4. Method for Finding an Employee

 

 

public Employee findEmployee(int id) {
return em.find(Employee.class, id);
}

 

 

 

In the case where no employee exists for the id that is passed in, then the method will return null because that is what find() will return.

 

 

Removing an Entity

 

Removal of an entity from the database is not as common as you might think. Many applications never delete objects, or if they do they just flag the data as being out of date or no longer valid and then just keep it out of sight of clients. We are not talking about that kind of application-level logical removal, where the data is not even removed from the database. We are talking about something that results in a DELETE statement being made across one or more tables.

 

In order to remove an entity, the entity itself must be managed, meaning that it is present in the persistence context. This means that the calling application should have already loaded or accessed the entity and is now issuing a command to remove it. This is not normally a problem given that most often the application will have caused it to become managed as part of the process of determining that this was the object that it wanted to remove.

 

A simple example of removing an employee is the following:

 

 

 

 

Employee emp = em.find(Employee.class, 158);
em.remove(emp);

 

 

 

In this example, we are first finding the entity using the find() call, which returns a managed instance of Employee, and then removing the entity using the remove() call on the entity manager. Of course, you learned in the previous section that if the entity was not found, then the find() method will return null. We would get a java.lang.IllegalArgumentException if it turned out that we passed null into the remove() call because we forgot to include a null check before calling remove().

In our application method for removing an employee, we can fix the problem by checking for the existence of the employee before we issue the remove() call, as shown in Listing 2-5.

 

Listing 2-5. Method for Removing an Employee

 

public void removeEmployee(int id) {
	Employee emp = em.find(Employee.class, id);
	if (emp != null) {
		em.remove(emp);
	}
}

 

 

 

This method will ensure that the employee with the given id, provided the id is not null, is removed from the database. It will return successfully whether the employee exists or not.

 

 

Updating an Entity

 

There are a few different ways of updating an entity, but for now we will illustrate the simplest and most common case. This is where we have a managed entity and want to make changes to it. If we do not have a reference to the managed entity, then we must first get one using find() and then perform our modifying operations on the managed entity. This code adds $1,000 to the salary of the employee with id 158:

 

 

Employee emp = em.find(Employee.class, 158);
emp.setSalary(emp.getSalary() + 1000);

 

 

Note the difference between this operation and the others. In this case we are not calling into the entity manager to modify the object, but directly calling the object itself. For this reason it is important that the entity be a managed instance; otherwise, the persistence provider will have no means of detecting the change, and no changes will be made to the persistent representation of the employee. Our method to raise the salary of a given employee will take the id and amount of the raise, find the employee, and change the salary to the adjusted one. Listing 2-6 demonstrates this approach.

 

Listing 2-6. Method for Updating an Employee

 

 

public Employee raiseEmployeeSalary(int id, long raise) {
	Employee emp = em.find(Employee.class, id);
	if (emp != null) {
		emp.setSalary(emp.getSalary() + raise);
	}
	return emp;
}

 

 

If we can’t find the employee, we return null so the caller will know that no change could be made. We indicate success by returning the updated employee.

 

 

Transactions

 

You may feel that the code so far seems inconsistent with what we said earlier about transactionality when working with entities. There were no transactions in any of the preceding examples, even though we said that changes to entities must be made persistent using a transaction.

 

In all the examples except the one that called only find(), we assume that a transaction enclosed each method. The find() call is not a mutating operation, so it may be called any time, with or without a transaction.

 

Once again, the key is the environment in which the code is being executed. The typical situation when running inside the Java EE container environment is that the standard Java Transaction API (JTA) is used. The transaction model when running in the container is to assume the application will ensure that a transactional context is present when one is required. If a transaction is not present, then either the modifying operation will throw an exception or the change will simply never be persisted to the data store. We will come back to discussing transactions in the Java EE environment in more detail in chapter3

 

In our example in this chapter, though, we are not running in Java EE. We are in a Java SE environment, and the transaction service that should be used in Java SE is the EntityTransaction service. When executing in Java SE, we either need to begin and to commit the transaction in the operational methods, or we need to begin and to commit the transaction before and after calling an operational method. In either case, a transaction is started by calling getTransaction() on the entity manager to get the EntityTransaction and then invoking begin() on it. Likewise, to commit the transaction the commit() call is invoked on the EntityTransaction obtained from the entity manager. For example, starting and committing before and after the method would produce code that creates an employee the way it is done in Listing 2-7.

 

Listing 2-7. Beginning and Committing an EntityTransaction

 

em.getTransaction().begin();
createEmployee(158, "John Doe", 45000);
em.getTransaction().commit();

 

Further detail about resource-level transactions and the EntityTransaction API are contained in Chapter 6.

 

 

Queries

 

In general, given that most developers have used a relational database at some point or another in their lives, most of us pretty much know what a database query is. In JPA, a query is similar to a database query, except that instead of using Structured Query Language (SQL) to specify the query criteria, we are querying over entities and using a language called Java Persistence Query Language (JP QL).

 

A query is implemented in code as a Query or TypedQuery object. They are constructed using the EntityManager as a factory. The EntityManager interface includes a variety of API calls that return a new Query or TypedQuery object. As a first-class object, a query can in turn be customized according to the needs of the application.

 

A query can be defined either statically or dynamically. A static query is defined in either annotation or XML metadata, and it must include the query criteria as well as a user-assigned name. This kind of query is also called a named query, and it is later looked up by its name at the time it is executed.

 

A dynamic query can be issued at runtime by supplying the JP QL query criteria, or a criteria object. They may be a little more expensive to execute because the persistence provider cannot do any query preparation beforehand, but JP QL queries are nevertheless very simple to use and can be issued in response to program logic or even user logic.

 

Following is an example showing how to create a dynamic query and then execute it to obtain all the employees in the database. Of course, this may not be a very good query to execute if the database is large and contains hundreds of thousands of employees, but it is nevertheless a legitimate example. The simple query is as follows:

 

TypedQuery<Employee> query = em.createQuery("SELECT e FROM Employee e",
Employee.class);
List<Employee> emps = query.getResultList();

 

We create a TypedQuery object by issuing the createQuery() call on the EntityManager and passing in the JP QL string that specifies the query criteria. The JP QL string refers not to an EMPLOYEE database table but to the Employee entity, so this query is selecting all Employee objects without filtering them any further. You will be diving into queries in Chapter 7, JP QL in Chapters 7 and 8, and criteria queries in Chapter 9. You will see that you can be far more discretionary about which objects you want to be returned.

 

To execute the query we simply invoke getResultList() on it. This returns a List (a subinterface of Collection) containing the Employee objects that matched the query criteria. We can easily create a method that returns all of the employees, as shown in Listing 2-8.

 

Listing 2-8. Method for Issuing a Query

 

public List<Employee> findAllEmployees() {
	TypedQuery<Employee> query = em.createQuery("SELECT e FROM Employee e", Employee.class);
	return query.getResultList();
}

 

This example shows how simple queries are to create, execute, and process, but what this example does not show is how powerful they are. In Chapter 7, we will examine many other extremely useful and interesting ways of defining and using queries in an application.

 

Putting It All Together

 

We can now take all the methods that we have created and combine them into a class. The class will act

like a service class, which we will call EmployeeService and will allow us to perform operations on employees. The code should be pretty familiar by now. Listing 2-9 shows the complete implementation.

 

Listing 2-9. Service Class for Operating on Employee Entities

 

import javax.persistence.*;
import java.util.List;

public class EmployeeService {
	protected EntityManager em;
	public EmployeeService(EntityManager em) {
		this.em = em;
	}
	
	public Employee createEmployee(int id, String name, long salary) {
		Employee emp = new Employee(id);
		emp.setName(name);
		emp.setSalary(salary);
		em.persist(emp);
		return emp;
	}
	
	public void removeEmployee(int id) {
		Employee emp = findEmployee(id);
		if (emp != null) {
			em.remove(emp);
		}
	}

	public Employee raiseEmployeeSalary(int id, long raise) {
		Employee emp = em.find(Employee.class, id);
		if (emp != null) {
			emp.setSalary(emp.getSalary() + raise);
		}
		return emp;
	}
	
	public Employee findEmployee(int id) {
		return em.find(Employee.class, id);
	}
	

	public List<Employee> findAllEmployees() {
		TypedQuery<Employee> query = em.createQuery("SELECT e FROM Employee e", Employee.class);
		return query.getResultList();
	}
}

This is a simple yet fully functional class that can be used to issue the typical create, read, update, and delete (CRUD) operations on Employee entities. This class requires that an entity manager is created and passed into it by the caller and also that any required transactions are begun and committed by the caller. It may seem strange at first, but decoupling the transaction logic from the operation logic makes this class more portable to the Java EE environment. We will revisit this example in the next chapter, in which we focus on Java EE applications.

 

A simple main program that uses this service and performs all the required entity manager creation and transaction management is shown in Listing 2-10.

 

Listing 2-10. Using EmployeeService

 

import javax.persistence.*;
import java.util.List;

public class EmployeeTest {
	public static void main(String[] args) {
		EntityManagerFactory emf = Persistence
				.createEntityManagerFactory("EmployeeService");
		EntityManager em = emf.createEntityManager();
		EmployeeService service = new EmployeeService(em);
		// create and persist an employee
		em.getTransaction().begin();
		Employee emp = service.createEmployee(158, "John Doe", 45000);
		em.getTransaction().commit();
		System.out.println("Persisted " + emp);
		// find a specific employee
		emp = service.findEmployee(158);
		System.out.println("Found " + emp);
		// find all employees
		List<Employee> emps = service.findAllEmployees();
		for (Employee e : emps)
			System.out.println("Found employee: " + e);
		// update the employee
		em.getTransaction().begin();
		emp = service.raiseEmployeeSalary(158, 1000);
		em.getTransaction().commit();
		System.out.println("Updated " + emp);
		// remove an employee
		em.getTransaction().begin();
		service.removeEmployee(158);
		em.getTransaction().commit();
		System.out.println("Removed Employee 158");
		// close the EM and EMF when done
		em.close();
		emf.close();
	}
}

 

Note that at the end of the program we use the close() methods to clean up the entity manager and the factory that we used to create it. This ensures that all the resources they might have allocated are properly released.

 

Packaging It Up

 

Now that you know the basic building blocks of JPA, we are ready to organize the pieces into an application that runs in Java SE. The only thing left to discuss is how to put it together so that it runs. 

 

Persistence Unit

The configuration that describes the persistence unit is defined in an XML file called persistence.xml. Each persistence unit is named, so when a referencing application wants to specify the configuration for an entity it needs only to reference the name of the persistence unit that defines that configuration. A single persistence.xml file can contain one or more named persistence unit configurations, but each persistence unit is separate and distinct from the others, and they can be logically thought of as being in separate persistence.xml files.

 

Many of the persistence unit elements in the persistence.xml file apply to persistence units that are deployed within the Java EE container. The only ones that we need to specify for our example are name, transaction-type, class, and properties. There are a number of other elements that can be specified in the persistence unit configuration in the persistence.xml file, but they will be discussed in more detail in Chapter 13. Listing 2-11 shows the relevant part of the persistence.xml file for this example.

 

Listing 2-11. Elements in the persistence.xml File

 

<persistence>
	<persistence-unit name="EmployeeService" transaction-type="RESOURCE_LOCAL">
		<class>examples.model.Employee</class>
		<properties>
			<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
			<property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/EmpServDB;create=true"/>
			<property name="javax.persistence.jdbc.user" value="APP"/>
			<property name="javax.persistence.jdbc.password" value="APP"/>
		</properties>
	</persistence-unit>
</persistence>

 

The name attribute of the persistence-unit element indicates the name of our persistence unit and is the string that we specify when we create the EntityManagerFactory. We have used “EmployeeService” as the name. The transaction-type attribute indicates that our persistence unit uses resource-level EntityTransaction instead of JTA transactions. The class element lists the entity that is part of the persistence unit. Multiple class elements can be specified when there is more than one entity. They would not normally be needed when deploying in a Java EE container because the container will scan for entities automatically as part of the deployment process, but they are needed for portable execution when running in Java SE. We have only a single Employee entity. 

 

The last section is just a list of properties that can be standard or vendor-specific. The JDBC database login parameters must be specified when running in a Java SE environment to tell the provider what resource to connect to. Other provider properties, such as logging options, are vendorspecific and might also be useful.

 

Persistence Archive

The persistence artifacts are packaged in what we will loosely call a persistence archive. This is really just a JAR-formatted file that contains the persistence.xml file in the META-INF directory and normally the entity class files.

 

Because we are running as a simple Java SE application, all we have to do is put the application JAR, the persistence provider JARs, and the JPA JAR on the classpath when the program is executed.

 

Summary

 

This chapter discussed just enough of the basics of the Java Persistence API to develop and run a simple

application in a Java SE runtime.

 

We started out discussing the entity, how to define one, and how to turn an existing Java class into one. We discussed entity managers and how they are obtained and constructed in the Java SE environment.

 

The next step was to instantiate an entity instance and use the entity manager to persist it in the database. After we inserted some new entities, we could retrieve them again and then remove them. We also made some updates and ensured that the changes were written back to the database.

 

We talked about the resource-local transaction API and how to use it. We then went over some of the different types of queries and how to define and execute them. Finally, we aggregated all these techniques and combined them into a simple application that we can execute in isolation from an enterprise environment.

 

In the next chapter, we will look at the impact of the Java EE environment when developing enterprise applications using the Java Persistence API.

 

 

 

 

 

 

 

 

 

  • 大小: 22 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics