"Write operations are not allowed in read-only mode" error : confused with Spring @Service @transaction @Repository and Hibernate
"Write operations are not allowed in read-only mode" error : confused with Spring @Service @transaction @Repository and Hibernate
我正在使用 Spring 和 Hibernate 处理一个现有项目,我感到很困惑,因为我得到了一个
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.
尝试保存对象时出错,但我仍然找不到具体问题所在。
有一个使用 @Service
注释的服务层和一个 save
方法,它应该是事务性的,因此使用 @Transactional(readOnly = false)
注释。对我来说,这意味着 spring 应该自己处理交易。
@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());
}
在 AddressDAO
中进行保存时发生错误。它被注释为 @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);
}
}
}
我认为会发生的是 @Transactional
使 spring 创建一个会话和一个事务以保存在服务层上,而在 DAO 中,hibernate 模板将获得当前spring 管理并使用它来保存对象的会话和事务。
不过,错误消息让我觉得我的服务方法和 dao 方法不在同一个事务中。
在servlet-context.xml中有这些语句:
<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>
这里我不明白为什么,如果使用组件扫描,DAO bean 仍然是显式声明的。由于 DAO 类 用 @Repository
注释,组件扫描功能不应该能够自己创建它们吗?
由于我认为此配置可能会创建重复的 bean,因此我尝试删除 xml 条目,但随后我开始收到:
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
这里我认为我的 DAO 的 HibernateDaoSupport 扩展会让它们继承 sessionFactory 和相关方法,所以我不明白发生了什么。
我读到过我可以将刷新模式设置为 AUTO 或将模板上的 setCheckWriteOperations 设置为 FALSE 来解决此类问题并且它似乎有效,但我想这不能确保事务的一致性所有情况都如我所愿。
任何帮助将不胜感激,因为我对 Spring 和 Hibernate 还很陌生,有点卡在这里。
当扩展 HibernateDaoSupport
时,您将无法从自动装配中获益,您将不得不覆盖 setSessionFactory
方法并在其上放置一个 @Autowired
注释。否则它不会工作。
我还希望 <tx:annotation-driven />
没有 @Transactional
几乎没用,什么也不做。
如果您的应用Spring 基于 MVC...
在应用程序上下文中试试这个..
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="yourSessionFactory"></property>
</bean>
但在 dispatcher-servlet 中(不在 appContext 中!!!)
<tx:annotation-driven />
不要忘记 tx 和 jar 库的命名空间 spring-tx,spring-orm,hibernate-core
在配置文件中
进行更改:-
@Configuration
@EnableTransactionManagement <-----Put this line
public PersistenceConfig{
//your code
}
(或)
@Bean
@Autowired
public HibernateTemplate getHibernateTemplate(SessionFactory session) {
HibernateTemplate hb = new HibernateTemplate();
hb.setCheckWriteOperations(false);
hb.setSessionFactory(session);
return hb;
}
我正在使用 Spring 和 Hibernate 处理一个现有项目,我感到很困惑,因为我得到了一个
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.
尝试保存对象时出错,但我仍然找不到具体问题所在。
有一个使用 @Service
注释的服务层和一个 save
方法,它应该是事务性的,因此使用 @Transactional(readOnly = false)
注释。对我来说,这意味着 spring 应该自己处理交易。
@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());
}
在 AddressDAO
中进行保存时发生错误。它被注释为 @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);
}
}
}
我认为会发生的是 @Transactional
使 spring 创建一个会话和一个事务以保存在服务层上,而在 DAO 中,hibernate 模板将获得当前spring 管理并使用它来保存对象的会话和事务。
不过,错误消息让我觉得我的服务方法和 dao 方法不在同一个事务中。
在servlet-context.xml中有这些语句:
<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>
这里我不明白为什么,如果使用组件扫描,DAO bean 仍然是显式声明的。由于 DAO 类 用 @Repository
注释,组件扫描功能不应该能够自己创建它们吗?
由于我认为此配置可能会创建重复的 bean,因此我尝试删除 xml 条目,但随后我开始收到:
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
这里我认为我的 DAO 的 HibernateDaoSupport 扩展会让它们继承 sessionFactory 和相关方法,所以我不明白发生了什么。
我读到过我可以将刷新模式设置为 AUTO 或将模板上的 setCheckWriteOperations 设置为 FALSE 来解决此类问题并且它似乎有效,但我想这不能确保事务的一致性所有情况都如我所愿。
任何帮助将不胜感激,因为我对 Spring 和 Hibernate 还很陌生,有点卡在这里。
当扩展 HibernateDaoSupport
时,您将无法从自动装配中获益,您将不得不覆盖 setSessionFactory
方法并在其上放置一个 @Autowired
注释。否则它不会工作。
我还希望 <tx:annotation-driven />
没有 @Transactional
几乎没用,什么也不做。
如果您的应用Spring 基于 MVC...
在应用程序上下文中试试这个..
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="yourSessionFactory"></property>
</bean>
但在 dispatcher-servlet 中(不在 appContext 中!!!)
<tx:annotation-driven />
不要忘记 tx 和 jar 库的命名空间 spring-tx,spring-orm,hibernate-core
在配置文件中
进行更改:-
@Configuration
@EnableTransactionManagement <-----Put this line
public PersistenceConfig{
//your code
}
(或)
@Bean
@Autowired
public HibernateTemplate getHibernateTemplate(SessionFactory session) {
HibernateTemplate hb = new HibernateTemplate();
hb.setCheckWriteOperations(false);
hb.setSessionFactory(session);
return hb;
}