The Apache DB Project
ObJectRelationalBridge

OJB

Downloads

Documentation

Development

Translated (Web)

The OJB Sequence Manager

All sequence manager implementations you will find under the org.apache.ojb.broker.util.sequence-package using the following naming convention SequenceManagerXXXImpl.

Automatical assignment of unique values

As mentioned in Tutorial1 OJB provides a mechanism to assign unique values for primary key attributes. You just have to enable the autoincrement attribute in the respective FieldDescriptor of the XML repository file as follows:


<class-descriptor
  class="my.Article"
  table="ARTICLE"
>
    <field-descriptor
     name="articleId"
     column="ARTICLE_ID"
     jdbc-type="INTEGER"
     primarykey="true"
     autoincrement="true"
    />
    ....

</class-descriptor>

This definitions contains the following information:
The attribute articleId is mapped on the table's column ARTICLE_ID. The JDBC Type of this column is INTEGER. This is a primary key column. OJB shall automatically assign unique values to this attribute.

This mechanism works for columns of type INTEGER, CHAR and VARCHAR. This mechanism helps you to keep your business logic free from code that computes unique Ids for primary key attributes.

Force computation of unique values

By default OJB triggers the computation of unique ids during calls to PersistenceBroker.store(...). Sometimes it will be necessary to have the ids computed in advance. This can be done by simply obtaining the Identity of the respective object as follows:

    Identity oid = new Identity(object, targetBroker);

How to change the sequence manager?

To enable a specific SequenceManager implementation declare an sequence-manager within the jdbc-connection-descriptor element in the repository file. If no sequence-manager was specified in the jdbc-connection-descriptor, OJB use a default sequence manager implementation (default was SequenceManagerHighLowImpl).

Further information you could find in the repository.dtd section sequence-manager element.

Example jdbc-connection-descriptor using sequence-manager tag:

<jdbc-connection-descriptor
        jcd-alias="farAway"
        platform="Hsqldb"
        jdbc-level="2.0"
        driver="org.hsqldb.jdbcDriver"
        protocol="jdbc"
        subprotocol="hsqldb"
        dbalias="../OJB_FarAway"
        username="sa"
        password=""
        batch-mode="false"
    >
        <connection-pool
            maxActive="5"
            whenExhaustedAction="0"
            validationQuery="select count(*) from OJB_HL_SEQ"
        />

        <sequence-manager className="org.apache.ojb.broker.util.
                                    sequence.SequenceManagerHighLowImpl">
            <attribute attribute-name="grabSize" attribute-value="5"/>
            <attribute attribute-name="globalSequenceId"
                    attribute-value="false"/>
            <attribute attribute-name="globalSequenceStart"
                    attribute-value="10000"/>
        </sequence-manager>
</jdbc-connection-descriptor>

The mandatory className attribute needs the full-qualified class name of the desired sequence-manager implementation. If a implementation needs configuration properties you pass them using attribute tags with attribute-name represents the property name and attribute-value the property value. Each sequence manager implementation shows all properties on the according javadoc page.

SequenceManager implementations

Source code of all SequenceManager implementations can be found in org.apache.ojb.broker.util.sequence package.
If you still think something is missing you can just write your own sequence manager implementation.


High/Low sequence manager
Per default OJB internally uses a High/Low algorithm based sequence manager for the generation of unique ids, as described in Mapping Objects To Relational Databases.
This implementation is called ojb.broker.util.sequence.SequenceManagerHighLowImpl and is able to generate IDs unique to a given object and all extent objects declarated in the objects class descriptor.
If you ask for an uid using an interface with several implementor classes, or a baseclass with several subclasses the returned uid have to be unique accross all tables representing objects of the extent in question (more see here).
It's also possible to use this implementation in a global mode, generate global unique id's.

<sequence-manager className=
    "org.apache.ojb.broker.util.sequence.SequenceManagerHighLowImpl">

    <attribute attribute-name="grabSize" attribute-value="20"/>
    <attribute attribute-name="globalSequenceId"
                                    attribute-value="false"/>
    <attribute attribute-name="globalSequenceStart"
                                    attribute-value="10000"/>
    <attribute attribute-name="autoNaming"
                                    attribute-value="true"/>
</sequence-manager>

With property grabSize you set the size of the assigned ids (default was 20).

If property globalSequenceId was set true you will get global unique ids over all persistent objects. Default was false.
The attribute globalSequenceStart define the start value of the global id generation (default was 10000).

