Transacties met Spring 2

Ik ben fan van het Spring framework! Spring maakt het ontwikkelen van Java EE applicaties stukken eenvoudiger. Dit draagt mijn inziens bij aan zowel de kwaliteit van de applicatie als aan de snelheid van het ontwikkelen.

Spring 1 had als grootste nadeel de grote hoeveelheid XML configuratie die vaak noodzakelijk was. Dit is gelukkig in Spring 2 grotendeels opgelost. De voorbeelden hieronder zijn dan ook gemaakt in het Spring 2 formaat.

Spring sluit ook goed aan bij de visie van Sogyo rondom domein gedreven ontwikkelen; mits de inzet van Spring beperkt wordt tot alleen de “plumbing” lagen rondom het domein, en het domein zelf alleen uit de bekende POJO’s bestaat.

Laten we als voorbeeld de Spring ondersteuning voor transacties nemen. Dit laat mooi de “non intrusive” visie van het Spring framework zien. Java EE biedt standaard twee manieren om gebruik te maken van transacties:

De eerste manier is declaratief via EJB’s. Als je hiervoor kiest krijg je alle andere overhead van het gebruik van EJB’s er gratis en voor niets bij cadeau. Overigens heeft EJB 3 deze overhead gelukkig wel fors verminderd.

De tweede manier is programmatisch transactie management. Je code doet een JNDI lookup naar java:comp/UserTransaction, om een usertransaction object te verkrijgen. Vervolgens moet in de code op dit usertransaction object steeds weer begin() en commit() of rollback() aangeroepen worden.

Spring biedt een combinatie van beide bovenstaande manieren: declaratief transactiemanagement, maar dan met POJO’s. Naast dat Spring de mogelijkheid biedt, is het ook nog eens erg eenvoudig om te configureren. Allereerst moeten we een transactiemanager opnemen in de Spring configuratie. De onderstaande tag is hiervoor al voldoende:

<tx:jta-transaction-manager />

Vervolgens kan met behulp van Spring AOP aangegeven worden welke methodes transactioneel moeten worden. Spring 2 gebruikt voor AOP de AspectJ pointcut expression language. Stel we hebben een java package genaamd ‘nl.sogyo.voorbeeldapplicatie.service’. Deze package bevat een aantal interfaces waarvan de naam eindigt op Service. Om de implementatie van de methodes uit deze interfaces transactioneel te maken komt het volgende in de Spring configuratie te staan:

<aop:config>
    <aop:advisor
        pointcut="execution(* nl.sogyo.voorbeeldapplicatie.services.*Service.*(..))"
        advice-ref="txAdvice" />
</aop:config>  

<tx:advice id="txAdvice">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED" />
    </tx:attributes>
</tx:advice>

De transaction propagation attributen zijn hetzelfde als bij EJB’s. Dus er kan naast REQUIRED ook MANDATORY, SUPPORTS, REQUIRES_NEW, NEVER en NOT_SUPPORTED gebruikt worden.

Hetgeen rest is het desbetreffende object uit de Spring applicatiecontext ophalen, zodat Spring er een proxy object tussen kan schuiven. Wanneer we zelf het object instancieren met de new operator zal het dus niet werken. Verder blijft de code ongewijzigd. Dit heeft als voordeel dat de code ook buiten een container gedraaid kan worden, bijvoorbeeld in een unittest.