EDIT (2011/01/21):* See
here for the latest version of the code (Hibernate 3.6.0.Final).
* See
here for how to use with annotations.
* See
here for how to declare it at package level when using annotations.
* See
here for a bug in Oracle 10.2.0.0 to 11.1.0.7 that may prevent a correct working of this code.
I am using a legacy Oracle database where the values of the primary keys are assigned from a sequence using a "before insert" trigger.
However I couldn't find an id generator for this case: there is the SequenceIdentityGenerator in Hibernate (sequence-identity), but that one increments the sequence itself by doing "insert (sequence.nextval, ...) returning id". In my case, the sequence is incremented by the trigger. So I had to do "insert (...) returning id" instead.
Therefore I have built my own id generator, based on the code of the sequence-identity generator (not extending it, because I don't require to access the sequence; the "before insert" trigger does the job with the sequence and set the id in the data before it is saved in the db).
As I have seen a few people in this forum with a similar requirement, I'd like to share the code of this id generator with you.
You can copy it and modify freely, but please keep my name in it.
Note: like the sequence-identity generator, it requires JDK 1.4 and an Oracle 10 JDBC driver (however I have tested it from a 10g driver connected to a 9i db and it works). The minimum version of the JDBC driver that supports the required feature is 10.2.0.1 (minimum recommended version is 10.2.0.3).
Code:
package jpl.hibernate.util;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.id.AbstractPostInsertGenerator;
import org.hibernate.id.IdentifierGeneratorFactory;
import org.hibernate.id.PostInsertIdentityPersister;
import org.hibernate.id.SequenceIdentityGenerator.NoCommentsInsert;
import org.hibernate.id.insert.AbstractReturningDelegate;
import org.hibernate.id.insert.IdentifierGeneratingInsert;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.util.NamedGeneratedKeysHelper;
/**
* A generator with immediate retrieval through JDBC3 {@link java.sql.Connection#prepareStatement(String, String[]) getGeneratedKeys}.
* The value of the identity column must be set from a "before insert trigger"
* <p/>
* This generator only known to work with newer Oracle drivers compiled for
* JDK 1.4 (JDBC3). The minimum version is 10.2.0.1
* <p/>
* Note: Due to a bug in Oracle drivers, sql comments on these insert statements
* are completely disabled.
*
* @author Jean-Pol Landrain
*/
public class TriggerAssignedIdentityGenerator extends AbstractPostInsertGenerator {
public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(PostInsertIdentityPersister persister, Dialect dialect, boolean isGetGeneratedKeysEnabled) throws HibernateException {
return new Delegate(persister, dialect);
}
public static class Delegate extends AbstractReturningDelegate {
private final Dialect dialect;
private final String[] keyColumns;
public Delegate(PostInsertIdentityPersister persister, Dialect dialect) {
super(persister);
this.dialect = dialect;
this.keyColumns = getPersister().getRootTableKeyColumnNames();
if (keyColumns.length > 1) {
throw new HibernateException("trigger assigned identity generator cannot be used with multi-column keys");
}
}
public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
NoCommentsInsert insert = new NoCommentsInsert(dialect);
return insert;
}
protected PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException {
return session.getBatcher().prepareStatement(insertSQL, keyColumns);
}
protected Serializable executeAndExtract(PreparedStatement insert) throws SQLException {
insert.executeUpdate();
return IdentifierGeneratorFactory.getGeneratedIdentity(NamedGeneratedKeysHelper.getGeneratedKey(insert), getPersister().getIdentifierType());
}
}
}
To use it, in your mapping files:
Code:
<id name="id"
column="MECA_UID">
<generator class="jpl.hibernate.util.TriggerAssignedIdentityGenerator" />
</id>
You can add to this forum in case you find a bug.
Please, also let me know if it has helped you.
Cheers,
Jean-Pol.