The sample Hibernate ODA driver extension
The package for the Hibernate ODA extension example, org.eclipse.birt
.report.data.oda.hibernate, implements the following classes using the ODA plug-in interfaces defined in the DTP plug-in, org.eclipse.datatools
.connectivity.oda, and the extension points defined in the XML schema file, datasource.exsd. The package implements the following classes:
*Activator
Extends org.eclipse.core.runtime.Plugin. Defines the methods for starting, managing, and stopping a plug-in instance.
*HibernateDriver
Implements the IDriver interface. Instantiates the connection object for the Hibernate ODA driver, which provides the entry point for the Hibernate ODA plug‑in.
*Connection
Implements the IConnection interface. Opens and closes the connection to the Hibernate ODA data source and instantiates the IQuery object.
*Statement
Implements the IQuery interface. Prepares the result set metadata containing the table and column names, executes the query, and fetches the data rows from the data source. This class is used instead of the default Query class added by the plug-in wizard to avoid name conflicts with org.hibernate.Query.
*ResultSet
Implements the IResultSet interface. Provides access to the data rows in the result set, maintaining a cursor that points to the current row. Handles the processing that gets the value for a column as the specified data type.
*ResultSetMetaData
Implements the IResultSetMetaData interface. Describes the metadata for each column in the result set.
*DataSetMetaData
Implements the IDataSetMetaData interface. Describes the features and capabilities of the driver for the data set.
*Messages
Defines the exception messages for the Hibernate ODA driver.
*DataTypes
Defines, validates, and returns the data types supported by the Hibernate ODA driver.
*CommonConstant
Defines the constants used in the package, such as the driver name, ODA version, query keywords, and delimiters.
*HibernateUtil
Manages the Hibernate SessionFactory that provides the session or run‑time interface between the Hibernate service and the ODA driver. This class is built based on the example HibernateUtil, available at http://www.hibernate.org.
The Hibernate ODA driver plug-in supports specifying the Hibernate configuration file and mapping files directory in the data source wizard. The plug-in creates the Hibernate SessionFactory from these settings. The example project has an exampleconfig directory that contains a Hibernate configuration and mapping files for use with the BIRT MySQL example database, Classic Models, Inc.
The following sections describe the classes where there are important differences between the implementation of Hibernate ODA driver and the earlier example, the CSV ODA driver.
HibernateDriver class
The HibernateDriver class instantiates the Connection object for the Hibernate ODA driver. This class implements the IDriver interface, but does not provide any processing for the methods that configure logging and set the application context. Listing 25‑33 shows the getConnection( ) method.
Listing 25‑33 The getConnection( ) method
public IConnection getConnection( String connectionClassName )
throws OdaException
{
return new Connection( );
}
getMaxConnections( ) returns 0, imposing no limit on the number of connections to the ODA data source from the application. Listing 25‑34 shows the getMaxConnections( ) method.
Listing 25‑34 The getMaxConnections( ) method
public int getMaxConnections( ) throws OdaException
{
return( 0 );
}
Connection class
The Connection class implements the following methods:
*open( )
Opens a Hibernate session and sets the Boolean variable, isOpen, to true. The open( ) method uses the HibernateUtil class to obtain a session from a Hibernate SessionFactory, providing the run-time interface between the Hibernate service and the ODA driver.
The open( ) method retrieves the locations for the Hibernate configuration file and mapping files directory from connection properties. The open( ) method calls HibernateUtil.constructSessionFactory( ), which attempts to build the SessionFactory with these settings. If the SessionFactory already exists, the plug-in does not recreate the SessionFactory unless the Hibernate configuration file or the mapping directory have changed.
Listing 25‑35 shows the code for the open( ) method.
Listing 25‑35 The open( ) method
public void open( Properties connProperties )
throws OdaException
{
try {
configfile =
connProperties.getProperty( "HIBCONFIG" );
mapdir = connProperties.getProperty( "MAPDIR" );
HibernateUtil.constructSessionFactory(
configfile, mapdir );
Session testSession = HibernateUtil.currentSession( );
this.isOpen = true;
}catch( Exception e ) {
throw new OdaException( e.getLocalizedMessage( ) );
}
}
*newQuery( )
Opens a new query by returning an instance of a Statement object, the class that implements the IQuery interface. The connection can handle multiple result set types, but the Hibernate ODA example uses only one and ignores the dataSetType parameter, as shown in Listing 25‑36.
Listing 25‑36 The newQuery( ) method
public IQuery newQuery( String dataSetType )
throws OdaException
{
if ( !isOpen( ) )
throw new OdaException( Messages.getString(
"Common.CONNECTION_IS_NOT_OPEN" ) );
return new Statement( this );
}
*getMetaData( )
Returns an IDataSetMetaData object of the data set type, as shown in Listing 25‑37.
Listing 25‑37 The getMetaData( ) method
public IDataSetMetaData getMetaData( String dataSetType )
throws OdaException
{
return new DataSetMetaData( this );
}
*getMaxQueries( )
Indicates the maximum number of queries the driver supports. The getMaxQueries( ) method returns 1, indicating that the Hibernate ODA driver does not support concurrent queries, as shown in Listing 25‑38.
Listing 25‑38 The getMaxQueries( ) method
public int getMaxQueries( ) throws OdaException
{
return 1;
}
*commit( ) and rollback( )
Handle transaction processing. The Hibernate ODA driver example does not support transaction operations, so these methods throw UnsupportedOperationException. Listing 25‑39.
Listing 25‑39 The commit( ) method
public void commit( ) throws OdaException
{
throw new UnsupportedOperationException ( );
}
*close( )
Closes the Hibernate session, as shown in Listing 25‑40.
Listing 25‑40 The close( ) method
public void close( ) throws OdaException
{
this.isOpen = false;
try {
HibernateUtil.closeSession( );
} catch(Exception e) {
throw new OdaException( e.getLocalizedMessage( ) );
}
}
DataSetMetaData class
The DataSetMetaData class describes the features and capabilities of the data source for the specified data set. The Hibernate ODA driver example returns true or false to indicate support for a feature. The Hibernate ODA driver example does not support input, output, or named parameters. The driver also does not support multiple result sets, as shown in Listing 25‑41.
Listing 25‑41 The supportsMultipleResultSets( ) method
public boolean supportsMultipleResultSets( ) throws OdaException
{
return false;
}
A method such as getSQLStateType( ), which has no implementation, simply throws UnsupportedOperationException, as shown in Listing 25‑42.
Listing 25‑42 The getSQLStateType( ) method
public int getSQLStateType( ) throws OdaException
{
throw new UnsupportedOperationException ( );
}
Statement class
The Statement class implements the IQuery interface. This class prepares and executes the query. Statement also handles parameters and retrieves the result set and result set metadata.
The Statement class implements the following methods:
*prepare( )
The ODA framework calls the prepare( ) method before executing the query. The ODA framework uses the query saved in the report design.
The Hibernate ODA user interface plug-in also calls prepare( ) to verify the columns used in the report design. The user interface plug-in passes an HQL statement that gets the columns from the result set object.
prepare( ) sets up the result-set metadata and stores the query in an object variable for use by the executeQuery( ) method. The ODA run time uses the result-set metadata to retrieve the data. BIRT Report Designer also uses
the result-set metadata to display the columns in the user interface.
The prepare( ) method performs the following operations:
*Sets up array lists to contain the columns, column types, and column classes
*Trims the query String
*Creates a Hibernate Query object, using the HQL query
*Gets the Hibernate column names, types, and classes for the query
*Instantiates a ResultSetMetaData object, passing in the column names and data types
*Saves the query for execution
Listing 25‑43 shows the code for the prepare( ) method.
Listing 25‑43 The prepare( ) method
public void prepare( String query ) throws OdaException
{
Query qry = null;
testConnection( );
ArrayList arColsType = new ArrayList( );
ArrayList arCols = new ArrayList( );
ArrayList arColClass = new ArrayList( );
 
String[ ] props = null;
try {
Session hibsession = HibernateUtil.currentSession( );
query = query.replaceAll( "[\\n\\r]+"," " );
query = query.trim( );
qry = hibsession.createQuery( query );
Type[ ] qryReturnTypes = qry.getReturnTypes( );
if ( qryReturnTypes.length > 0
&& qryReturnTypes[0].isEntityType( ) )
{
for ( int j=0; j< qryReturnTypes.length; j++ )
{
String clsName=qryReturnTypes[j].getName( );
props = HibernateUtil.getHibernateProp( clsName );
for ( int x = 0; x < props.length; x++ )
{
String propType =
HibernateUtil.getHibernatePropTypes(
clsName, props[x] );
if( DataTypes.isValidType( propType ))
{
arColsType.add( propType );
arCols.add( props[x] );
arColClass.add( clsName );
}
else
{
throw new OdaException( Messages.getString(
"Statement.SOURCE_DATA_ERROR" ) );
}
}
}
}
else
{
props = extractColumns( qry.getQueryString( ) );
for( int t=0; t < qryReturnTypes.length; t++)
{
if ( DataTypes.isValidType(
qryReturnTypes[t].getName( )))
{
arColsType.add( qryReturnTypes[t].getName( ));
arCols.add( props[t] );
}
else
{
throw new OdaException( Messages.getString(
"Statement.SOURCE_DATA_ERROR") );
}
}
}
}
catch( Exception e ) {
throw new OdaException( e.getLocalizedMessage( ) );
}
this.resultSetMetaData = new ResultSetMetaData(
( String[ ] )arCols.toArray( new String[arCols.size()] ),
( String[ ] )arColsType.toArray(
new String[arColsType.size( )] ),
( String[ ] )arCols.toArray( new String[arCols.size()] ),
( String[ ] )arColClass.toArray(
new String[arColClass.size( )] ));
this.query = query;
}
*getMetaData( )
The BIRT framework calls getMetaData( ) after the prepare( ) method to retrieve the metadata for a result set. The BIRT framework uses the metadata to create the data set in the report.
Listing 25‑44 shows the code for the getMetaData( ) method.
Listing 25‑44 The getMetaData( ) method
public IResultSetMetaData getMetaData( ) throws OdaException
{
return this.resultSetMetaData;
}
*executeQuery( )
The executeQuery( ) method executes the prepared query and retrieves the results. The executeQuery( ) method returns an IResultSet object, which is created using the list results, result-set metadata, and Hibernate types returned from the HQL query. The ODA framework uses the IResultSet object to iterate over the results.
The executeQuery( ) method performs the following operations:
*Sets up an array of org.hibernate.type.Type to map Java types to JDBC data types
*Sets up a list to contain the results set
*Trims the query String
*Instantiates a Hibernate Query object, creating the HQL query
*Executes the HQL query, returning the query result set in a List
*Gets the Hibernate types for the query result set
*Instantiates a ResultSet object, passing in the data, metadata, and Hibernate types
Listing 25‑45 shows the code for the executeQuery( ) method.
Listing 25‑45 The executeQuery( ) method
public IResultSet executeQuery( ) throws OdaException
{
Type[ ] qryReturnTypes = null;
List rst = null;
try {
Session hibsession = HibernateUtil.currentSession( );
String qryStr = this.query;
qryStr = qryStr.replaceAll( "[\\n\\r]+"," " );
qryStr.trim( );
Query qry = hibsession.createQuery( qryStr );
rst = qry.list( );
qryReturnTypes = qry.getReturnTypes( );
}
catch( Exception e ) {
throw new OdaException( e.getLocalizedMessage( ) );
}
return new ResultSet( rst, getMetaData( ), qryReturnTypes );
}
*close( )
The close( ) method clears the Connection and ResultSetMetaData objects. In the Connection object, the close( ) method closes the Hibernate session.
Listing 25‑46 shows the code for the Statement.close( ) method.
Listing 25‑46 The Statement.close( ) method
public void close( ) throws OdaException
{
connection = null;
resultSetMetaData = null;
mmaxRows = 0;
}
ResultSet class
The ResultSet class implements the IResultSet interface. When this class is instantiated, it stores the list.iterator( ) passed from the Statement object. It uses the iterator when the ODA driver framework calls the next( ) method.
The iterator points to the next available row of data from the HQL query results. The framework calls the accessor methods that get the data types for the columns in the current row. For example, if the first column is a String, the framework calls getString( ). This method calls the getResult( ) method, which interprets the HQL query results.
The getResult( ) method parses the results in two ways, depending on whether the query returns a Hibernate EntityType or just an array of values:
*If the query uses HQL and each return type is an EntityType, getResult( ) gets each Column class and uses the Hibernate ClassMetaData methods to retrieve the value.
*If the query returns standard data types, getResult( ) gets each value or values, returning an Object containing the simple value or an array of Objects containing the multiple values.
Listing 25‑47 shows the code for the getResult( ) method.
Listing 25‑47 The getResult( ) method
private Object getResult( int rstcol ) throws OdaException
{
Object obj = this.currentRow;
Object value = null;
 
try {
if ( qryReturnTypes.length > 0
&& qryReturnTypes[0].isEntityType( ))
{
String checkClass = (( ResultSetMetaData )
getMetaData( )).getColumnClass(rstcol);
Object myVal = HibernateUtil.getHibernatePropVal( obj,
checkClass, getMetaData( ).getColumnName( rstcol ));
value = myVal;
}
else
{
if( getMetaData( ).getColumnCount( ) == 1)
{
value = obj;
}
else
{
Object[ ] values = ( Object[ ])obj;
value = values[rstcol-1];
}
}
}
catch( Exception e ) {
throw new OdaException( e.getLocalizedMessage( ) );
}
 
return( value );
}
HibernateUtil class
HibernateUtil is a utility class that provides the run-time interface between the Hibernate service and the application. The HibernateUtil class example derives from the class provided with the Hibernate documentation. HibernateUtil performs the following operations:
*Initializes the SessionFactory
*Builds the Hibernate SessionFactory
*Opens and closes a session
*Returns information on Hibernate classes and properties
*Registers the JDBC driver with the DriverManager
Connection.open( ) calls HiberFnateUtil.constructSessionFactory( ), which creates a SessionFactory if one does not already exist. The constructSessionFactory( ) method closes and rebuilds the SessionFactory if the location of the configuration file or mapping files directory has changed.
The SessionFactory construction process creates the ClassLoader. The ClassLoader adds the drivers directory in the org.eclipse.birt.report.data.oda
.jdbc plug-in and the hibfiles directory in the Hibernate ODA plug-in to classpath. This process also registers the JDBC driver specified in the Hibernate config file with the DriverManager.
The HibernateUtil class implements the following methods:
*initSessionFactory( )
This method creates the SessionFactory object from the configuration settings in the hibernate.cfg.xml file.
Listing 25‑48 shows the code for the initSessionFactory( ) method.
Listing 25‑48 The initSessionFactory( ) method
private static synchronized void initSessionFactory(
String hibfile, String mapdir ) throws HibernateException
{
if ( sessionFactory == null )
{
Thread thread = Thread.currentThread( );
 
try {
oldloader = thread.getContextClassLoader( );
refreshURLs( );
ClassLoader changeLoader = new URLClassLoader(
( URL [ ] )URLList.toArray( new URL[0]),
thread.getContextClassLoader( ));
thread.setContextClassLoader( changeLoader );
Configuration cfg = buildConfig( hibfile,mapdir );
Class driverClass = changeLoader.loadClass(
cfg.getProperty( "connection.driver_class" ));
Driver driver = ( Driver ) driverClass.newInstance( );
WrappedDriver wd = new WrappedDriver( driver,
cfg.getProperty( "connection.driver_class" ));
boolean foundDriver = false;
Enumeration drivers = DriverManager.getDrivers( );
 
while ( drivers.hasMoreElements( ))
{
Driver nextDriver = ( Driver )drivers.nextElement( );
if ( nextDriver.getClass( ) == wd.getClass( ))
{
if ( nextDriver.toString( ).equals(
wd.toString( )) )
{
foundDriver = true;
break;
}
}
}
 
if( !foundDriver )
{
DriverManager.registerDriver( wd );
}
sessionFactory = cfg.buildSessionFactory( );
configuration = cfg;
HibernateMapDirectory = mapdir;
HibernateConfigFile = hibfile;
}
catch( Exception e) {
e.printStackTrace( );
throw new HibernateException(
"No Session Factory Created " +
e.getLocalizedMessage( ));
}
finally {
thread.setContextClassLoader( oldloader );
}
}
}
*constructSessionFactory
This method checks to see if a configuration change occurred. If a change occurred, the method closes the session and SessionFactory and calls the initSessionFactory to rebuild the SessionFactory.
Listing 25‑49 shows the code for the constructSessionFactory( ) method.
Listing 25‑49 The constructSessionFactory( ) method
public static void constructSessionFactory( String hibfile,
String mapdir ) throws HibernateException
{
if ( hibfile == null)
{
hibfile = "";
}
if ( mapdir == null)
{
mapdir = "";
}
if( sessionFactory == null)
{
initSessionFactory( hibfile, mapdir);
return;
}
 
if( HibernateMapDirectory.equalsIgnoreCase( mapdir )
&& HibernateConfigFile.equalsIgnoreCase( hibfile ))
{
return;
}
 
synchronized( sessionFactory )
{
Session s = ( Session ) session.get( );
if ( s != null )
{
closeSession( );
}
if ( sessionFactory != null &&
!sessionFactory.isClosed( ))
{
closeFactory( );
}
sessionFactory = null;
initSessionFactory( hibfile, mapdir );
}
}
*currentSession( )
This method opens a session when called by the Connection.open( ) method, as shown in Listing 25‑50.
Listing 25‑50 The currentSession( ) method
public static Session currentSession( )
throws HibernateException
{
Session s = ( Session ) session.get( );
if ( s == null )
{
s = sessionFactory.openSession( );
session.set( s );
}
return s;
}
Other methods in this class return information on a particular class and its properties. The getHibernateProp( ) method returns the class properties. The getHibernatePropTypes( ) method returns the data type for a class property.