This sequence manager implementation supports user defined sequence-names to manage the sequences. The attribute autoNaming define if sequence names should be build automatic if none found in field-descriptor.
If set 'true' OJB try to build a sequence name automatic if none found in field-descriptor and set this name as sequence-name in field-descriptor (see more). If set 'false' OJB throws an exception if none sequence name was found in field-descriptor (default was 'true').

Limitations:
- do not use in managed environments when connections were enlisted in running transactions, e.g. when using DataSources of an application server
- if set connection-pool attribute 'whenExhaustedAction' to 'block' (wait for connection if connection-pool is exhausted), under heavy load this sequence manager implementation can block application.
- superfluously to mention, do not use if other non-OJB applications insert objects too


In-Memory sequence manager
Another sequence manager implementation is a In-Memory version called ojb.broker.util.sequence.SequenceManagerInMemoryImpl.
Only the first time an uid was requested for a object, the manager query the database for the max value of the target column - all following request were performed in memory. This implementation ditto generate unique ids across all extents, using the same mechanism as the High/Low implementation.

<sequence-manager className="org.apache.ojb.broker.util.
                                sequence.SequenceManagerInMemoryImpl">
    <attribute attribute-name="autoNaming"
                                    attribute-value="true"/>
</sequence-manager>
For attribute autoNaming see

This sequence manager implementation supports user defined sequence-names to manage the sequences (see more) or if not set in field-descriptor it is done automatic.

This is the fastest standard sequence manager implementation, but has some Limitations:
- do not use in clustered environments
- superfluously to mention, do not use (or handle with care) if other non-OJB applications insert objects too


Database sequences based implementation
If your database support sequence key generation (e.g. Oracle, SAP DB, PostgreSQL) you could use the SequenceManagerNextValImpl implementation let your database generate the requested ids.

<sequence-manager className="org.apache.ojb.broker.util.
                                sequence.SequenceManagerNextValImpl">
    <attribute attribute-name="autoNaming"
                                    attribute-value="true"/>
</sequence-manager>

Attribute autoNaming default was 'true'. If set 'true' OJB try to build a sequence name automatic if none found in field-descriptor and set this generated name as sequence-name in field-descriptor.
If set 'false' OJB throws an exception if none sequence name was found in field-descriptor, ditto OJB does NOT try to create a database sequence entry when for given sequence name no database sequence could be found.

When using this sequence manager it is possible to define a sequence-name field-descriptor attribute in the repository file for each autoincrement/pk field. If you don't specify a sequence name, the sequence manager try to build a extent-aware sequence name on its own - except you set attribute autoNaming to 'false', then an exception will be thrown.
Keep in mind that in this case you are responsible to be aware of extents. Thus you have to use the same sequence-name attribute value for all extents, even if the extents were mapped to different database tables.
See usage of the sequence-name attribute:
<class-descriptor
      class="org.apache.ojb.broker.sequence.SMDatabaseSequence"
      table="SM_TAB_DATABASE_SEQUENCE"
    >
        <field-descriptor
         name="seqId"
         column="SEQ_ID"
         jdbc-type="INTEGER"
         primarykey="true"
         autoincrement="true"
         sequence-name="TEST_SEQUENCE"
        />

        ....
    </class-descriptor>

Limitations:
- none known


Database sequences based high/low implementation
Based on the sequence manager implementation described above, but use a high/low algorithm to avoid database access.

<sequence-manager className="org.apache.ojb.broker.util.
                            sequence.SequenceManagerSeqHiLoImpl">
    <attribute attribute-name="grabSize" attribute-value="20"/>
    <attribute attribute-name="autoNaming"
                                    attribute-value="true"/>
</sequence-manager>
With the property grabSize you set the size of the assigned ids. For attribute autoNaming see.

This sequence manager implementation supports user defined sequence-names to manage the sequences (see more) or if not set in field-descriptor it is done automatic.

Limitations:
- superfluously to mention, do not use (or handle with care) if other non-OJB applications insert objects too


Oracle-style sequencing using stored procedure
(Thanks Ryan Vanderwerf et al.)
Ryan wrote:
This solution will give those seeking an oracle-style sequence generator a final answer (Identity columns really suck). If you are using multiple application servers in your environment, and your database does not support read locking like Microsoft SQL Server, this is the only safe way to guarantee unique keys (HighLowSequenceManager WILL give out duplicate keys, and corrupt your data).
The SequenceManagerStoredProcedureImpl implementation enabled database sequence key generation in a Oracle-style for all databases (e.g. MSSQL, MySQL, DB2, ...).
First add a new table OJB_NEXTVAL_SEQ to your database.

