Generating EntityBeans with XSL Templates

Generating EntityBeans with XSL Templates 

By Tim Burns, OCI Senior Software Engineer

April 2001


The Enterprise Java Beans implementation allows the programmer to manage EntityBeans with either container-managed persistence (the EJB container manages the data of the EntityBean) or bean-managed persistence (the developer manages the data explicitly).

Often, developers are dealing with legacy applications that do not map to container-managed persistence and must use bean-managed persistence. This article describes how a developer can use XSL Templates to generate Java source code that implements the EntityBean interface using the metadata for existing database schemas.

The article is divided into four parts.

  1. Part one describes how tables in a database schema correspond to EntityBeans, where an instance of the EntityBean is a row in a table.
  2. Part two discusses how the DatabaseMetaData class can be used to generate the XML to represent the translation from SQL to Java.
  3. Part three discusses an XSL Template for translating the XML and generating the EntityBean source code.
  4. Part four explains how to use the javax.xml.transform package from JAXP 1.1 to perform this task.

1. Turning an Existing SQL Schema into EntityBeans

Say we have an existing database with many tables.

One such table might have the following DDL:

  1. CREATE TABLE auction_ledger (
  2. auction_id NUMBER( 9 ) NOT NULL,
  3. title VARCHAR2( 192 ) NOT NULL,
  4. topic VARCHAR2( 32 ) NOT NULL,
  5. condition VARCHAR2( 32 ),
  6. CONSTRAINT auction_ledger_PK
  7. PRIMARY KEY ( auction_id )
  8. );

An EntityBean to perform bean-managed persistence of the above relational object will contain member variables for each column defined by the database.

The member variables in the Java code will correspond to column data types. For instance VARCHAR maps to String, DECIMAL to Float, etc.

The default data type in most SQL databases allows data to be NULL, or that no value corresponds to the data type. In Java, primitive data types cannot be NULL and must have a value.

Thus, it makes good sense that the Java types are class rather than primitive types to handle NULL values. Even with database entries that can be NOT NULL, the data constraint can change to allow nulls, and such changes should not have a cascade effect within an application, so the SQL column types should map to Java class types.

A header fragment of the Java code that uses object types and maps every column to a member variable is as follows:

  1. public class AuctionLedgerBean implements EntityBean {
  2.  
  3. public Integer auctionId;
  4. public String title;
  5. public String topic;
  6. public String condition;
  7.  
  8. ... (more code)
  9. }

Within the EntityBean class, code to interface a database with SQL usually must contain specific methods for updating, creating, selecting, and deleting a row in a table.

The Java source maps to the SQL in a very predictable fashion, illustrated by the following code that implements the required ejbStore() interface method:

  1. public void ejbStore() {
  2. Connection con = null;
  3. PreparedStatement ps = null;
  4. try {
  5. con = this.getConnection();
  6. ps = con.prepareStatement(
  7. "update AUCTION_LEDGER set " +
  8. "AUCTION_ID = ?, " +
  9. "TITLE = ?, " +
  10. "TOPIC = ?, " +
  11. "CONDITION = ? " +
  12. "where AUCTION_ID = ?" );
  13. ps.setInt( 1, this.auctionId );
  14. ps.setString( 2, this.title );
  15. ps.setString( 3, this.topic );
  16. ps.setString( 4, this.condition );
  17.  
  18. if (ps.executeUpdate() != 1) {
  19. throw new EJBException("ejbStore failed" );
  20. }
  21. }
  22. catch (SQLException se) {
  23. throw new EJBException(se);
  24. }
  25. finally {
  26. try {
  27. if ( con != null ) con.close();
  28. } catch( SQLException se ){
  29. se.printStackTrace();
  30. }
  31. }
  32. }

The EntityBean implementation requires similar code for create, delete, and find-by-primary-key for each table in the SQL schema.

If you have many tables in your database schema, the developer must write a lot of source code that is essentially the same, except for types and column names.

2. Using XML To Represent the SQL-to-Java Translation

In the given problem, XSL Transforms can be useful, because they allow the developer to set up a template for creating this code.

The developer can pass the template XML data gathered from the database meta-data class (java.sql.DatabaseMetaData) to create the Java source for bean-managed persistence.

