9 min read

Readers familiar with previous versions of J2EE will notice that Entity Beans were not mentioned in the above paragraph. In Java EE 5, Entity Beans have been deprecated in favor of the Java Persistence API (JPA). Entity Beans are still supported for backwards compatibility; however, the preferred way of doing Object Relational Mapping with Java EE 5 is through JPA. Refer to Chapter 4 in the book Java EE 5 Development using GlassFish Application Server for a detailed discussion on JPA.

Session Beans

As we previously mentioned, session beans typically encapsulate business logic. In Java EE 5, only two artifacts need to be created in order to create a session bean: the bean itself, and a business interface. These artifacts need to be decorated with the proper annotations to let the EJB container know they are session beans.

Previous versions of J2EE required application developers to create several artifacts in order to create a session bean. These artifacts included the bean itself, a local or remote interface (or both), a local home or a remote home interface (or both) and a deployment descriptor. As we shall see in this article, EJB development has been greatly simplified in Java EE 5.

Simple Session Bean

The following example illustrates a very simple session bean:

    package net.ensode.glassfishbook;
import javax.ejb.Stateless;
@Stateless
public class SimpleSessionBean implements SimpleSession
{
private String message =
"If you don't see this, it didn't work!";
public String getMessage()
{
return message;
}
}

The @Stateless annotation lets the EJB container know that this class is a stateless session bean. There are two types of session beans, stateless and stateful. Before we explain the difference between these two types of session beans, we need to clarify how an instance of an EJB is provided to an EJB client application.

When EJBs (both session beans and message-driven beans) are deployed, the EJB container creates a series of instances of each EJB. This is what is typically referred to as the EJB pool. When an EJB client application obtains an instance of an EJB, one of the instances in the pool is provided to this client application.

The difference between stateful and stateless session beans is that stateful session beans maintain conversational state with the client, where stateless session beans do not. In simple terms, what this means is that when an EJB client application obtains an instance of a stateful session bean, the same instance of the EJB is provided for each method invocation, therefore, it is safe to modify any instance variables on a stateful session bean, as they will retain their value for the next method call.

The EJB container may provide any instance of an EJB in the pool when an EJB client application requests an instance of a stateless session bean. As we are not guaranteed the same instance for every method call, values set to any instance variables in a stateless session bean may be “lost” (they are not really lost; the modification is in another instance of the EJB in the pool).

Other than being decorated with the @Stateless annotation, there is nothing special about this class. Notice that it implements an interface called SimpleSession. This interface is the bean’s business interface. The SimpleSession interface is shown next:

    package net.ensode.glassfishbook;
import javax.ejb.Remote;
@Remote
public interface SimpleSession
{
public String getMessage();
}

The only peculiar thing about this interface is that it is decorated with the @Remoteannotation. This annotation indicates that this is a remote business interface . What this means is that the interface may be in a different JVM than the client application invoking it. Remote business interfaces may even be invoked across the network.

Business interfaces may also be decorated with the @Local interface. This annotation indicates that the business interface is a local business interface. Local business interface implementations must be in the same JVM as the client application invoking their methods.

As remote business interfaces can be invoked either from the same JVM or from a different JVM than the client application, at first glance, we might be tempted to make all of our business interfaces remote. Before doing so, we must be aware of the fact that the flexibility provided by remote business interfaces comes with a performance penalty, because method invocations are made under the assumption that they will be made across the network. As a matter of fact, most typical Java EE application consist of web applications acting as client applications for EJBs; in this case, the client application and the EJB are running on the same JVM, therefore, local interfaces are used a lot more frequently than remote business interfaces.

Once we have compiled the session bean and its corresponding business interface,we need to place them in a JAR file and deploy them. Just as with WAR files, the easiest way to deploy an EJB JAR file is to copy it to [glassfish installationdirectory]/glassfish/domains/domain1/autodeploy.

Now that we have seen the session bean and its corresponding business interface, let’s take a look at a client sample application:

    package net.ensode.glassfishbook;
import javax.ejb.EJB;
public class SessionBeanClient
{
@EJB
private static SimpleSession simpleSession;
private void invokeSessionBeanMethods()
{
System.out.println(simpleSession.getMessage());
System.out.println("nSimpleSession is of type: "
+ simpleSession.getClass().getName());
}
public static void main(String[] args)
{
new SessionBeanClient().invokeSessionBeanMethods();
}
}

The above code simply declares an instance variable of type net.ensode.SimpleSession, which is the business interface for our session bean. The instance variable is decorated with the @EJB annotation; this annotation lets the EJB container know that this variable is a business interface for a session bean. The EJB container then injects an implementation of the business interface for the client code to use. As our client is a stand-alone application (as opposed to a Java EE artifact such as a WAR file) in order for it to be able to access code deployed in the server, it must be placed in a JAR file and executed through the appclient utility. This utility can be found at [glassfish installation directory]/glassfish/bin/. Assuming this path is in the PATH environment variable, and assuming we placed our client code in a JAR file called simplesessionbeanclient.jar, we would execute the above client code by typing the following command in the command line:

  appclient -client simplesessionbeanclient.jar

