Extensions : RDBMS Adapters

Plugin

DataNucleus is developed as a plugin-driven framework and one of the components that is pluggable is the adapter for the datastore. The datastore adapter provides the translation between DataNucleus and the specifics of the RDBMS in use. DataNucleus provides support for a large selection of RDBMS but is structured so that you can easily add your own adapter for your RDBMS and have it usable within your DataNucleus usage.

DataNucleus supports many RDBMS databases, and by default, ALL RDBMS are supported without the need to extend DataNucleus. Due to incompabilities, or specifics of each RDBMS database, its allowed to extend DataNucleus to make the support to a specific database fit better to DataNucleus and your needs. The RDBMS page lists all RDBMS databases that have been tested with DataNucleus, and some of these databases has been adapted internally to get a good fit. You can extend DataNucleuss capabilities using the plugin extension org.datanucleus.store.rdbms.datastoreadapter.

Plugin extension-point Key Description Location
org.datanucleus.store.rdbms.datastoreadapter derby Adapter for Apache Derby/Cloudscape datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter db2 Adapter for IBM DB2 datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter as/400 Adapter for IBM DB2 AS/400 datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter firebird Adapter for Firebird/Interbase datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter microsoft Adapter for MSSQL server datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter h2 Adapter for H2 datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter hsql Adapter for HSQLDB datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter mckoi Adapter for McKoi DB datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter mysql Adapter for MySQL datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter sybase Adapter for Sybase datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter oracle Adapter for Oracle datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter pointbase Adapter for Pointbase datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter postgresql Adapter for PostgreSQL datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter sapdb Adapter for SAPDB/MaxDB datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter sqlite Adapter for SQLite datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter timesten Adapter for Timesten datanucleus-rdbms
org.datanucleus.store.rdbms.datastoreadapter informix Adapter for Informix datanucleus-rdbms

DataNucleus supports a very wide range of RDBMS datastores. It will typically auto-detect the datastore adapter to use and select it. This, in general, will work well and the user will not need to change anything to benefit from this behaviour. There are occasions however where a user may need to provide their own datastore adapter and use that. For example if their RDBMS is a new version and something has changed relative to the previous (supported) version, or where the auto-detection fails to identify the adapter since their RDBMS is not yet on the supported list.

By default when you create a PMF to connect to a particular datastore DataNucleus will automatically detect the datastore adapter to use and will use its own internal adapter for that type of datastore. The default behaviour is overridden using the persistence property org.datanucleus.rdbms.datastoreAdapterClassName, which specifies the class name of the datastore adapter class to use. This class must implement the DataNucleus interface DatastoreAdapter Javadoc.

So you need to implement DatastoreAdapter. You have 2 ways to go here. You can either start from scratch (when writing a brand new adapter), or you can take the existing DataNucleus adapter for a particular RDBMS and change (or extend) it. Lets take an example so you can see what is typically included in such an Adapter. Bear in mind that ALL RDBMS are different in some (maybe small) way, so you may have to specify very little in this adapter, or you may have a lot to specify depending on the RDBMS, and how capable its JDBC drivers are.

