Archiv für die Kategorie ‘Tools’

PostgreSQL-Funktionen mit ANT einspielen

Mittwoch, 12. August 2009

Um eine Datenbank auf dem aktuellen Stand zu halten, bietet sich ANT mit dem Task sql an. Hiermit ist es ein leichtes viele SQL-Skripte effektiv in die Datenbank einzuspielen:

<sql
    driver="org.database.jdbcDriver"
    url="jdbc:database-url"
    userid="sa"
    password="pass"
    src="data.sql"/>

Der Lösung so nah

Dies funktioniert soweit sehr gut, es sei denn man versucht Funktionen in die Datenbank einzuspielen. ANT kann leider nicht mit den Semikoli (;) in der Funktion richtig umgehen; so wird davon ausgegegangen das jedes Semikolon ein Statement beendet. Dies trifft beim Einspielen einer Funktion natürlich nicht zu. Hierfür bietet ANT eine schnelle Lösung mit dem Attribute “delimiter” am sql-Task. In dem folgenden Beispiel wird der Default-Delimiter (;) durch “$$” ersetzt.

<sql
    driver="org.database.jdbcDriver"
    url="jdbc:database-url"
    userid="sa"
    password="pass"
    src="data.sql"
    delimiter="$$"/>

Einfacher Fall

Das SQL der einzuspielenden Funktion für PostgreSQL sollte in etwa wie folgt aussehen:

CREATE OR REPLACE FUNCTION hello_world() RETURNS void AS '
BEGIN
    -- Aktionen der Funktion
END;
' LANGUAGE 'plpgsql' $$

Das SQL-Statement erstellt eine Funktion ohne Rückgabewerte, definiert danach die verwendete Sprache und beschliesst das Statement mit dem Delimiter ($$).
Ein kleines aber sehr wichtiges Detail sind hierbei die Hochkomma (’) hinter dem AS und vor dem LANGUAGE. Auch wenn bei der Verwendung von PgAdmin fast jede beliebiege Zeichenkette dort stehen kann sollte bei der Verwendung von ANT nur das Hochkomma verwendet werden, so das der JDBC den Inhalt der Funktion als Text auffasst und nicht versucht den Inhalt zu interpretieren.

Komplexer Fall

Doch was tun, wenn innerhalb der Funktion Texte verwendet werden, die selbst mit einem Hochkomma (’) eingefasst sind. In diesem Fall muss man darauf achten, das alle Hochkommas innerhalb der Funktion mit einem Hochkomma “escaped” werden.

CREATE OR REPLACE FUNCTION hello_world() RETURNS void AS '
BEGIN
    insert into my_table (text) values (''hello'');
END;
' LANGUAGE 'plpgsql' $$

Thomas Bader


Transaktionssteuerung mit Spring AOP

Freitag, 24. April 2009

Spring bietet uns mit AOP die Möglichkeit innerhalb der Spring Konfiguration die Behandlung der Datenbanktransaktionen zu steuern. Hierfür benötigen wir lediglich eine AOP Konfiguration, die auf alle Methoden zeigt, die innerhalb einer Transaktion ablaufen sollen und den Spring Tag .

In diesem Beispiel gehen wir von zwei Service Klassen aus, deren Methoden von einer Transaktion umschlossen werden sollen.

applicationContext.xml:

<-- service beans -->
<bean id="fooService" class="a.b.service.FooService" />
<bean id="barService" class="a.b.service.BarService" />
 
<-- hibernate transaction manager -->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
 
<-- hibernate session factory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource">
        <ref bean="dataSource" />
    </property>
    <property name="annotatedClasses">
        <list>....</list>
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
        </props>
    </property>
</bean>

Innerhalb des tx:advice definieren wir das Verhalten für die einzelnen Methoden der Services. Für alle Methodenaufrufe werden die gleichen Isolation- und Propagation-Level benutzt. Für Methoden die mit „find“ oder „get“ beginnen setzen wir die Transaktion noch auf read-only. In diesem Methoden dürfen jetzt nur noch Datenbank SELECTs ausgeführt werden, anderenfalls würde es eine Exception geben. Die Datenbank kann so das Locking auf Zeilen, die von diesem Methodenaufrufen betroffen, sind optimieren.

applicationContext.xml:

<tx:advice id="defaultTranscationAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="*" isolation="READ_COMMITTED" propagation="REQUIRED" />
        <tx:method name="find*" read-only="true" />
        <tx:method name="get*" read-only="true" />
    </tx:attributes>
</tx:advice>

Als letztes brauchen wir jetzt noch die AOP Konfiguration, die das defaultTransactionAdvice mit den Service Klassen verbindet. Durch die Definition „a.b.service.*.*(..)“ wird um jede Methode in jeder Klasse innerhalb des Paketes a.b.service eine Transaktion gelegt. Es können so weitere Service Klassen hinzugefügt werden, ohne dass das Transaktion Handling angepasst werden muss.

applicationContext.xml:

<aop:config>
    <aop:pointcut id="allServiceMethods" expression="execution(* a.b.service.*.*(..))" />
    <aop:advisor advice-ref="defaultTranscationAdvice" pointcut-ref="allServiceMethods" />
