29Aug

So ein SequenceGenerator ist schon eine feine Sache, vorallem bei der Verwendung von Relationalen Datenbanksystemen á la Oracle. Musste man sich ohne die Vorzüge des ORM (Object-Relational-Mapping) noch mit der händischen Erstellung von Sequenzen zur Incrementierung von Werten herumschlagen, erledigt Hibernate das Ganze voll automatisch mit zwei Zeilen Code.

Die Tücken der Technik

In meinem letzten Projekt zeigte sich jedoch ein interessantes Phänomen: Ich erstellte eine Klasse mit Annotation und einer Autoincrement-Sequence zum Hochzählen der Id.

@Entity
@SequenceGenerator(name = "user_seq", sequenceName = "user_id_seq")
public class User implements Serializable { ... }

Zunächst schien alles zu funktionieren, als ich aber einige Zeit später in die Datenbank schaute entdeckte ich seltsame Sprünge zwischen den Ids:

 

id

name

age

50

Fred

31

51

Harry

25

100

Mike

13

101

Frank

46

102

Richard

52

150

Ted

31

151

Kyle

29

152

Steve

34

153

Michael

42

 

Nach einigem Probieren stellte ich fest, dass die Sprünge stets nach dem Neustart der Anwendung auftraten. Das Problem konnte ich schließlich nach etwas Recherche im Internet ausmachen. Für den SequenceGenerator existiert ein Parameter allocationSize, welcher die definierte Sequenz um den angegeben Wert erhöht und anschließend diese Nummern vergibt. Erst wenn alle verbraucht sind, wird die Sequenz erneut aufgerufen. Der Nachteil dieser performanten Vorgehensweise ist, dass bei einem Neustart der Anwendung die zwischengspeicherten Werte verloren gehen und dadurch Lücken zwischen den Id’s entstehen. Standardmäßig ist diese Einstellung auf 50 gesetzt, was auch den Abstand in der Datenbank erklärt. Die nachfolgende Einstellung schafft also Abhilfe:

@Entity
@SequenceGenerator(name = "user_seq", sequenceName = "user_id_seq" allocationSize=1)
public class User implements Serializable { ... }

Nun wird zur Generierung des nächsten Primary-Keys immer die Datenbanksequenz bemüht, was die Performance zwar etwas mindert, jedoch bleiben die Lücken aus.

 

Links: http://www.galileocomputing.de/artikel/gp/artikelID-328

Technorati Tags: , ,

28Apr

 Nach dem Neuaufsetzen einer bestehenden Grails-Anwendung, tauchte nachfolgender Fehler während der Ansprache der Datenbank durch die Applikation auf:

Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: bigint ~~ bigint
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:1512)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1297)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:188)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:437)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:353)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:257)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:92)
... 2 more

Zunächst vermutete ich ein JDBC Treiber-Problem, jedoch konnte der Fehler nach etwas Recherche und Debugging auf das Object-Mapping eingegrenzt werden. Das Kernproblem ließ sich dann letzendlich seitens der Datenbank bzw. deren Ansprache durch das Grails ORM identifizieren - ich hatte versehentlich eine falsche Version der PostgreSQL Datenbank installiert. Da Grails Hibernate als Persistenz-Framework verwendet (GORM) und  dieses nur Postgres bis zur Version 8.1 unterstützt,  quittierte meine Application die meisten Datenbankoperationen mit obiger Meldung.

Links: Von Hibernate unterstützte Datenbanken

 

Technorati Tags: , ,

10Jan

Standardmäßig generiert die Hibernate Andromda Cartridge die Mappingdateien so, dass eine Sequenz für alle Tabellen genutzt wird. Eine Umstellung auf eine Sequenz für jede Tabelle ist dabei sehr einfach möglich. In der andromda.xml muss die Hibernate Cartridge einfach wie folgt angepaßt werden:

<namespace name="hibernate">
<properties>
...
<property name="defaultHibernateGeneratorClass">sequence</property>
<property name="sequenceIdentifierSuffix">_seq</property>
...
</properties>
</namespace>

Der finale Sequzenzname ergibt sich dabei aus dem Tabellennamen plus den Wert, der der Property sequenceIdentifierSuffix zugeordnet ist.