Executing the above command results in the following console output:

  If you don't see this, it didn't work!

SimpleSession is of type: net.ensode.glassfishbook._SimpleSession_Wrapper

which is the output of the SessionBeanClient class.

The first line of output is simply the return value of the getMessage() method we implemented in the session bean. The second line of output displays the fully qualified class name of the class implementing the business interface. Notice that the class name is not the fully qualified name of the session bean we wrote; instead, what is actually provided is an implementation of the business interface created behind the scenes by the EJB container.

A More Realistic Example

In the previous section, we saw a very simple, “Hello world” type of example. In this section, we will show a more realistic example. Session beans are frequently used as Data Access Objects (DAOs). Sometimes, they are used as a wrapper for JDBC calls, other times they are used to wrap calls to obtain or modify JPA entities. In this section, we will take the latter approach.

The following example illustrates how to implement the DAO design pattern in asession bean. Before looking at the bean implementation, let’s look at the business interface corresponding to it:

    package net.ensode.glassfishbook;
import javax.ejb.Remote;
@Remote
public interface CustomerDao
{
public void saveCustomer(Customer customer);
public Customer getCustomer(Long customerId);
public void deleteCustomer(Customer customer);
}

As we can see, the above is a remote interface implementing three methods; thesaveCustomer() method saves customer data to the database, the getCustomer()method obtains data for a customer from the database, and the deleteCustomer() method deletes customer data from the database. Let’s now take a look at the session bean implementing the above business interface. As we are about to see, there are some differences between the way JPA code is implemented in a session bean versus in a plain old Java object.

    package net.ensode.glassfishbook;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.sql.DataSource;
@Stateless
public class CustomerDaoBean implements CustomerDao
{
@PersistenceContext
private EntityManager entityManager;
@Resource(name = "jdbc/__CustomerDBPool")
private DataSource dataSource;
public void saveCustomer(Customer customer)
{
if (customer.getCustomerId() == null)
{
saveNewCustomer(customer);
}
else
{
updateCustomer(customer);
}
}
private void saveNewCustomer(Customer customer)
{
customer.setCustomerId(getNewCustomerId());
entityManager.persist(customer);
}
private void updateCustomer(Customer customer)
{
entityManager.merge(customer);
}
public Customer getCustomer(Long customerId)
{
Customer customer;
customer = entityManager.find(Customer.class, customerId);
return customer;
}
public void deleteCustomer(Customer customer)
{
entityManager.remove(customer);
}
private Long getNewCustomerId()
{
Connection connection;
Long newCustomerId = null;
try
{
connection = dataSource.getConnection();
PreparedStatement preparedStatement = connection
.prepareStatement(
"select max(customer_id)+1 as new_customer_id "
+ "from customers");
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet != null && resultSet.next())
{
newCustomerId = resultSet.getLong("new_customer_id");
}
connection.close();
}
catch (SQLException e)
{
e.printStackTrace();
}
return newCustomerId;
}
}

The first difference we should notice is that an instance of javax.persistence. EntityManager is directly injected into the session bean. In previous JPA examples,we had to inject an instance of javax.persistence.EntityManagerFactory, then use the injected EntityManagerFactory instance to obtain an instance of EntityManager.

The reason we had to do this was that our previous examples were not thread safe. What this means is that potentially the same code could be executed concurrently by more than one user. As EntityManager is not designed to be used concurrently by more than one thread, we used an EntityManagerFactory instance to provide each thread with its own instance of EntityManager. Since the EJB container assigns a session bean to a single client at time, session beans are inherently thread safe, therefore, we can inject an instance of EntityManager directly into a session bean.

The next difference between this session bean and previous JPA examples is that in previous examples, JPA calls were wrapped between calls to UserTransaction.begin() and UserTransaction.commit(). The reason we had to do this is because JPA calls are required to be in wrapped in a transaction, if they are not in a transaction, most JPA calls will throw a TransactionRequiredException. The reason we don’t have to explicitly wrap JPA calls in a transaction as in previous examples is because session bean methods are implicitly transactional; there is nothing we need to do to make them that way. This default behavior is what is known as Container-Managed Transactions. Container-Managed Transactions are discussed in detail later in this article.

When a JPA entity is retrieved in one transaction and updated in a different transaction, the EntityManager.merge() method needs to be invoked to update the data in the database. Invoking EntityManager.persist() in this case will result in a “Cannot persist detached object” exception.

LEAVE A REPLY

Please enter your comment!
Please enter your name here