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).