CREATE TABLE OJB_NEXTVAL_SEQ
(
    SEQ_NAME    VARCHAR(150) NOT NULL,
    MAX_KEY     INTEGER,
    CONSTRAINT SYS_PK_OJB_NEXTVAL PRIMARY KEY(SEQ_NAME)
)
You will also need a stored procedure called ojb_nextval_proc that will take care of giving you a guaranteed unique sequence number.
Here is an example for the stored procedure you need to use sequencing for MSSQL server:
CREATE PROCEDURE OJB_NEXTVAL_PROC
    @SEQ_NAME varchar(150)
    AS
    declare @MAX_KEY BIGINT
    -- return an error if sequence does not exist
    -- so we will know if someone truncates the table
    set @MAX_KEY = 0

    UPDATE OJB_NEXTVAL_SEQ
        SET    @MAX_KEY = MAX_KEY = MAX_KEY + 1
        WHERE  SEQ_NAME = @SEQ_NAME

    if @MAX_KEY = 0
        select 1/0
    else
        select @MAX_KEY
RETURN @MAX_KEY
You have to adapt this script if MSSQL was not used (We are interested in scripts for other databases). Last, enable this sequence manager implementation:
<sequence-manager className="org.apache.ojb.broker.util.
                       sequence.SequenceManagerStoredProcedureImpl">
    <attribute attribute-name="autoNaming"
                                    attribute-value="true"/>
</sequence-manager>
For attribute autoNaming see.

This sequence manager implementation supports user defined sequence-names to manage the sequences (see more) or if not set in field-descriptor it is done automatic.

Limitations:
- currently none known


Microsoft SQL Server 'uniqueidentifier' type (GUID) sequencing
(Thanks Andrew Clute)
For those users you are using SQL Server 7.0 and up, the uniqueidentifier was introduced, and allows for your rows Primary Keys to be GUID's that are guaranteed to be unique in time and space.

However, this type is different than the Identity field type, whereas there is no way to programmatically retrieve the inserted value. Most implementations when using the u.i. field type set a default value of "newid()". The SequenceManagerMSSQLGuidImpl class manages this process for you as if it was any normal generated sequence/identity field.

Assuming that your PK on your table is set to 'uniqueidentifier', your field-description would be the same as using any other SequenceManager:

<field-descriptor
         name="guid"
         column="document_file_guid"
         jdbc-type="VARCHAR"
         primarykey="true"
         autoincrement="true"
      />
Note that the jdbc-type is a VARCHAR, and thus the attribute (in this case 'guid') on your class should be a String (SQL Server does the conversion from the String representation to the binary representation when retrieved/set).

You also need to turn on the SequenceManager in your jdbc-connection-descriptor like this:

<sequence-manager
className="org.apache.ojb.broker.util.sequence.SequenceManagerMSSQLGuidImpl"
/>

Limitations:
-This will only work with SQL Server 7.0 and higher as the uniqueidentifier type was not introduced until then.
This works well in situations where other applications might be updated the database as well, because it guarantees (well, as much as Microsoft can guarantee) that there will be no collisions between the Guids generated.


Identity based sequence manager
This sequence manager implementation supports database Identity columns (supported by MySQL, MsSQL, HSQL, ...). When using identity columns we have to do a trick to make the sequence manager work.
OJB identify each persistence capable object by a unique ojb-Identity object. These ojb-Identity objects were created using the sequence manager instance to get UID's. Often these ojb-Identity objects were created before the persistence capable object was written to database.
When using Identity columns it is not possible to retrieve the next valid UID before the object was written to database. As recently as the real object was written to database, you can ask the DB for the last generated UID. Thus in SequenceManagerNativeImpl we have to do a trick and use a 'temporary' UID till the object was written to database.
So, if it's possible try to avoid using Identity columns in your database model. If not use this sequence manager implementation to as a workaround for the Identity problem.

To enable this sequence manager implementation set in your jdbc-connection-descriptor:
<sequence-manager className="org.apache.ojb.broker.util.
                            sequence.SequenceManagerNativeImpl">
</sequence-manager>
To declare the identity column in the repository.xml file add primarykey="true", autoincrement="true" and access="readonly" to the field-descriptor for your table's primary key identity column.
<field-descriptor
        name="identifier"
        column="NATIVE_ID"
        jdbc-type="BIGINT"
        primarykey="true"
        autoincrement="true"
        access="readonly"/>

