"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;
    }