Reputation: 43
I am working on an existing project using Spring and Hibernate and am getting confused because I get a
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
error when trying to save objects but I still can't find what exactly is wrong.
There is a service layer that is annotated using @Service
and a save
method that it should be transactional so it is annotated with @Transactional(readOnly = false)
. To me that means that spring should handle transactions itself.
@Service
public class LadyService {
Logger log = Logger.getLogger(LadyService.class);
@Autowired
private PictureDAO pictureDao;
@Autowired
private LadyDAO ladyDao;
@Autowired
private AddressDAO addressDao;
@Transactional(readOnly = false)
public void save(Lady lady) {
Address a = addressDao.getExistingAddress(lady.getAddress());
if (a == null) {
a = addressDao.save(lady.getAddress());
}
lady.setAddress(a);
ladyDao.save(lady);
pictureDao.savePictures(lady.getPictures());
}
The error happens when doing a save in the AddressDAO
. It is annotated as @Repository
.
@Repository
public class AddressDAO extends HibernateDaoSupport {
public Address save(Address address) {
getHibernateTemplate().save(address); <-- write not permitted error happens here
return address;
}
@SuppressWarnings({ "unchecked" })
public Address getExistingAddress(Address address) {
DetachedCriteria cd = DetachedCriteria.forClass(Address.class);
cd.add(Restrictions.eqOrIsNull("administrative_area_level_1",
address.getAdministrative_area_level_1()));
cd.add(Restrictions.eqOrIsNull("administrative_area_level_2",
address.getAdministrative_area_level_2()));
List<Address> result = (List<Address>) getHibernateTemplate()
.findByCriteria(cd);
if (result.isEmpty()) {
return null;
} else {
return (Address) result.get(0);
}
}
}
What I thought would happen was that @Transactional
makes spring create a session and a transaction for the save on the service layer, and that in the DAOs, the hibernate template would get the current session and transaction that spring manages and use it to save the objects.
The error message, though, makes me think that my service method and dao methods are not in the same transaction.
In the servlet-context.xml there are these statements:
<annotation-driven />
<context:component-scan base-package="com.kog.fable" />
<beans:bean id="mySessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<beans:property name="dataSource" ref="myDataSource" />
<beans:property name="packagesToScan">
<beans:array>
<beans:value>com.kog.fable.**.*</beans:value>
</beans:array>
</beans:property>
<beans:property name="hibernateProperties">
<beans:props>
<beans:prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect
</beans:prop>
<!-- create, validate, update -->
<beans:prop key="hibernate.hbm2ddl.auto">create</beans:prop>
<beans:prop key="hibernate.show_sql">false</beans:prop>
<beans:prop key="hibernate.connection.pool_size">10</beans:prop>
<beans:prop key="hibernate.connection.autocommit ">false</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<beans:bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<beans:property name="sessionFactory" ref="mySessionFactory" />
</beans:bean>
<beans:bean id="addressDAO" class="com.kog.fable.dao.AddressDAO">
<beans:property name="sessionFactory" ref="mySessionFactory" />
</beans:bean>
<beans:bean id="ladyDAO" class="com.kog.fable.dao.LadyDAO">
<beans:property name="sessionFactory" ref="mySessionFactory" />
</beans:bean>
<beans:bean id="pictureDAO" class="com.kog.fable.dao.PictureDAO">
<beans:property name="sessionFactory" ref="mySessionFactory" />
</beans:bean>
Here I don't understand why, if component scan is used, the DAO beans are still declared explicitly. Shouldn't the component scan feature be able to create those by itself since the DAO classes are annotated with @Repository
?
Since I thought this configuration could create duplicate beans, I tried deleting the xml entries, but then I started getting:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'addressController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.kog.fable.dao.AddressDAO com.kog.fable.controller.AddressController.addressDAO; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'addressDAO' defined in file [***\com\kog\fable\dao\AddressDAO.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: 'sessionFactory' or 'hibernateTemplate' is required
Here I thought that the extension of HibernateDaoSupport for my DAOs would make them inherit the sessionFactory and related methods so I don't understand what happens.
I have read that I could set the flush mode to AUTO or set the setCheckWriteOperations on the template to FALSE to solve that kind of problems and it seems to work, but I guess that this would not ensure the transaction coherence in all cases as I would like it.
Any help would be appreciated as I am quite new to Spring and Hibernate and am a bit stuck here.
Upvotes: 4
Views: 15330
Reputation: 107
do the change:-
@Configuration
@EnableTransactionManagement <-----Put this line
public PersistenceConfig{
//your code
}
(OR)
@Bean
@Autowired
public HibernateTemplate getHibernateTemplate(SessionFactory session) {
HibernateTemplate hb = new HibernateTemplate();
hb.setCheckWriteOperations(false);
hb.setSessionFactory(session);
return hb;
}
Upvotes: 0
Reputation: 672
if your app is Spring MVC based...
in application-context try this..
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="yourSessionFactory"></property>
</bean>
but in dispatcher-servlet (not in appContext !!!)
<tx:annotation-driven />
Dont forget namespaces for tx, and jar libraries spring-tx,spring-orm,hibernate-core
Upvotes: 1
Reputation: 124581
When extending HibernateDaoSupport
you will not benefit from autowiring, you will have to override the setSessionFactory
method and put an @Autowired
annotation on it. Else it won't work.
I would also expect a <tx:annotation-driven />
without that the @Transactional
is pretty much useless and doesn't do anything.
Upvotes: 5