CPAN423 Enterprise Java Programming
Lecture #8: Introduction to EJB-Entity Beans
Entity beans are objects that represent data in the database tier of an enterprise Java application. An entity bean instance corresponds to a row of data in a database table, and is identified by its primary key
The entity bean has three states: Does not exist, Pooled and Ready. The entity bean transition from Does Not Exist state to Pooled state after the container creates an instance of this bean. The container does that by calling the methods Class.newInstance and setEntityContext in sequence. In the pooled state, all the bean instances are identical.
The entity bean transition from Pooled state to Ready state when the client either inserts a new data or finds an existing data. To insert a new data the client call create method with the proper arguments. This cases the container call ejbCreate and ejbPostCreate methods in sequence. To read data from the database, the client calls one of the finder methods of the bean ( findByPrimaryKey, findByName, .. etc). If the bean is in the pooled state the container activate the bean by calling ejbActivate method. If the bean is already in ready state, the container provides the data immediately.
The entity bean transition from Ready state to Pooled state either when the client deletes data from the database by calling remove method or when the container needs to free resources used by the entity bean by calling ejbPassivate method. The container moves the bean from Pooled to Does Not Exist by calling unsetEntityContext method.
Transactions are managed with entity beans using container-managed transaction only. The container handles the transaction and rollback if the transaction failed. Otherwise it commits this transaction. Managing transaction become important when multiple clients may access the same entity bean instance.
Persistence with Entity beans can be maintained either using Container Managed Persistence, or Bean Managed Persistence..
With BMP the developer has to provide all the underling code to access the database. With CMP the container generates the required code for database access. BMP allows the user to have more control over the database access. CMP is more portable and faster to develop.
Beans can have relationship with each other based on the abstract persistence schema that defines the beans’ fields and their relationship. When BMP is used to maintain persistence, the developer has to specify how the relationship is represented.
If CMP is used, the container construct the proper relationship based on the abstract schema description provided. The developer should use EJB query language to describe the abstract schema and the relationship between the entity beans.
Local interface and local home interface are required for CMP entity beans with relationship.
Examples:
Ex1: CMP Entity bean example with a servlet as a client:
In this example we will maintain the student state using a Container Managed Persistence Entity Bean. We will create a local interface called Student . This interface defines the database related business methodsthat will be available to the clients.
package school;
import javax.ejb.*;
public interface Student extends EJBLocalObject
{
Integer getStudentId() ;
void setStudentName(String name);
String getStudentName();
void setStudentAddress(String address);
String getStudentAddress();
}
Then we will write a Local Home interface called StudentHome. This interface defines the database access methods:
package school;
import javax.ejb.*;
import java.util.Collection;
public interface StudentHome extends EJBLocalHome {
Student create(Integer studentId,String studentName,String studentAddress) throws CreateException;
Student create (Integer studentId)throws CreateException;
Student findByPrimaryKey(Integer studentId)
throws FinderException;
Collection findAll()
throws FinderException;
Student findByName(String studentName)
throws FinderException;
}
Then we will provide an implementation class called StudentBean. Since we are using CMP, abstract access methods must be provided for each table’s field. This also means that the class itself must be declared an abstract. The EJB container will generate the required persistence code based on these methods.
package school;
import javax.ejb.*;
import javax.naming.*;
import java.rmi.*;
import javax.sql.DataSource;
import java.io.Serializable;
import java.util.Enumeration;
import javax.ejb.*;
public abstract class StudentBean implements EntityBean {
public abstract Integer getStudentId();
public abstract void setStudentId(Integer id);
public abstract String getStudentName();
public abstract void setStudentName(String studentName);
public abstract String getStudentAddress();
public abstract void setStudentAddress(String address);
public Integer ejbCreate(Integer studentId,String studentName,String studentAddress)
throws CreateException
{
setStudentId(studentId);
setStudentName(studentName);
setStudentAddress(studentAddress);
return studentId;
}
public void ejbPostCreate(Integer studentId,String studentName,String studentAddress)
throws CreateException
{
}
public Integer ejbCreate(Integer studentId)
throws CreateException
{
setStudentId(studentId);
return studentId;
}
public void ejbPostCreate(Integer studentId)
throws CreateException
{
}
public void setEntityContext(EntityContext e){}
public void unsetEntityContext(){}
public void ejbActivate(){}
public void ejbPassivate(){}
public void ejbRemove() throws RemoveException{}
public void ejbLoad(){}
public void ejbStore(){}
}
The following is a Servlet called insertServlet that obtain a reference to the Student object, and insert a new student to the database:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.naming.*;
import javax.ejb.*;
import java.util.*;
import school.*;
import javax.rmi.*;
import java.sql.*;
import javax.sql.*;
public class insertServlet extends HttpServlet {
private StudentHome home = null;
public void setStudentHome(StudentHome h)
{
home = h;
}
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException
{
PrintWriter out = res.getWriter();
try
{
res.setContentType("text/html");
out.println("<h1 align='center'>current Student Information</h1>");
String name=req.getParameter("userName");
String address=req.getParameter("userAddress");
// get a new student id
int st_id=findIDfromSequence();
Student st1=home.create(new Integer(st_id+1),name,address);
Student student=null;
Collection c = home.findAll();
Iterator iter = c.iterator();
out.println("<table border='1'<tr<th>Student Id</th<th>Student Name</th<th>Student Addreess</th</tr>");
while (iter.hasNext()) {
student = (Student) iter.next();
out.println("<tr<td>" + student.getStudentId() + "</td>");
out.println("<td>" + student.getStudentName() + "</td>");
out.println("<td>" + student.getStudentAddress() + "</td</tr>");
}
out.println("</table>");
}
catch (FinderException e) {
throw new ServletException(e);
}
catch(CreateException ce)
{
out.println(ce.toString());
}
}
/*
This function query the next value from the sequence st_seq to return the next
available value for the student id.
*/
public int findIDfromSequence()
{
int id=0;
try {
Context ic = (Context) new InitialContext();
DataSource ds = (DataSource) ic.lookup("java:comp/env/jdbc/test");
Connection con=ds.getConnection();
Statement stmt = con.createStatement();
ResultSet res = stmt.executeQuery(" select st_Seq.nextval from dual");
if(res.next())
{
id = res.getInt(1);
}
}
catch(SQLException ex)
{
throw new SQLException(ex.toString());
} finally
{
return id;
}
}
}
To make sure the we are using a unique ID for the student, we have create an Oracle sequence called st_Seq:
Create sequence st_Seq;
Before we create a new student record, we query this sequence for the next available value to make sure the student is assigned a unique id.
And here is an HTML file called insert.html that will be used to invoke the insertServlet:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"
<html xmlns="
<head>
<title>Insert Example</title>
</head>
<body>
<form method="get" action="
Enter Student Name: <input type="text" name="userName" size="20" /<br>
Enter Student Address: <input type="text" name="userAddress" size="10" /<br>
<input type="submit" value="Submit">
</form>
</html>
The following is a Servlet called updateServlet that obtain a reference to the Student object, and update an existing student record:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.naming.*;
import javax.ejb.*;
import java.util.*;
import school.*;
import javax.rmi.*;
public class updateServlet extends HttpServlet
{
private StudentHome home = null;
public void setStudentHome(StudentHome h)
{
home = h;
}
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException
{
PrintWriter out = res.getWriter();
try
{
res.setContentType("text/html");
out.println("<h1 align='center'>current Student Information</h1>");
String id=req.getParameter("userID");
String name=req.getParameter("userName");
String address=req.getParameter("userAddress");
Student st2=home.findByPrimaryKey(new Integer(id));
st2.setStudentName(name);
st2.setStudentAddress(address);
Collection c = home.findAll();
Iterator iter = c.iterator();
Student student=null;
while (iter.hasNext())
{
student = (Student) iter.next();
}
int st_id=student.getStudentId().intValue();
c = home.findAll();
iter = c.iterator();
out.println("<table border='1'<tr<th>Student Id</th<th>Student Name</th<th>Student Addreess</th</tr>");
while (iter.hasNext()) {
student = (Student) iter.next();
out.println("<tr<td>" + student.getStudentId() + "</td>");
out.println("<td>" + student.getStudentName() + "</td>");
out.println("<td>" + student.getStudentAddress() + "</td</tr>");
}
out.println("</table>");
}
catch (FinderException e) {
throw new ServletException(e);
}
}
}
And here is an HTML file called update.html that will be used to invoke the updateServlet:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"
<html xmlns="
<head>
<title>Update Example</title>
</head>
<body>
<form method="get" action="
Enter Student ID: <input type="text" name="userID" size="20"
<p>
Enter Student Name: <input type="text" name="userName" size="20" /<br>
Enter Student Address: <input type="text" name="userAddress" size="10" /<br>
<input type="submit" value="Submit">
</p>
</form>
</html>
The following is a Servlet called deleteServlet that obtain a reference to the Student object, and delete an existing student record:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.naming.*;
import javax.ejb.*;
import java.util.*;
import school.*;
import javax.rmi.*;
public class deleteServlet extends HttpServlet {
private StudentHome home = null;
public void setStudentHome(StudentHome h)
{
home = h;
}
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException
{
PrintWriter out = res.getWriter();
try
{
res.setContentType("text/html");
out.println("<h1 align='center'>current Student Information</h1>");
String id=req.getParameter("userID");
Student st2=home.findByPrimaryKey(new Integer(id));
st2.remove();
Collection c = home.findAll();
Iterator iter = c.iterator();
out.println("<table border='1'<tr<th>Student Id</th<th>Student Name</th<th>Student Addreess</th</tr>");
while (iter.hasNext()) {
Student student = (Student) iter.next();
out.println("<tr<td>" + student.getStudentId() + "</td>");
out.println("<td>" + student.getStudentName() + "</td>");
out.println("<td>" + student.getStudentAddress() + "</td</tr>");
}
out.println("</table>");
}
catch (FinderException e) {
throw new ServletException(e);
}
catch(RemoveException re)
{
out.println(re.toString());
}
}
}
And here is an HTML file called delete.html that will be used to invoke the deleteServlet:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"
<html xmlns="
<head>
<title>Delete Example</title>
</head>
<body>
<form method="get" action="
Enter Student ID: <input type="text" name="userID" size="20"
<p>
<br>
<input type="submit" value="Submit">
</p>
</form>
</html>
EJB applications are deployed under webapps folder of Resin web server. We will deploy our EJB under a folder called EntityEJB. The following steps illustrate the required steps:
- Create a folder called EntityEJB under webapps folder.
Any HTML or JSP file should be stored inEntityEJB folder.
- Under EntityEJB folder create a folder called WEB-INF. WEB-INF contain the web application deployment descriptor files: web.xml and one EJB descriptor file for each beans deployed underEntityEJB folder.
- Under WEB-INF create the following folders:
- classes: contains the servlet classes, the EJB classes and any other supporting classes. We will package our EJB classes into a folder called school.
- lib: Contain any JAR file used in the web application. JARs can contain EJB, servlets and other supporting classes.
The resulting structure should look as follow:
webapps
EntityEJB
WEB-INF
classes
school
lib
The files Student.java, StudentHome.java and StudentBean.java should be stored under school folder. insertServlet.java, updateServlet.java and deleteServlet.java should be stored under classes folder. insert.html, update.html, and delete.html should be stored under EntityEJB folder.
Following is an EJB descriptor file called school.ejb that should be stored under WEB-INF folder:
<?xml version="1.0"?>
<ejb-jar xmlns="
<enterprise-beans>
<entity>
<ejb-name>student</ejb-name>
<local-home>school.StudentHome</local-home>
<local>school.Student</local>
<ejb-class>school.StudentBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.Integer</prim-key-class>
<reentrant>True</reentrant>
<abstract-schema-name>students</abstract-schema-name>
<sql-table>students</sql-table>
<cmp-field>
<field-name>studentId</field-name>
</cmp-field>
<cmp-field>
<field-name>studentName</field-name>
<sql-column>student_name</sql-column>
</cmp-field>
<cmp-field>
<field-name>studentAddress</field-name>
<sql-column>student_address</sql-column>
</cmp-field>
<primkey-field>studentId</primkey-field>
<query>
<query-method>
<method-name>findAll</method-name>
</query-method>
<ejb-ql>
<![CDATA[SELECT o FROM students o]]>
</ejb-ql>
</query>
<query>
<query-method>
<method-name>findByName</method-name>
<method-params>
<method-param>
java.lang.String
</method-param>
</method-params>
</query-method>
<ejb-ql>
<![CDATA[SELECT o FROM students o where o.studentName= ?1]]>
</ejb-ql>
</query>
</entity>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>student</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
Following is the web application deployment descriptor for this application (web.xml) that should be stored under WEB-INF folder:
<web-app xmlns="
<database jndi-name="jdbc/test">
<driver type="oracle.jdbc.driver.OracleDriver">
<url>jdbc:oracle:thin:@munro.humber.ca:1521:msit</url>
<user>user</user>
<password>password</password>
</driver>
</database>
<ejb-server jndi-name="cmp">
<config-directory>WEB-INF</config-directory>
<data-source>jdbc/test</data-source>
<create-database-schema/>
</ejb-server>
<servlet servlet-name="insert"
servlet-class="insertServlet">
<init>
<student-home>${jndi:lookup("cmp/student")}</student-home>
</init>
</servlet>
<servlet-mapping url-pattern="/insert" servlet-name="insert"/>
<servlet servlet-name="update"
servlet-class="updateServlet">
<init>
<student-home>${jndi:lookup("cmp/student")}</student-home>
</init>
</servlet>
<servlet-mapping url-pattern="/update" servlet-name="update"/>
<servlet servlet-name="delete"
servlet-class="deleteServlet">
<init>
<student-home>${jndi:lookup("cmp/student")}</student-home>
</init>
</servlet>
<servlet-mapping url-pattern="/delete" servlet-name="delete"/>
</web-app>
You also have to create a table called student under Oracle server on MUNRO:
create table students (student_id number(6) not null primary key, student_name varchar2(60), student_address varchar2(60));
1