Session Beans, Value Objects, and Data Access Objects

In this and in the sections that follow, we'll take a look at a session bean and an entity bean, and see how they both can be used for accessing information in a database. We'll also examine how other objects value objects and data access objects can help improve performance and reduce complexity.

Value Objects

One of the potential problems of using a remote object to perform database access is that EJBs often have getter and setter methods for each of their attributes. If these method calls are local, the overhead may be acceptable, but if it's over a network, this can generate a lot of network traffic. One way around this problem is to use a value object to encapsulate all the data and send all the data at once. This is similar to the approach we used in the previous chapter, where we used JavaBeans to pass data from a servlet to a JSP page.

One important difference between the JavaBean we used to pass data from a Java class to a JSP and a value object used to pass data from an EJB to a client application is that the value object must implement the Serializable interface. This is because the value object must be serialized before it can be transferred over the network.

Data Access Objects

Another potential problem is that multiple remote objects typically need to access the database; if we have duplicate code in multiple objects, changes will need to be made in many places if the database changes. This problem can be solved by providing a single object, a data access object (DAO) that simplifies and manages access to the database for remote objects.

Session Bean Example

In this example, we'll use a session bean to obtain CD information. Unlike the CMP entity bean in the last chapter where we were restricted to using a single row from a single table, we'll also include information about each of the songs on the CD.

To begin, use JDeveloper to create a new project, CDInfo, and a stateless session bean, also called CDInfo, following the steps outlined above. This will create the remote interface CDInfo.java, the home interface CDInfoHome.java, and the bean implementation class CDInfoBean.java.

We'll add a method to our session bean, CDInfoBean.java, to retrieve the CD information. This method will instantiate a data access object, CDInfoDAO, then will call it to obtain the data which is packaged in a value object, CDInfoModel.

 Package mypackage3.impl; import javax.ejb.SessionBean; import javax.ejb.SessionContext; import mypackage3.CDInfoModel; import mypackage3.CDInfoDAO; public class CDInfoBean implements SessionBean {   public CDInfoModel getCDInfoModel(long cdid)   {     CDInfoDAO cdDAO = new cdDAO();     return cdDAO.createCDInfoModel();   }   /* ...*/ } 

Notice that we must import the DAO and value object classes. We must also declare a corresponding method in our remote interface.

 package mypackage3; import javax.ejb.EJBObject; import java.rmi.RemoteException; import mypackage3.CDInfoModel; import mypackage3.CDInfoDAO; public interface CDInfo extends EJBObject {   public CDInfoModel getCDInfoModel(long cdid)      throws RemoteException; } 

These are the only changes we need to make in the EJB itself. Next, we will create the value object and DAO classes.