Here is an example of the XML generated the by the java.sql.DatabaseMetaData class to represent the SQL to Java mapping:

  1. <project
  2. name="xmedbook"
  3. packageName="com.owlmountain.xmedbook.ejb.entity"
  4. packageDirectory="com/owlmountain/xmedbook/ejb/entity">
  5.  
  6. <sqlJavaMap javaClassName="AuctionLedger"
  7. sqlTableName="AUCTION_LEDGER" >
  8.  
  9. <column javaName="AuctionId" javaVarName="auctionId"
  10. javaType="Integer" sqlColumnName="AUCTION_ID"/>
  11.  
  12. <column javaName="Title" javaVarName="title"
  13. javaType="String" sqlColumnName="TITLE"/>
  14.  
  15. <column javaName="Topic" javaVarName="topic"
  16. javaType="String" sqlColumnName="TOPIC"/>
  17.  
  18. <column javaName="Condition" javaVarName="condition"
  19. javaType="String" sqlColumnName="CONDITION"/>
  20.  
  21.  
  22. </sqlJavaMap>
  23. </project>
Note: An alternative to providing the mapping would be to write a named template in XSL for translating the SQL names to Java names. I chose to map the SQL to Java in a Java method because the Java source was simpler and easier for me to understand than the XSL template.

3. XSL Templates for Generating Java Source

The beauty of using XSL Templates is seen when we apply the mapping defined by the XML in the previous example. It makes it very easy for us to modify or cut-and-paste existing Java source into the template and apply the transformation to get working EntityBeans.

For any table we wish to translate, the following Java source template will be:

public class <Translated Name> implements EntityBean {...}

A good strategy is to develop a single working EntityBean class with corresponding home and remote interfaces first and then look for all the instances that correspond to the XML mapping.

The example below shows an XSL template for the remote interface. The EntityBean class method containing the bulk of the source is listed in Appendix A.

  1. <?xml version="1.0" ?>
  2. <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  3. <xsl:output method="text"></xsl:output>
  4. <xsl:template match="/">
  5. <xsl:apply-templates></xsl:apply>
  6. </xsl:template>
  7.  
  8. <xsl:template match="project">
  9.  
  10. <xsl:variable name="javaClassName">
  11. <xsl:value-of select="sqlJavaMap/@javaClassName"></xsl:value>
  12. </xsl:variable>
  13.  
  14.  
  15. package <xsl:value-of select="@packageName"></xsl:value>;
  16.  
  17. import javax.ejb.EJBObject;
  18. import java.rmi.RemoteException;
  19.  
  20. public interface <xsl:value-of select="$javaClassName"></xsl:value> extends EJBObject {
  21.  
  22. <xsl:for-each select="sqlJavaMap/column">
  23. <xsl:value-of select="@javaType"></xsl:value> get<xsl:value-of
  24. select="@javaName"></xsl:value>() throws RemoteException;
  25. void set<xsl:value-of select="@javaName"></xsl:value>( <xsl:value-of
  26. select="@javaType"></xsl:value> value ) throws RemoteException;
  27. </xsl:for-each>
  28.  
  29. }
  30. </xsl:template>
  31. </xsl:stylesheet>

The XSLT transformer will replace the <xsl:value-of select="x"/> tags with the corresponding entries in the XML example. Each table in the database will have its own values mapped into XML, and so we can generate many EntityBean interfaces and classes by simply re-applying the template.

4. Using JAXP 1.1 to Transform XSL Templates to Java

Because we are using java.sql.DatabaseMetaData to generate the XML, it makes sense to use a standard transformer class within the same program to generate the Java source code.

The JAXP 1.1 package javax.xml.transform provides a standard interface for applying XSL Templates to XML. We can use the interface with any implementation (like Xalan2 or Saxon) that implements this interface.

The transformer classes within javax.xml.transform can be used to generate the EntityBeans described above by creating StreamSources from the XML SQL-to-Java translation and the file containing the Java source and XSL templates.

Once the XML specifying the translation from SQL to Java has been created, we create a Transformer object using the TransformerFactory. We use the generic source stream interfaces and the generic result interfaces to create streams for our sources and results.

  1. // Get the Java code template.
  2. TransformerFactory tfactory = TransformerFactory.newInstance();
  3. Transformer transformer = tfactory.newTransformer(
  4. new StreamSource( new FileReader( xslFile.getFullPath() ) ) );
  5.  
  6. // Transform the template with the XML mapping.
  7. StringWriter resultBuffer = new StringWriter();
  8. transformer.transform(
  9. new StreamSource( new StringReader( xmlIn.toString() ) ),
  10. new StreamResult( resultBuffer ) );

For the case of transforming code, using JAXP internally is preferable to a stand-alone XSLT transformer tool because the database meta-data that can be transformed into XML is either a String or in DOM format. The interface is also quite clean and doesn't require any specific implementation of an XSLT transformer.

XSLT is truly a powerful tool for automating many coding tasks. Because it uses a tag-style format, many programmers will find the resulting template code only a little more difficult to read than the original Java.

Furthermore, by using templates, it is easy to adapt to new interfaces and patterns into the EntityBean source code simply by editing the XSL stylesheets.

References



Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.


secret