才不信你没考试呢,不定什么时候弄了两首英文诗,跑这里来招摇了
It is really true that I didn't enter for the examination
It is no for fun
my heart feels sad now
自我放逐。。。
Entity listeners are classes that can generically intercept entity callback events. They are not entity classes themselves, but they can be attached to an entity class through a binding annotation or XML. You can assign methods on an entity listener class to intercept a particular life cycle event. These methods return void and take one Object parameter that is the entity instance on which the event is being triggered. The method is annotated with the callback in which it is interested.
public class TitanAuditLogger { @PostPersist void postInsert(Object entity) { System.out.println("Inserted entity: " + entity.getClass().getName( )); } @PostLoad void postLoad(Object entity) { System.out.println("Loaded entity: " + entity.getClass().getName( )); } }
The entity listener class must have a public no-arg constructor. It can be applied to an entity class by using the @javax.persistence.EntityListeners annotation:
package javax.persistence; @Target(TYPE) @Retention(RUNTIME) public @interface EntityListeners { Class[] value( ); }
You can specify one or more entity listeners that intercept the callback events of an entity class:
@Entity @EntityListeners ({TitanAuditLogger.class, EntityJmxNotifier.class}) public class Cabin { ... @PostPersist void afterInsert( ) { ... } @PostLoad void afterLoading( ) { ... } }
By using the @EntityListeners annotation on the Cabin entity class, any callback methods within those entity listener classes will be invoked whenever Cabin entity instances interact with a persistence context. Entity listeners can also be applied using XML:
<entity class="com.titan.domain.Cabin"> <entity-listeners> <entity-listener class="com.titan.listeners.TitanAuditLogger"> </entity-listener> <entity-listener class="com.titan.listeners.EntityJmxNotifier"> <pre-persist name="beforeInsert"/> <post-load name="afterLoading"/> </entity-listener> </entity-listeners> </entity>
The <entity-listeners> element can be used within the declaration of an entity class. It lists the set of listeners that should be executed. Any entity listener class that doesn't use annotations to identify the callbacks can use the <post-persist> and other callback elements to specify these methods. Declaring <entity-listeners> also has the side effect of overriding any @EntityListeners annotation that might be applied to the entity class.
The execution order of entity listeners is the order they were declared in the @EntityListeners annotation or ORM XML mapping file. Any callbacks on the entity bean class itself are called last. Let's look at what would happen if we invoked the following operations:
1 EntityManager em = factory.createEntityManager( ); 2 em.getTransaction().begin( ); 3 4 Cabin cabin = new Cabin( ); 5 em.persist(cabin); 6 7 Cabin anotherCabin = em.find(Cabin.class, 5); 8 9 em.getTransaction().commit( );
Pretend this code executes on the examples we have shown in this chapter. The EntityJmxNotifier class is interested in a <pre-persist> callback. The EntityJmxNotifier.beforeInsert( ) method is executed when the EntityManager.persist( ) method is invoked at Line 5.
At Line 7, the TitanAuditLogger.postLoad( ), EntityJmxNotifier.afterLoading( ), and Cabin.afterLoading( ) methods are invoked, in that order, as the @PostLoad event is triggered by the EntityManager.find( ) invocation. The Cabin.afterLoading( ) method is invoked on the same instance returned by the find( ) method.
Our persistence provider has decided to delay the database insert of the newly persisted cabin until the transaction commits. In Line 9, the TitanAuditLogger.postPersist( ) and Cabin.afterInsert( ) methods are called, in that order. The Cabin.afterInsert( ) method is invoked on the same entity instance that we persisted in Line 5.
You can specify a set of default entity listeners that are applied to every entity class in the persistence unit by using the <entity-listeners> element under the top-level <entity-mappings> element in the ORM mapping file. For instance, if you wanted to apply the TitanAuditLogger to every entity class in a particular persistence unit, you would do the following:
<entity-mappings> <entity-listeners> <entity-listener class="com.titan.listeners.TitanAuditLogger"> <post-persist name="afterInsert"/> <post-load name="afterLoading"/> </entity-listener> <entity-listener class="com.titan.listeners.EntityJmxNotifier"/> </entity-listeners> </entity-mappings
If you want to turn off default entity listeners to a particular entity class, you can use the @javax.persistence.ExcludeDefaultListeners annotation:
@Entity @ExcludeDefaultListeners public class Cabin { ... }
There is also an XML equivalent of this annotation:
<entity class="com.titan.domain.Cabin"> <exclude-default-listeners/> </entity
If either the @ExcludeDefaultListeners annotation or its XML equivalent are applied to the Cabin entity, the TitanAuditLogger is turned off for that entity.
If you have an inheritance entity hierarchy in which the base class has entity listeners applied to it, any subclass will inherit these entity listeners. If the subclass also has entity listeners applied to it, then both the base and the subclass's listeners will be attached.
@Entity @EntityListeners(TitanAuditLogger.class) public class Person { @PostPersist void anotherCallback( ) { ... } } @Entity @EntityListeners(EntityJmxNotifier.class) public class Customer extends Person { ... }
In this example, the TitanAuditLogger entity listener and EntityJmxNotifier will be attached to the Customer entity. If all of these listeners have an @PostPersist callback, the order of callback execution will be as follows:
TitanAuditLogger's @PostPersist method
EntityJmxNotifier's @PostPersist method
Person's @PostPersist anotherCallback( ) method
Entity listeners applied to a base class happen before any listeners attached to a subclass. Callback methods defined directly in an entity class happen last.
You can turn off inherited entity listeners by using @javax.persistence.ExcludeSuperclassListeners :
@Entity @EntityListeners(TitanAuditLogger.class) public class Person { } @Entity @EntityListeners(EntityJmxNotifier.class) @ExcludeSuperclassListeners public class Customer extends Person { ... }
In this example, only the EntityJmxNotifier listener would be executed for Customer entity instances. @ExcludeSuperclassListeners has an XML equivalent in the <exclude-superclass-listeners/> element.
Entity beans provide an object-oriented model that makes it easier for developers to create, modify, and delete data from the database. They allow developers to be more productive by encouraging reuse, thus reducing development costs. For example, once a bean has been defined to represent a concept like a ship, that bean can be reused throughout a business system without redefining, recoding, or retesting the business logic and data access.
However, entity beans are not the entire story. We have seen another kind of enterprise bean: the session bean. Session beans fill the gaps left by entity beans. They are useful for describing interactions between other beans (taskflow) and for implementing particular tasks. Unlike entity beans, session beans do not represent data in the database, but they can access data. This means that we can use session beans to read, update, and insert data in a business process. For example, we might use a session bean to provide lists of information, such as a list of all available cabins. Sometimes we might generate the list by interacting with entity beans, like the cabin list we developed in the TravelAgent EJB in Chapter 4.
When do you use an entity bean and when do you use a session bean? As a rule of thumb, an entity bean should provide a safe and consistent interface to a set of shared data that defines a concept. This data may be updated frequently. Session beans access data that spans concepts, is not shared, and is usually read-only. Session beans contain business logic and entity beans model persistent data.
In addition to accessing data directly, session beans can represent taskflow. Taskflow refers to all the steps required to accomplish a particular task, such as booking passage on a ship or renting a video. Session beans frequently manage the interactions among entity beans, describing how they work together to accomplish a specific task. The relationship between session beans and entity beans is like the relationship between a script for a play and the actors that perform the play. Actors are pointless without a script; they may represent something, but they can't tell a story. Similarly, entities represented in a database aren't meaningful unless you can create interactions between the entities. It makes no sense to have a database full of cabins, ships, customers, and such if we can't create interactions between them, such as booking a customer for a cruise.
Session beans are divided into two basic types: stateless and stateful. A stateless session bean is a collection of related services, each represented by a method; the bean maintains no state from one method invocation to the next. When you invoke a method on a stateless session bean, it executes the method and returns the result without knowing or caring what other requests have gone before or might follow. Think of a stateless session bean as a set of procedures or batch programs that execute a request based on some parameters and return a result.
A stateful session bean is an extension of the client application. It performs tasks on behalf of a client and maintains state related to that client. This state is called conversational state because it represents a continuing conversation between the stateful session bean and the client. Methods invoked on a stateful session bean can write and read data to and from this conversational state, which is shared among all methods in the bean. Stateful session beans tend to be specific to one scenario. They represent logic that might have been captured in the client application of a two-tier system.
Depending on the vendor, stateful session beans may have a timeout period. If the client fails to use the stateful bean before it times out, the bean instance is destroyed and the EJB object reference is invalidated. This prevents the stateful session bean from lingering long after a client has shut down or otherwise has finished using it. After all, clients can crash, and users can walk away from their desks and forget what they were doing; we don't want stateful session beans associated with dead clients or forgetful users cluttering up our server forever. A client can also explicitly remove a stateful session bean by calling one of its remove methods.
Stateless session beans have longer lives because they do not retain any conversational state and are not dedicated to one client. As soon as a stateless session bean has finished a method invocation, it can be reassigned to service a new client. Stateless session beans may also have a timeout period and can be removed by the client, but the impact of a bean timeout or removal is different than with a stateful session bean. A timeout or remove operation simply invalidates the EJB object reference for that client; the bean instance is not destroyed and is free to service other client requests.
Whether they are stateful or stateless, session beans are not persistent, like entity beans are. In other words, session beans don't represent persistent data and are not saved to the database.
A stateless session bean is very efficient and relatively easy to develop. A session bean can be swapped freely between EJB objects because it isn't dedicated to one client and doesn't maintain any conversational state. As soon as it is finished servicing a method invocation it can be swapped to another EJB object. Because it does not maintain conversational state, a stateless session bean does not require passivation or activation, further reducing the overhead of swapping. In short, stateless session beans are lightweight and fast.
Saying that a stateless session bean doesn't maintain any conversational state means that every method invocation is independent of previous invocations and everything the method needs to know has to be passed via the method's parameters. Since stateless session beans can't remember anything from one method invocation to the next, they must take care of an entire task in one method invocation. The only exception to this rule is information obtainable from the SessionContext and the JNDI ENC, or environment references that are injected directly into the bean (we'll talk later about dependency injection). Stateless session beans are EJB's version of the traditional transaction-processing applications, which are executed using a procedure call. The procedure executes from beginning to end and then returns the result. Once the procedure finishes, nothing about the data that was manipulated or the details of the request is remembered.
These restrictions don't mean that a stateless session bean can't have instance variables or maintain any kind of internal state. Nothing prevents you from keeping a variable that tracks the number of times a bean has been called or that saves data for debugging. An instance variable can even hold a reference to a live resource, such as a URL connection for logging, verifying credit cards through a different EJB, or anything else that might be usefulthe resource should be obtained from the JNDI ENC or be injected into fields directly using EJB's injection features. However, it is important to remember that this state can never be visible to a client. A client can't assume that the same bean instance will service all of its requests. Instance variables may have different values in different bean instances, so their values can appear to change randomly as stateless session beans are swapped from one client to another. Therefore, anything you reference in instance variables should be generic. For example, each bean instance might reasonably record debugging messagesthat might be the only way to figure out what is happening on a large server with many bean instances. The client doesn't know or care where debugging output is going. However, it would clearly be inappropriate for a stateless bean to remember that it was in the process of making a reservation for Madame Xthe next time it is called, it may be servicing another client entirely.
Stateless session beans can be used for report generation, batch processing, or some stateless services such as validating credit cards. Another good application might be a StockQuote EJB that returns a stock's current price. Any activity that can be accomplished in one method call is a good candidate for the high-performance stateless session bean.
Chapters 2 and 3 discussed the TravelAgent EJB, which has a business method called bookPassage( ) that uses the ProcessPayment EJB. The next section develops a complete definition of the TravelAgent EJB, including the logic of the bookPassage( ) method. At this point, however, we are primarily interested in the ProcessPayment EJB, which is a stateless bean the TravelAgent EJB uses to charge the customer for the price of the cruise. Charging customers is a common activity in Titan's business systems. Not only does the reservation system need to charge customers, so do Titan's gift shops, boutiques, and other related businesses. Because many different systems charge customers for services, we've encapsulated the logic for charging customers in its own bean.
Payments are recorded in a special database table called PAYMENT. The PAYMENT data is batch-processed for accounting purposes and is not normally used outside of accounting. In other words, the data is only inserted by Titan's system; it is not read, updated, or deleted. Because the process of making a charge can be completed in one method, and because the data is not updated frequently and is not shared, we will use a stateless session bean for processing payments. Several different forms of payment can be used: credit card, check, and cash. We will model these payment forms in our stateless ProcessPayment EJB.
The ProcessPayment EJB accesses an existing table in Titan's system, called the PAYMENT table. In your database, create a table called PAYMENT, with this definition:
CREATE TABLE PAYMENT ( customer_id INTEGER, amount DECIMAL(8,2), type CHAR(10), check_bar_code CHAR(50), check_number INTEGER, credit_number CHAR(20), credit_exp_date DATE )
A stateless session bean has one or more business interfaces. The business interface for ProcessPayment obviously needs a byCredit( ) method because the TravelAgent EJB uses it. We can also identify two other methods that we'll need: byCash( ) for customers paying cash and byCheck( ) for customers paying with a personal check.
A business interface can be a remote or local interface, but not both. Remote business interfaces are able to receive method invocations from networked clients. When a client invokes a method on a session bean's remote interface, the parameter values and return value are copied. This is true irregardless of whether the client is running in the same VM or on another machine in the network. This is known as call-by-value semantics.
Local interfaces are available only within the same JVM as the session bean. Invoking on a local interface does not copy the parameters or return value. Because of this, local interfaces are said to follow what is termed call-by-reference semantics.
Since the TravelAgent EJB will be in the same deployment, the ProcessPayment EJB should provide a local interface so that invocations on ProcessPayment are efficient. The ProcessPayment EJB also has remote clients, so we'll provide a remote business interface as well.
The local and remote interfaces will publish the same API. To make our design a little bit cleaner, we will have these interfaces extend a base interface called com.titan.processpayment.ProcessPayment :
package com.titan.processpayment; import com.titan.domain.*; public interface ProcessPayment { public boolean byCheck(Customer customer, CheckDO check, double amount) throws PaymentException; public boolean byCash(Customer customer, double amount) throws PaymentException; public boolean byCredit(Customer customer, CreditCardDO card, double amount) throws PaymentException; }
The EJB specification allows you to define a common base class for your remote and local interfaces if they share the same methods. Three business methods have been defined: byCheck( ), byCash( ), and byCredit( ), which take information relevant to the form of payment used and return a boolean value that indicates whether the payment succeeded. These methods can throw application-specific exceptions, like PaymentException. PaymentException is thrown if any problems occur while processing the payment, such as a low check number or an expired credit card. Notice, however, that nothing about the ProcessPayment interface is specific to the reservation system. It could be used just about anywhere in Titan's system. In addition, each method defined in the base business interface is completely independent of the others. All the data that is required to process a payment is obtained through the method's arguments. Next, let's specify both the remote and local interfaces:
package com.titan.processpayment; import javax.ejb.Remote; @Remote public interface ProcessPaymentRemote extends ProcessPayment { } package com.titan.processpayment; import javax.ejb.Local; @Local public interface ProcessPaymentLocal extends ProcessPayment{ }
The ProcessPaymentRemote and ProcessPaymentLocal interfaces extend the base ProcessPayment interface so that they do not have to duplicate the method definitions. ProcessPaymentRemote is identified as a remote interface by the @javax.ejb.Remote annotation and ProcessPaymentLocal by the @javax.ejb.Local annotation.
Each method of the ProcessPayment EJB's business interface takes a Customer entity bean as a parameter. Because entity beans are plain Java objects, they can be serialized across the network as plain Java objects as long as they implement java.io.Serializable or Externalizable . This is important because the ProcessPayment EJB accesses the internal state of the Customer entity. If every call to the Customer's get methods went over a remote interface (as is required in the EJB 2.1 specification), the ProcessPayment EJB would be very inefficient, because network calls are expensive. This is yet another example of the simplicity of the EJB 3.0 specification. In EJB 2.1, because entities were first-class components, one had to write parallel value object classes if one wanted to pass around the state of an entity instead of accessing it through the old remote interface model.
The ProcessPayment EJB's business interface uses two classes that are particularly interesting, CreditCardDO and CheckDO:
/* CreditCardDO.java */ package com.titan.processpayment; import java.util.Date; public class CreditCardDO implements java.io.Serializable { final static public String MASTER_CARD = "MASTER_CARD"; final static public String VISA = "VISA"; final static public String AMERICAN_EXPRESS = "AMERICAN_EXPRESS"; final static public String DISCOVER = "DISCOVER"; final static public String DINERS_CARD = "DINERS_CLUB_CARD"; public String number; public Date expiration; public String type; public CreditCardDO (String nmbr, Date exp, String typ) { number = nmbr; expiration = exp; type = typ; } } /* CheckDO.java */ package com.titan.processpayment; public class CheckDO implements java.io.Serializable { public String checkBarCode; public int checkNumber; public CheckDO(String barCode, int number) { checkBarCode = barCode; checkNumber = number; } }
CreditCardDO and CheckDO are domain objects . They are simply serializable Java classes, not enterprise beans; they provide a convenient mechanism for transporting related data. CreditCardDO, for example, collects all the credit card data together in one class, making it easier to pass the information across the network as well as making our interfaces a little cleaner.
Any remote or local interface can throw application exceptions. Application exceptions should describe a business logic problemin this case, a problem making a payment. Application exceptions should be meaningful to the client, providing a brief and relevant identification of the error.
It is important to understand what exceptions you should use, and when you should use them. Exceptions such as javax.naming.NamingException and java.sql.SQLException are thrown by other Java subsystems and have nothing to do with the business process an EJB is supposed to be modeling. These types of Java subsystem exceptions expose the implementation of your session bean. Another thing (which you will learn in Chapter 16) is that checked exceptions (non-RuntimeException s), by default, do not cause a transaction rollback. Instead of throwing these types of exceptions directly, you should catch them in a try/catch block and throw a more appropriate exception. A common practice is to wrap these checked exceptions in a javax.ejb.EJBException , as these types of subsystem errors are unrecoverable.
An EJBException indicates that the container ran into problems processing a business interface invocation. EJBException is unchecked (it extends java.lang.RuntimeException ), so you won't get a compile error if you don't catch it. However, under certain circumstances, it is a good idea to catch EJBException, and in other circumstances, it should be propagated.
A PaymentException describes a specific business problem that is possibly recoverable. This makes it an application exception. The EJB container treats any exception that does not extend RuntimeException as an application exception. Here is the definition of PaymentException:
package com.titan.processpayment; public class PaymentException extends java.lang.Exception { public PaymentException( ) { super( ); } public PaymentException(String msg) { super(msg); } }
An application exception is propagated to the calling client as is. Any instance variables you include in these exceptions should be serializable. Nonapplication exceptions are always wrapped in an EJBException . This means that any exception you throw that is or extends RuntimeException will be caught by the EJB container and wrapped in an EJBException. This is especially important for session beans that interact with entity beans. All exceptions thrown by Java Persistence interfaces are RuntimeExceptions. Your client code must be aware of this if it needs to take action on specific persistence exceptions. Exception behavior can be declared explicitly using the @javax.ejb.ApplicationException and the <application-exception> XML deployment descriptor metadata. These constructs are discussed in detail in Chapter 16, as they have a huge impact on transactional behavior.