To create the value object class, CDInfoModel, right-click on the CDInfo.jpr project file and select New Class; then, in the screen that follows, enter the class name, CDInfoModel. The remaining defaults are okay. The class implements the Serializable interface and generally follows JavaBean conventions with getter and setter methods to provide public access to private instance variables.

 package mypackage3; import java.util.Vector; import java.io.Serializable; public class CDInfoModel implements Serializable {   private long cdid;   private String artistName;   private String title;   private String label;   private String releaseDate;   private Vector songs = new Vector();   public CDInfoModel()   {   }   // getters   public long getCdid()   {     return cdid;   }   public String getArtistName()   {     return artistName;   }   public String getTitle()   {     return title;   }   public String getLabel()   {     return label;   }   public String getReleaseDate()   {     return releaseDate;   }   public Vector getSongs()   {     return songs;   }   // setters   public void setCdid(long cdid)   {     this.cdid = cdid;   }   public void setArtistName(String artistName)   {     this.artistName = artistName;   }   public void setTitle(String title)   {     this.title = title;   }   public void setLabel(String label)   {     this.label = label;   }   public void setReleaseDate(String releaseDate)   {     this.releaseDate = releaseDate;   }   public void setSongs(Vector songs)   {     this.songs = songs;   } } 

Next, we create a class for storing the information for each song.

 package mypackage3; import java.io.Serializable; public class CDSong implements Serializable {   private String title;   private String composer;   private int length;   public CDSong()   {   }   // getter methods   public String getTitle()   {     return title;   }   public String getComposer()   {     return composer;   }   public int getLength()   {     return length;   }   // setter methods   public void setTitle(String title)   {     this.title = title;   }   public void setComposer(String composer)   {     this.composer = composer;   }   public void setLength(int length)   {     this.length = length;   } } 

Finally, we create the data access object, CDInfoDAO, that will access the database and populate the CDInfoModel with data.

 package mypackage3; import java.sql.*; import java.util.*; import javax.naming.*; import java.io.*; public class CDInfoDAO {   Connection conn = null;   Statement stmt = null;   public CDInfoDAO()   {   }   public CDInfoModel createCDInfoModel(long cd_id)   {     CDInfoModel cdModel = new CDInfoModel();     try     {       getConnection();       stmt = conn.createStatement();       // Get CD info       String sql = "SELECT DISPLAY_NAME(A.NAME, A.SURNAME, A.LOCALE), " +                    "C.ALBUM_TITLE, C.LABEL, C.RELEASE_DATE " +                    "FROM CD_COLLECTION C, ARTISTS A " +                    "WHERE C.ARTIST_ID = A.ARTIST_ID " +                    "AND CD_ID = " + cd_id;       ResultSet rs = stmt.executeQuery(sql);       while(rs.next())       {         cdModel.setArtistName(rs.getString(1));         cdModel.setTitle(rs.getString(2));         cdModel.setLabel(rs.getString(3));         cdModel.setReleaseDate(rs.getString(4));       }       rs.close();       // Get song list       Vector songs = new Vector();       sql = "SELECT TITLE, COMPOSER, LENGTH FROM SONGS " +             "WHERE  CD_createCDinfoModel() - Caught: " + e);     }     return cdModel;   }   private void getConnection()   {     try     {       InitialContext ic=new InitialContext();       javax.sql.DataSource ds =            (javax.sql.DataSource)(ic.lookup("jdbc/OsirisDS"));       conn= ds.getConnection();     }     catch(Exception e)     {       System.out.println("getConnection() - Caught: " + e);     }   } } 

Configuring an EJB DataSource

In an EJB, we don't register and load an Oracle JDBC driver as we would in a standalone application. Database connectivity is one of the services that is provided by the EJB container in the form of a datasource. This has two benefits. First, portability is improved because we obtain the datasource by doing JNDI lookup, so the datasource can be configured by changing the data-source.xml file instead of editing the source and recompiling the application. Second, because the EJB container is managing database access, it can transparently provide features such as connection pooling, which can improve performance.

Note how we do this in the code above: We call a parameterless constructor, InitialContext(), to obtain the JNDI initial context, ic. Unlike a client application, where we must provide the JNDI properties for the initial context (either by building a hashtable or by reading a properties file), here the EJB container uses the application's default context environment to create the JNDI initial context. Next, we call the initial context's ic.lookup() method to obtain the datasource, using the logical name "jdbc/OsirisDS".

Before running the EJB in the embedded EJB container or deploying it to the standalone server, we need to set up the datasource information. We can do this by editing the data-source.xml file. For the embedded server, this file is located in the c:\JDeveloper\jdev\system\oc4j-config directory, assuming that JDeveloper is installed in c:\JDeveloper.

For the standalone OC4J server, it's in the c:\JDeveloper\j2ee\home\config directory, assuming that we're using the one that came with JDeveloper.

We must specify several datasources: one that provides connection pooling, another that supports distributed transactions, and an EJB-aware one that supports pooled connections and distributed transactions.

[View full width]

<?xml version="1.0" standalone='yes'?> <!DOCTYPE data-sources PUBLIC "Orion data-sources" "http://xmlns.oracle.com/ias/dtds/ graphics/ccc.gifdata-sources.dtd"> <data-sources> <data-source name="Osiris" location="jdbc/Osiris" xa-location="jdbc/xa/OsirisXADS" ejb-location="jdbc/OsirisDS" connection-driver="oracle.jdbc.driver.OracleDriver" username="david" password="bigcat" url="jdbc:oracle:thin:@localhost:1521:osiris" inactivity-timeout="30" /> </data-sources>

Typically in an EJB application, we would use the ejb-location DataSource, which is named jdbc/osirisDS here.

Testing the CDInfoBean EJB

We can use JDeveloper to build a sample application to test our EJB, as we did for the previous example, by right-clicking on CDInfo bean and selecting Create Sample Java client. Depending on whether you're running the bean in the embedded EJB container or on a standalone container, select either Connect to OC4J Embedded in JDeveloper or Connect to Remote App Server.

We'll need to make the following changes in order to print out information for a selected CD:

  • Uncomment the line to create the session bean.

  • Add code to obtain a CDInfoModel value object for an arbitrary CD. (In this example, we hard-coded the value of a known CD_ID, 107L.)

  • Add print statements with getter methods.

With these changes, the sample application looks like this:

 package Samplemypackage3; import java.util.Hashtable; import javax.naming.Context; import javax.naming.InitialContext; import mypackage3.CDInfo; import mypackage3.CDInfoHome; import mypackage3.CDInfoModel; import mypackage3.CDSong; import java.util.Vector; import java.lang.Integer; public class CDInfoClient2 {   public static void main(String [] args)   {     CDInfoClient2 cDInfoClient2 = new CDInfoClient2();     try     {       Hashtable env = new Hashtable();       env.put(Context.INITIAL_CONTEXT_FACTORY,          "com.evermind.server.rmi.RMIInitialContextFactory");       env.put(Context.SECURITY_PRINCIPAL, "admin");       env.put(Context.SECURITY_CREDENTIALS, "adminpwd");       env.put(Context.PROVIDER_URL, "ormi://noizmaker/CDInfo");       Context ctx = new InitialContext(env);       CDInfoHome cDInfoHome = (CDInfoHome)ctx.lookup("CDInfo");       CDInfo cDInfo;       // Create EJB       cDInfo = cDInfoHome.create(  );       // Call EJB method to get CD information       CDInfoModel cdInfo = cDInfo.getCDInfoModel(107L );       // Print information from CDInfoModel       System.out.println(cdInfo.getArtistName());       System.out.println(cdInfo.getTitle());       System.out.print(cdInfo.getLabel() + ", ");       System.out.println(cdInfo.getReleaseDate());       Vector songs = cdInfo.getSongs();       for(int i=0; i < songs.size(); i++)       {         CDSong song = (CDSong) songs.elementAt(i);         System.out.println("  " + (i + 1)  + ". " +                            song.getTitle() +                            ", " + song.getComposer() + " - " +                            formatTime(song.getLength()));       }     }     catch(Exception e)     {       System.out.println("Caught: " + e);     }   }   public static String formatTime(int time)   {     int min = time/60;     int sec = time - (min*60);     return Integer.toString(min) + ":" + Integer.toString(sec);   } } 

Notice that we've also added a helper method, formatTime(), which takes the time in seconds and formats it into a string, with minutes and seconds separated by a colon.

The sample application prints out information in the following format:

 Richard and Linda Thompson Shoot Out the Lights Hannibal, 1982   1. Don't Renege On Our Love, Richard Thompson - 4:15   2. Walking On A Wire, Richard Thompson - 5:24   3. Man In Need, Richard Thompson - 3:32   4. Just The Motion, Richard Thompson - 6:16   5. Shoot Out The Lights, Richard Thompson - 5:20   6. Black Street Slide, Richard Thompson - 4:29   7. Did She Jump Or Was She Pushed, Richard and Linda Thompson - 4:45   8. Wall Of Death, Richard Thompson - 3:42 

We'll stop here with this example, but we should note that this is more nearly what we would want in the Web application that we examined in the last chapter, where we used a CMP entity bean that JDeveloper created for us. Certainly, creating the CMP bean was a lot easier but we were much more limited in what we could do. Also, because its life cycle is managed by the container, the database is updated each time we call it even though we use it as a read-only object. Here, we don't have that overhead.

Finally, it's worth noting that if this information were being displayed using a JSP, the formatTime() method would be a good candidate for a custom tag because it is Java code used for formatting purposes and doesn't properly belong in an EJB, servlet, or controller helper class.



Java Oracle Database Development
Java Oracle Database Development
ISBN: 0130462187
EAN: 2147483647
Year: 2002
Pages: 71

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net