Limitations:
- The Identity columns have to start with value >= 1 and should never be negative.
- Use of Identity columns is not extent aware (This may change in further versions). More info here.

How to write my own sequence manager?

Very easy to do, just write a implementation class of the interface org.apache.ojb.broker.util.sequence.SequenceManager. OJB use a factory (SequenceManagerFactory) to obtain sequence manager instances.

This Factory can be configured to generate instances of your specific implementation by adding a sequence-manager tag in the jdbc-connection-descriptor.

<sequence-manager className="my.SequenceManagerMYImpl">
</sequence-manager>
That's it!

If your sequence manager implementation was derived from org.apache.ojb.broker.util.sequence.AbstractSequenceManager it's easy to pass configuration properties to your implementation using attribute tags.

<sequence-manager className="my.SequenceManagerMYImpl">
    <attribute attribute-name="myProperty" attribute-value="test"/>
</sequence-manager>
With
public String getConfigurationProperty(String key, String defaultValue)
method get the properties in your implementation class.

Of course we interested in your solutions! If you have implemented something interesting, just contact us.

Questions


When using sequence-name attribute in field-descriptor?
Most SequenceManager implementations based on sequence names. If you want retain control of sequencing use your own sequence-name attribute in the field-descriptor. In that case you are reponsible to use the same name across extents (see more info about extents and polymorphism). Per default the sequence manager build its own extent aware sequence name with an simple algorithm (see org.apache.ojb.broker.util.sequence.SequenceManagerHelper#buildSequenceName) if necessary.
In most cases this should be sufficient. If you have a very complex data model and you will do many metadata changes in the repository file in future, then it could be better to explicit use sequence-names in the field-descriptor. See more avoid pitfals.


What to hell does extent aware mean?
Say we have a abstract base class Animal and two classes Dog and Cat which extend Animal. For each non-abstract class we create a separate database table.
We will be able to do a query like give me all animals. Thus the uid's of Dog and Cat objects must be unique across the tables of both classes or else you may not get a vaild query result.
The reason for this behaviour is the org.apache.ojb.broker.Identity class implementation (this may change in further versions).


How could I prevent auto-build of the sequence-name?
All shipped SequenceManager implementations which using sequence names for UID generation, support by default auto-build (autoNaming) of the sequence name if none was found in the field-descriptor.
To prevent this, all relevant SM implementations support a autoNaming property - set via attribute element. If set false OJB doesn't try to build sequence names automatic.

<sequence-manager className="XYZ">
...
    <attribute attribute-name="autoNaming" attribute-value="true"/>
...
</sequence-manager>


Sequence manager handling using multiple databases
If you use multiple databases you have to declare a sequence manager in each jdbc-connection-descriptor. If you don't specify a sequence manager OJB use the default one (currently ojb.broker.util.sequence.SequenceManagerHighLowImpl).


One sequence manager with multiple databases?
OJB was intended to use a sequence manager per database. But it shouldn't be complicated to realize a global sequence manager solution by writing your own SequenceManager implementation.


Can I get direct access to the sequence manager?
That's no problem:

PersistenceBroker broker =
    PersistenceBrokerFactory.createPersistenceBroker(myPBKey);
SequenceManager sm = broker.serviceSequenceManager();
...
broker.close();
If you use autoincrement=true in your field-descriptor, there is no reason to obtain UID directly from the sequence manager or to handle UID in your object model.
[NOTE: Don't use SequenceManagerFactory#getSequenceManager(PersistenceBroker broker), this method returns a new sequence manager instance for the given broker instance and not the current used SM instance of the given PersistenceBroker instance]


Any known pitfalls?

  • When enable a sequence manager implementation based on sequence-name attributes and if the name was not set as an attribute in the field-descriptor (see), an simple algorithm was used to build the sequence name.
    The algorithm try to get the top-level class of the field's enclosing class, if no top-level class was found, the table name of the field's enclosing class was used. If a top-level class was found, the first found extent class table name was used as sequence name.
    When using base classes/interfaces with extent classes based on different database tables and the extent-class entries in repository often change, the algorithm could be corrupted, because the first found extent class's table name could be change.
    To avoid this, remove the implementation internal sequence name entry (e.g. OJB_HL_SEQ table entry when using the Hi/Lo implementation, or remove the database sequence entry when using the 'Nextval' implementation) in that case, or use custom sequence name attributes in the field descriptor.


Copyright © 1999-2003, Apache Software Foundation