Archive for April, 2007

Post Icon

Spring and JPA

By joost on April 16th, 2007

Spring biedt uiteraard ondersteuning voor JPA. Echter, er zijn zoveel opties, dat het nog redelijk ingewikkeld is om dit correct te configureren.

Spring heeft een tweetal beans waaronder de LocalContainerEntityManagerFactoryBean welke de JPA persistence.xml inlezen. Groot voordeel om dit met Spring te doen is, dat je niet alles in de persistence.xml hoeft te configureren. De datasource kan je bijvoorbeeld in Spring configureren. Zie hieronder.

<bean id=”entityManagerFactory” class=”org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean”>
<property name=”persistenceUnitName” value=”dino_lic” />
<property name=”dataSource” ref=”dinoDS” />
</bean>

Als je simpel JPA gebruikt, is bovenstaand het enige wat je in Spring hoeft te configureren om met JPA te kunnen werken. Als je Spring ook je transacties laat regelen, moet je ervoor zorgen dat je de juiste EntityManager gebruikt. Je mag niet zelf de EntityManager in je methodes opvragen aan de EntityManagerFactory. Bij het starten van een transactie, zal Spring een EntityManager opvragen en de transactie daarop starten. Als je toch zelf een EntityManager maakt, is dit een andere waarop geen transactie gestart is.
Als er geen transactie gestart is, zal JPA geen update uitvoeren !

JPA biedt ook dependency injection dmv annotations.

  • @PersistencyUnit kan gebruikt worden om een EntityManagerFactory te injecten.
  • @PersistenceContext injects een EntityManager instance.

Uit Spring reference, hoofdstuk 12.6.3:

While EntityManagerFactory instances are thread safe, EntityManager instances are not. The injected JPA EntityManager behave just like an EntityManager fetched from an application server’s JNDI environment, as defined by the JPA specification. It will delegate all calls to the current transactional EntityManager, if any; else, it will fall back to a newly created EntityManager per operation, making it thread safe.

Spring ondersteund ook de JPA injection annotations dmv de PersistenceAnnotationBeanPostProcessor. Door deze bean op te nemen in de Spring context, zal Spring het juiste object injecteren afhankelijk van de gebruikte annotation.

<!– JPA annotations bean post processor –>
<bean class=”org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor”/>

Er zijn diverse manier om Spring transactions voor JPA te laten managen. Zie hoofdstuk 12.7 in de Spring reference. Hieronder staat een manier.
Door de JpaTransactionManager bean op te nemen in de context, worden al opeens een hoop transactionele zaken geregeld. Het enige wat je nog moet configureren is wat je transactioneel wilt hebben. In onderstaand voorbeeld is dit gedaan dmv Aspects in Spring. In aop:config wordt een pointcut gedefinieerd en welke advisor voor die pointcut geldt. Met tx:advice wordt een transactioneel advies gemaakt welke gebruik zal maken van de JpaTransactionManager. Door meerdere tx:method elementen op te nemen, kan voor elke methode de gewenste propagation gedefinieerd worden.

Door onderstaande configuratie te gebruiken in combinatie met @PersistenceContext, zal Spring ervoor zorgen dat op de bean waarin @PersistenceContext staat, een EntityManager geinject wordt waarop de transactie is gestart.

<bean id=”myTxManager” class=”org.springframework.orm.jpa.JpaTransactionManager”>
<property name=”entityManagerFactory” ref=”entityManagerFactory” />
<property name=”jpaDialect” ref=”jpaDialect” />
</bean>
<bean id=”jpaDialect” class=”org.springframework.orm.jpa.vendor.HibernateJpaDialect” />

<aop:config>
<aop:pointcut id=”allDaoMethods”
expression=”execution(* nl.tno.nitg.dino.lic.dao.*Dao.*(..))” />
<aop:advisor advice-ref=”txAdvice”
pointcut-ref=”allDaoMethods” />
</aop:config>

<tx:advice id=”txAdvice” transaction-manager=”myTxManager”>
<tx:attributes>
<tx:method name=”*” propagation=”REQUIRED” />
</tx:attributes>
</tx:advice>

Door met log4j debugging aan te zetten voor org.springframework kan je percies zien wanneer Spring transacties start en commit.