|
This lesson expands the Lesson 1 example to use an entity
Bean. The BonusServlet calls on the entity Bean to
save the social
security number and bonus information to and retrieve it from a
database table. This database access functionality adds the third
and final tier to the thin-client, multitiered example started
in Lesson 1.
The J2EE Reference Implementation comes with Cloudscape database, and
you need no additional setup to your environment for the entity Bean to
access it. In fact in this example, you do not write any SQL or
JDBC TM code to create the database
table or perform any database access operations. The table is
created and the SQL code generated with the Deployer tool during
assembly and deployment.
Lesson 2 Zip file
Create the Entity Bean
An entity Bean represents persistent data stored in one row of a
database table. When an entity Bean is created, the data is written
to the appropriate database table row, and if the data in an entity
Bean is updated, the data in the appropriate database table row is
also updated. The database table creation and row updates all occur
without your writing any SQL or JDBCTM code.
Entity Bean data is persistent because it survives crashes. If a
crash occurs while the data in an entity Bean is being updated,
the entity Bean data is automatically restored to the state of
the last committed database transaction. If the crash occurs in
the middle of a database transaction, the transaction is backed
out to prevent a partial commit from corrupting the data.
BonusHome
The main difference between the CalcHome session
Bean code and the BonusHome entity Bean code
shown below is the addition of the findByPrimaryKey
finder method. This method takes the primary key as a parameter,
which is a social security number. The primary key is used to
retrieve the table row with a primary key value that corresponds
to the social security number passed to this method.
The create method takes the bonus value and
primary key as parameters. When BonusServlet
instantiates the home interface and calls its create
method, the container creates a BonusBean instance
and calls its ejbCreate method. The BonusHome.create
and BonusBean.ejbCreate methodsmust have the same signatures,
so the bonus and primary key values are passed from the home
interface to the entity Bean by way of the entity Bean's container.
If a row for a given primary key (social security) number already exists,
a java.sql.RemoteException is thrown that is handled
in the BonusServlet client code.
package Beans;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.ejb.EJBHome;
public interface BonusHome extends EJBHome {
public Bonus create(double bonus, String socsec)
throws CreateException, RemoteException;
public Bonus findByPrimaryKey(String socsec)
throws FinderException, RemoteException;
}
Bonus
After the home interface is created, the container creates the remote
interface and entity Bean. The Bonus
interface declares the getBonus and getSocSec
methods so the servlet can retrieve data from the entity Bean.
package Beans;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Bonus extends EJBObject {
public double getBonus() throws RemoteException;
public String getSocSec() throws RemoteException;
}
BonusBean
BonusBean is a container managed entity Bean.
This means the container handles data persistence and transaction
management without your writing code to transfer data between the
entity Bean and the database or define transaction boundaries.
If for some reason you want the entity Bean to manage its own persistence
or transactions, you would provide implementations for some of the empty
methods shown in the BonusBean code below. The following links
take you to documents that describe Bean-managed persistence and transactions,
and a later lesson in this tutorial will cover Bean-managed transactions.
- Chapter 3 of the Writing Advanced Applications tutorial.
- Chapter 4
of the Enterprise JavaBeansTM Developer's Guide.
When BonusServlet calls BonusHome.create,
the container calls the BonusBean.setEntityContext method.
The EntityContext instance passed to the
setEntityContext method has
methods that let the Bean return a reference to itself or get its primary
key.
Next, the container calls the ejbCreate method. The
ejbCreate method assigns data to the Bean's instance
variables, and then the container writes that data to the database.
The ejbPostCreate method is called after the
ejbCreate method and performs any processing
needed after the Bean is created. This simple example
does no post-create processing.
The rest of the empty methods are callback methods called by
the container to notify the Bean that some event is about to
occur. You would provide behavior for some of these methods if
you are using Bean-managed persistence, and others if you
need to provide Bean-specific cleanup or initialization operations.
These cleanup and initialization operations take place at specific
times during the Bean's lifecycle, and the container notifies the
Bean and calls the applicable method at the appropriate time.
Here is a brief description of the empty methods:
- The
ejbPassivate and ejbActivate
methods are called by the container before the container swaps
the Bean in and out of storage. This process is similar to the
virtual-memory concept of swapping a memory page between memory
and disk.
- The container calls the
ejbRemove method
if the home interface has a corresponding remove
method that gets called by the client.
- The
ejbLoad and ejbStore methods
are called by the container before the container synchronizes
the Bean's state with the underlying database.
The getBonus and getSocSec methods are
called by clients to retrieve data stored in the instance variables.
This example has no setXXX methods, but if it did,
clients would call them to change the data in the Bean's instance
variables. Any changes to the instance variables result in an update
to the table row in the underlying database.
package Beans;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
public class BonusBean implements EntityBean {
public double bonus;
public String socsec;
private EntityContext ctx;
public double getBonus() {
return this.bonus;
}
public String getSocSec() {
return this.socsec;
}
public String ejbCreate(double bonus,
String socsec)
throws CreateException{
//Called by container after setEntityContext
this.socsec=socsec;
this.bonus=bonus;
return null;
}
public void ejbPostCreate(double bonus,
String socsec) {
//Called by container after ejbCreate
}
//These next methods are callback methods that
//are called by the container to notify the
//Bean some event is about to occur
public void ejbActivate() {
//Called by container before Bean
//swapped into memory
}
public void ejbPassivate() {
//Called by container before
//Bean swapped into storage
}
public void ejbRemove() throws RemoteException {
//Called by container before
//data removed from database
}
public void ejbLoad() {
//Called by container to
//refresh entity Bean's state
}
public void ejbStore() {
//Called by container to save
//Bean's state to database
}
public void setEntityContext(EntityContext ctx){
//Called by container to set Bean context
}
public void unsetEntityContext(){
//Called by container to unset Bean context
}
}
Change the Servlet
The BonusServlet
program is very similar to the Lesson 1 version with changes
in the init and doGet methods. The
init method for this lesson looks up both the
CalcBean session Bean, and the BonusBean
entity Bean.
public void init(ServletConfig config)
throws ServletException{
try {
InitialContext ctx = new InitialContext();
Object objref = ctx.lookup("bonus");
Object objref2 = ctx.lookup("calcs");
homebonus=(BonusHome)PortableRemoteObject.narrow(
objref, BonusHome.class);
homecalc=(CalcHome)PortableRemoteObject.narrow(
objref2, CalcHome.class);
} catch (Exception NamingException) {
NamingException.printStackTrace();
}
}
The try statement in the doGet method
creates the CalcBean and BonusBean
home interfaces. After calling calBonus to
calculate the bonus, the BonusHome.create
method is called to create an entity Bean instance and a corresponding
row in the underlying database table. After creating the table,
the BonusHome.findByPrimaryKey method is called
to retrieve the same record by its primary key (social security
number). Next, an HTML page is returned to the browser
showing the data originally passed in, the calculated bonus, and
the data retrieved from the database table row.
The catch statement catches and handles duplicate
primary key values (social security numbers). The underlying
database table cannot have two rows with the same primary key, so
if you pass in the same social security number, the servlet catches
and handles the error before trying to create the entity Bean. In the
event of a duplicate key, the servlet returns an HTML page the original
data passed in, the calculated bonus, and a duplicate key error message.
try {
//Calculate bonus
double bonus = 100.00;
theCalculation = homecalc.create();
calc = theCalculation.calcBonus(multiplier, bonus);
try {
//Create row in table
theBonus = homebonus.create(calc, socsec);
record = homebonus.findByPrimaryKey(socsec);
//Display data
out.println("<H1>Bonus Calculation</H1>");
out.println("<P>Soc Sec passed in: " +
theBonus.getSocSec() + "<P>");
out.println("<P>Multiplier passed in: " +
multiplier + "<P>");
out.println("<P>Bonus Amount calculated: " +
theBonus.getBonus() + "<P>");
out.println("<P>Soc Sec retrieved: " +
record.getSocSec() + "<P>");
out.println("<P>Bonus Amount retrieved: " +
record.getBonus() + "<P>");
out.println("</BODY></HTML>");
//Catch duplicate key error
//Remote Exception is thrown by BonusHome
} catch (java.rmi.RemoteException e) {
String message = e.getMessage();
//Display data
out.println("<H1>Bonus Calculation</H1>");
out.println("<P>Soc Sec passed in: " +
socsec + "<P>");
out.println("<P>Multiplier passed in: " +
multiplier + "<P>");
out.println("<P>Bonus Amount calculated: " +
calc + "<P>");
out.println("<P>" + message + "<P>");
out.println("</BODY></HTML>");
}
} catch (Exception CreateException) {
CreateException.printStackTrace();
}
}
Compile
First, compile the entity Bean and servlet. Refer to Lesson 1
for path and classpath settings, and information on where to
place the source files.
Compile the Entity Bean
Unix:
#!/bin/sh
cd /home/monicap/J2EE
J2EE_HOME=/export/home/monicap/J2EE/j2sdkee-beta
CPATH=.:$J2EE_HOME/lib/j2ee.jar
javac -d . -classpath "$CPATH" Beans/BonusBean.java
Beans/BonusHome.java Beans/Bonus.java
Windows:
cd \home\monicap\J2EE
set J2EE_HOME=\export\home\monicap\J2EE\j2sdkee-beta
set CPATH=.;%J2EE_HOME%\lib\j2ee.jar
javac -d . -classpath %CPATH% Beans/BonusBean.java
Beans/BonusHome.java Beans/Bonus.java
Compile the Servlet
Unix:
cd /home/monicap/J2EE/ClientCode
J2EE_HOME=/export/home/monicap/J2EE/j2sdkee-beta
CPATH=.:$J2EE_HOME/lib/j2ee.jar:/export/home/monicap/J2EE:
/export/home/monicap/J2EE/Beans
javac -d . -classpath "$CPATH" BonusServlet.java
Windows:
cd \home\monicap\J2EE\ClientCode
J2EE_HOME=\export\home\monicap\J2EE\j2sdkee-beta
CPATH=.;%J2EE_HOME%\lib\j2ee.jar:\export\home\monicap\J2EE:
\export\home\monicap\J2EE/Beans
javac -d . -classpath %CPATH% BonusServlet.java
Start the Platform and Tools
To run this example, you need to start the J2EE server,
the Deployer tool, and Cloudscape database.
In different windows, type the following commands:
j2ee -verbose
deploytool
cloudscape -start
If that does not work, type this from the J2EE
directory:
Unix:
j2sdkee-beta/bin/j2ee -verbose
j2sdkee-beta/bin/deploytool
j2sdkee-beta/bin/cloudscape -start
Windows:
j2sdkee-beta\bin\j2ee -verbose
j2sdkee-beta\bin\deploytool
j2sdkee-beta\bin\cloudscape -start
Assemble and Deploy
Before you can redeploy the J2EE application with the
changes for this lesson, you have to uninstall the
Lesson 1 J2EE application. You can do this any time
before you deploy, but just to make sure you do not
forget, do it now.
Uninstall the Application
At the bottom of the Deployer tool is a
window listing BonusApp with
an Uninstall button to the
right. Click Uninstall.
Delete and Create New WAR File
The web archive (WAR) file contains BonusServlet and
bonus.html. Because you have changed BonusServlet,
you have to delete the WAR file from the application, recreate it
with the new BonusServlet, and add the new WAR
file back to the J2EE application.
Click the BonusApp application in the
Local Applications window so you can see its application
components. Select BonusWar so it is highlighted.
At the top of the Deployer tool, there is an Edit
menu. Select Delete.
Now, go through the steps to create the WAR file. These steps
are outlined in Lesson 1 and summarized below:
- Select
Application.New Web Component from the menus at the top.
- Step 1 of 15: Read and Click
Next
- Step 2 of 15: Specify the
ClientCode directory,
Click Add, and select
BonusServlet.class in the top window and bonus.html
in the bottom window.
- Step 3 of 15: Name the WAR file Bonus.war with a display name of
BonusWar.
- Skip Step 4, 5, 6, 7, and 8 of 15.
- Step 9 of 15: Make sure
Describe a servlet is selected.
- Step 10 of 15: Make the servlet class and display name
BonusServlet.
- Skip Step 11, 12, and 13 of 15.
- Step 14 of 15: Create the WAR now.
- Step 15 of 15: Read and click
Done.
- Add the WAR now.
- In the
Inspecting window, select Web Context and specify
BonusRoot.
Create EJB JAR for Entity Bean
The steps to creating the EJB JAR for the entity Bean
are very similar to the steps for the session Bean
covered in Lesson 1. There are a few differences, however,
and those differences are explained here.
Note:
In this lesson, the entity Bean is placed in a separate
JAR file from the session Bean as an easy way to continue
the example from Lesson 1 with the least number of changes.
Because these Beans have related functionality, however,
you could bundle and deploy them in the same JAR file.
You will see how to bundle related Beans in the same JAR
file in Lesson 3.
- Select
Application.New EJB JAR from the menus at the top.
- Step 1 of 12: Read and click
Next.
- Step 2 of 12: Make sure the directory path points to
J2EE.
Step 3 of 12: Type: Beans.BonusBean,
Beans.BonusHome, and Beans.Bonus for
the class, home, and remote names. The display name is
BonusBean. Click Entity for the Bean type.
- Step 4 of 12: Select
Container managed persistence.
In the window below, check bonus and socsec.
The primary key class is
java.lang.String, and the primary key field name
is socsec. Note that the primary key has to be a class type.
Primitive types are not valid for primary keys.
- Skip Step 5, 6, 7, and 8 of 12.
- Step 9 of 12: Select
Transactions managed by the server.
In the list below make getBonus and getSocSec
required. This means the container starts a new transaction before
running these methods. The transaction commits just before the
methods end. You can find more information on these transaction
settings in
Chapter 6 of the Enterprise JavaBeans Developer's
Guide.
- Step 10 of 12: Select
Don't add another ejb descriptor.
- Step 11 of 12: In the
Location field, make the JAR file name
Bonus.jar, and give it BonusJar for the display name.
- Add the JAR file to the application now.
- In the
Local Applications window, select
BonusApp. In the Inspecting window, select
JNDI names, give BonusBean the JNDI name
of bonus, and press the Return key.
Before the J2EE application can be deployed, you need to specify deployment
settings for the entity Bean and generate the SQL. Here
is how to do it:
- In the Local Applications window, select
BonusBean.
- In the Inspecting window, select
Entity, and click the
Deployment Settings button to the lower right.
- In the Deployment Settings window, specify
jdbc/Cloudscape
(with a capital C on Cloudscape)
for the Database JNDI name, make sure the Create table on deploy and
Delete table on Deploy boxes are checked, and
click Generate SQL now.
- When the SQL generation completes, select the
findByPrimaryKey
method in the EJB method box. To the right a SQL statement appears.
It should read SELECT "socsec" FROM "BonusBeanTable" WHERE
"socsec"=?. The question mark (?) represents the parameter passed
to the findByPrimaryKey method.
- Click
OK.
Verify and Deploy the J2EE Application
Normally you would verify the J2EE application before deploying
it as was done in Lesson 1. But this Beta release has
bugs that cause the verification of a container-managed
entity Bean to generate false errors. So, rather than confuse
things by verifying the application and getting a lot of
errors on the entity Bean that you do not have to worry about,
the next step is to deploy the application.
- Select
Tools.Deploy Application from the menus at the top.
If you did not uninstall the application first, you are
prompted to do it now.
- Step 1: Click
Next.
- Step 2: JNDI name for
CalcBean is
calcs and for BonusBean is bonus.
If these do not display, type them in.
- Step 3: Context root is BonusRoot. If this does not
display, type it in.
- Click
Deploy.
Run the J2EE Application
The web server runs on port 8000 by default. To open the
bonus.html page point your browser to
http://localhost:8000/BonusRoot/bonus.html, which is
where the deploy tool put the HTML file.
Fill in a social security number and multiplier, and click the
Submit button. BonusServlet
processes your data and returns an HTML page with the bonus
calculation on it.
Bonus Calculation
Soc Sec passed in: 777777777
Multiplier passed in: 25
Bonus Amount calculated: 2500.0
Soc Sec retrieved: 7777777777
Bonus Amount retrieved: 2500.0
If you go back to bonus.html and change
the multiplier to 2, but use the same social security
number, you see this:
Bonus Calculation
Soc Sec passed in: 777777777
Multiplier passed in: 2
Bonus Amount calculated: 200.0
RemoteException occurred in server thread; nested
exception is: java.rmi.RemoteException: ERROR in
database INSERT ; nested exception is: SQL Exception:
The statement was aborted because it would have caused
a duplicate key value in a unique or primary key constraint.
[TOP]
|