</aop:config>

Innerhalb der Service Methoden brauchen wir uns nun keine Gedanken mehr um offene oder geschlossene Transaktionen machen. Wir bewegen uns in jedem Fall innerhalb einer Transaktion. Sollte es zu einem Fehler kommen, so wird ein Rollback auf allen Änderungen der Methode durchgeführt.
Erst beim Verlassen der Service Methode müssen wir berücksichtigen, dass Objekte ggf. nicht mehr an eine Session gebunden sind (hier kann es dann zu Problemen mit LazyLoading o.ä. kommen).


Felix Breske


Maven2 in Eclipse nutzen

Dienstag, 29. April 2008

Viele Projekte verwenden mittlerweile Maven2 für Build, Deployment, usw. Um Maven aus Eclipse heraus relativ komfortabel zu nutzen, sind nicht unbedingt Plugins nötig. Mit wenigen Handgriffen ist das lokale Repository konfiguriert und häufig genutzte Goals eingerichtet.
Plugins bieten zwar Vorteile, z.B. bei der Verwaltung der Dependencies, sind aber leider (noch) an vielen Stellen unflexibel, nicht stabil und umständlich zu benutzen.

Voraussetzung ist eine lauffähige Maven2 Version (Kommandozeile) und im Betriebssystem die Path-Variable MAVEN_HOME, die auf das Maven Installationsverzeichnis verweist (dort wo man Maven entpackt hat).
Die folgenden Schritte wurden mit Eclipse 3.3 durchgeführt.

Lokales Repository zum Buildpath hinzufügen

Damit in Eclipse das Maven Repository gefunden wird, muss die Classpath-Variable M2_REPO angelegt und auf das lokale Repository verlinkt werden.
Das geschieht über das Menü:
Window – Preferences – Java – Build Path – Classpath Variables
Hier mit “New” die Variable M2_REPO anlegen und den Path auf das lokale Maven Repository setzen.
Unter Windows liegt das lokale Repository in der Regel unter:
C:\Dokumente und Einstellungen\<Benutzername>\.m2\repository
Bei Linux unter:
/home/<Benutzername>/.m2/repository

Maven Goals konfigurieren

Hierfür bietet es sich an die “External Tools Launch Configuration” von Eclipse zu nutzen mit der, wie der Name vermuten lässt, externe Programme aufgerufen werden können.

1. Über die Schaltfläche External Tools den Open External Tools Dialog … öffnen.

2. Unter Program eine New launch configuration anlegen.

3. Location: ${env_var:MAVEN_HOME}\bin\mvn.bat
Hiermit wird über die Systemvariable MAVEN_HOME die aktuelle Maven Version aufgerufen.
Z.B. liegt die Maven Installation im Verzeichnis: c:/dev/maven-2.0.7 auf welches die Variable MAVEN_HOME verweist. Eine neuere Maven Version packt man im Verzeichnis c:/dev/maven-2.0.8 aus und stellt nur die MAVEN_HOME Variable entspr. um.

4. Working Directory: ${project_loc}
Nun wollen wir nicht für jedes Projekt eine eigene Launch Configuration anlegen. Das nervt und wird schnell sehr unübersichtlich. Auch hierfür biete Eclipse eine eigene Variable an: ${project_loc} verweist auf das aktuell ausgewählte Projekt.

5. Arguments: install -DcreateChecksum=true
Hier werden Maven die Kommandozeilen-Argumente übergeben. In diesem Fall also ein einfaches install mit Erzeugen der Checksum. Ein weiteres gutes Beispiel ist das eclipse:eclipse Goal. Mit diesen beiden Goals sind schon mal die Grundbedürfnisse erfüllt.

Ausführen eines Maven Goal

Im z.B. Package Explorer ein Maven Projekt auswählen (anklicken), hierdurch wird die Eclipse Variable ${project_loc} gesetzt. Anschließend aus den External Tools das gewünschte Goal auswählen und Maven startet in der Console.
Fertig :-)

Ein typischer Arbeitsschritt wäre z.B.:

  • Anpassungen am pom.xml machen
  • Goal eclipse:eclipse ausführen
  • Refresh des Projekts (F5) -> kann man auch durch die Launch Configuration auto. durchführen lassen
  • Goal install ausführen

BTW: Zusätzlich fügt man diese Launch Configurations noch zu den Favoriten hinzu, dann geht es noch schneller.

Beispiele:
Folgende Goals verwende ich wie oben beschrieben.

  • install -DcreateChecksum=true
  • eclipse:eclipse -DdownloadSources=true -DdownloadJavadocs=true
  • Da ab maven 2.0.10 beim eclipse Goals auch Projekte referenziert werden, dies aber nicht immer gewünscht ist, kann man dieses Feature abschalten

  • eclipse:eclipse -DdownloadSources=true -DdownloadJavadocs=true -Declipse.useProjectReferences=false

Viel Spaß und Erfolg beim Ausprobieren und Anwenden


Christof Aenderl