HSQLDB unterstützt seit einiger Zeit Sequences. Mit Sequences lassen sich sehr einfach Primärschlüssel generieren was von Hibernate mit dem Sequences-Generator auch direkt unterstützt wird. Will man das Default-Verhalten "eine Sequence für alle Entitäten" aber ersetzen durch "eine Sequence per Entität", so stößt man mit HSQLDB 1.8 und Hibernate 3.1.3 auf Probleme.
Das Problem liegt an den von Hibernate erzeugten SQL Statements zum "fetchen" der Sequences:
select next value for hibernate_sequence from dual_hibernate_sequence
Zum einen ist eine solche Tabelle in der Standardinstallation gar nicht vorhanden, zum anderen hängt der Name der "Fetch"-Tabelle vom Namen der Sequence ab.
Man würde also pro Entität einen "Fetch"-Table brauchen.
Abhilfe schafft eine kleine Erweiterung des HSQLDialects von Hibernate 3.1:
package com.rinke.solutions.hibernate;
import org.hibernate.dialect.HSQLDialect;
public class MyHSQLDialect extends HSQLDialect {
@Override
public String getSequenceNextValString(String sequenceName) {
return "select next value for " + sequenceName
+ " from hsql_dual";
}
}
Damit werden nun alle Seqences von einem Table gefetched. Dieser eine Table muss nun natürlich noch angelegt werden. Wie er konkret aussieht ist eigentlich egal, Hauptsache ein besitzt nur eine Zeile, damit das Select-Statement auch genau nur einen "Next Value" zurück liefert:
create table hsql_dual ( id integer );
insert into hsql_dual values ( 1 );
Die Verwendung von einer Sequence per Entität erreicht man am einfachsten mit einem eigenen Identity-Generator. Statt "sequence" schreiben wir auf hier unsere eigene Klasse:
package com.rinke.solutions.hibernate;
import java.util.Properties;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.SequenceGenerator;
import org.hibernate.type.Type;public class TableNameSequenceGenerator extends SequenceGenerator {
/**
* If the parameters do not contain a {@link SequenceGenerator#SEQUENCE} name, we
* assign one based on the table name.
*/
@Override
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
if(params.getProperty(SEQUENCE) == null || params.getProperty(SEQUENCE).length() == 0) {
String tableName = params.getProperty(PersistentIdentifierGenerator.TABLE);
if(tableName != null) {
String seqName = "seq_" + tableName;
params.setProperty(SEQUENCE, seqName);
}
}
super.configure(type, params, dialect);
}
}
Ein komplettes Beispiel (verwendet zusätzlich Spring aber nur der Einfachheit wegen) kann hier heruntergeladen werden.
Referenzen: CustomSequences mit Hibernate: http://www.hibernate.org/296.html