Reputation: 379
I'm trying to implement a read-only
transaction for a service method, following item 11.5.2 of the Spring Framework Reference, but the transaction still automatically commits data to the DB.
I'm using Spring 3.1.0.RELEASE, Hibernate 3.5.5-Final and Oracle 11g Express Edition Release 11.2.0.2.0. Here is my setup:
The XML for the advice, the pointcut, the advisor and the transaction manager:
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="myServiceMethods" expression="execution(* my.example.service.*.*(..))" />
<aop:advisor pointcut-ref="myServiceMethods" advice-ref="transactionAdvice" />
</aop:config>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
<!-- the sessionFactory bean declaration does not set the -->
<!-- hibernate.connection.autocommit property as either true or false -->
</bean>
The service interface:
package my.example.service;
public interface MyService {
void getFoo();
void bar();
}
The service implementation:
package my.example.service.impl;
import my.example.dao.MyDao;
import my.example.domain.MyEntity;
import my.example.service.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MyServiceImpl implements MyService {
@Autowired
private MyDao dao;
public void getFoo() {
MyEntity example = this.dao.getMyEntity(1L);
example.setSomeInteger(2);
example.setSomeString("three");
}
public void bar() {
MyEntity example = this.dao.getMyEntity(4L);
example.setSomeInteger(5);
example.setSomeString("six");
}
}
After calling either getFoo()
or bar()
, the DB is updated, even though getFoo()
is marked as read-only
. But if I change these two lines:
<tx:method name="get*" read-only="true" />
<tx:method name="*"/>
to:
<tx:method name="*" read-only="true" />
both methods respect the read-only
property and data does not get committed to the DB.
What's going on? What am I doing wrong? What did I miss?
Upvotes: 4
Views: 4661
Reputation: 379
I found out why the read-only
transactions were being overriden. There is an OpenSessionInViewFilter declared in web.xml
, which previous developers used to prevent LazyInitializationException
s when loading the user and its roles in the controller/interface.
Here is the filter declaration:
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
I worked around the issue by configuring the filter to let each transaction use its own session (called deferred mode
).
Here is the filter declaration with deferred mode
activated:
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>singleSession</param-name>
<param-value>false</param-value>
</init-param>
</filter>
Please, read the NOTE in the filter's Javadoc. My DAO class extends org.springframework.orm.hibernate3.support.HibernateDaoSupport
, and the the Hibernate Session returned by the inherited methods getSession()
or getSessionFactory().getCurrentSession()
had its flush mode is set to FlushMode.MANUAL
, instead of FlushMode.NEVER
, as I exptected after reading this SO answer.
Upvotes: 4
Reputation: 6540
Why don't you just use the readOnly flag on the @Transactional
annotation.
Annotate your method with @Transactional(readOnly=true)
and Hibernate will attempt to perform the transaction in a read-only manner and (I think, you may want to double check this) will throw an exception if a write is attempted.
There is no point in trying to re-invent the wheel, where this is concerned.
Upvotes: 0