public class MySQLAdapter extends BaseDatastoreAdapter
{
    /**
     * A string containing the list of MySQL keywords that are not also SQL/92
     * <i>reserved words</i>, separated by commas.
     */
    public static final String NONSQL92_RESERVED_WORDS =
        "ANALYZE,AUTO_INCREMENT,BDB,BERKELEYDB,BIGINT,BINARY,BLOB,BTREE," +
        "CHANGE,COLUMNS,DATABASE,DATABASES,DAY_HOUR,DAY_MINUTE,DAY_SECOND," +
        "DELAYED,DISTINCTROW,DIV,ENCLOSED,ERRORS,ESCAPED,EXPLAIN,FIELDS," +
        "FORCE,FULLTEXT,FUNCTION,GEOMETRY,HASH,HELP,HIGH_PRIORITY," +
        "HOUR_MINUTE,HOUR_SECOND,IF,IGNORE,INDEX,INFILE,INNODB,KEYS,KILL," +
        "LIMIT,LINES,LOAD,LOCALTIME,LOCALTIMESTAMP,LOCK,LONG,LONGBLOB," +
        "LONGTEXT,LOW_PRIORITY,MASTER_SERVER_ID,MEDIUMBLOB,MEDIUMINT," +
        "MEDIUMTEXT,MIDDLEINT,MINUTE_SECOND,MOD,MRG_MYISAM,OPTIMIZE," +
        "OPTIONALLY,OUTFILE,PURGE,REGEXP,RENAME,REPLACE,REQUIRE,RETURNS," +
        "RLIKE,RTREE,SHOW,SONAME,SPATIAL,SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS," +
        "SQL_SMALL_RESULT,SSL,STARTING,STRAIGHT_JOIN,STRIPED,TABLES," +
        "TERMINATED,TINYBLOB,TINYINT,TINYTEXT,TYPES,UNLOCK,UNSIGNED,USE," +
        "USER_RESOURCES,VARBINARY,VARCHARACTER,WARNINGS,XOR,YEAR_MONTH," +
        "ZEROFILL";

    /**
     * Constructor.
     * Overridden so we can add on our own list of NON SQL92 reserved words
     * which is returned incorrectly with the JDBC driver.
     * @param metadata MetaData for the DB
     **/
    public MySQLAdapter(DatabaseMetaData metadata)
    {
        super(metadata);

        reservedKeywords.addAll(parseKeywordList(NONSQL92_RESERVED_WORDS));
    }

    /**
     * An alias for this adapter.
     * @return The alias
     */
    public String getVendorID()
    {
        return "mysql";
    }

    /**
     * MySQL, when using AUTO_INCREMENT, requires the primary key specified
     * in the CREATE TABLE, so we do nothing here. 
     * 
     * @param pkName The name of the primary key to add.
     * @param pk An object describing the primary key.
     * @return The statement to add the primary key separately
     */
    public String getAddPrimaryKeyStatement(SQLIdentifier pkName, PrimaryKey pk)
    {
        return null;
    }

    /**
     * Whether the datastore supports specification of the primary key in
     * CREATE TABLE statements.
     * @return Whetehr it allows "PRIMARY KEY ..."
     */
    public boolean supportsPrimaryKeyInCreateStatements()
    {
        return true;
    }

    /**
     * Method to return the CREATE TABLE statement.
     * Versions before 5 need INNODB table type selecting for them.
     * @param table The table
     * @param columns The columns in the table
     * @return The creation statement 
     **/
    public String getCreateTableStatement(TableImpl table, Column[] columns)  
    {
        StringBuffer createStmt = new StringBuffer(super.getCreateTableStatement(table,columns));

        // Versions before 5.0 need InnoDB table type
        if (datastoreMajorVersion < 5)
        {
            createStmt.append(" TYPE=INNODB");
        }

        return createStmt.toString();
    }

    ...
}

So here weve shown a snippet from the MySQL DatastoreAdapter. We basically take much behaviour from the base class but override what we need to change for our RDBMS. You should get the idea by now. Just go through the Javadocs of the superclass and see what you need to override.

A final step that is optional here is to integrate your new adapter as a DataNucleus plugin. To do this you need to package it with a file plugin.xml, specified at the root of the CLASSPATH. The file should look like this

<?xml version="1.0"?>
<plugin id="mydomain" name="MyCompany DataNucleus plug-in" provider-name="MyCompany">
    <extension point="org.datanucleus.store.rdbms.datastoreadapter">
        <datastore-adapter vendor-id="myname" class-name="mydomain.MyDatastoreAdapter" priority="10"/>
    </extension>
</plugin>

Note that you also require a MANIFEST.MF file as per the Extensions Guide.

Where the myname specified is a string that is part of the JDBC product name (returned by DatabaseMetaData.getDatabaseProductName()). If there are multiple adapters for the same vendor-id defined, the attribute priority is used to determine which one is used. The adapter with the highest number is chosen. Note that the behaviour is undefined when two or more adapters with vendor-id have the same priority. All adapters defined in DataNucleus and its official plugins use priority values between 0 and 9. So, to make sure your adapter is chosen, use a value higher than that.