java “在只读模式下不允许写操作”错误:与 Spring @Service @transaction @Repository 和 Hibernate 混淆

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28237252/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 13:15:04  来源:igfitidea点击:

"Write operations are not allowed in read-only mode" error : confused with Spring @Service @transaction @Repository and Hibernate

javaspringhibernatetransactions

提问by Flop000

I am working on an existing project using Spring and Hibernate and am getting confused because I get a

我正在使用 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.

org.springframework.dao.InvalidDataAccessApiUsageException:在只读模式下不允许写入操作 (FlushMode.MANUAL):将您的会话转换为 FlushMode.COMMIT/AUTO 或从事务定义中删除“readOnly”标记。

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 @Serviceand a savemethod that it should be transactional so it is annotated with @Transactional(readOnly = false). To me that means that spring should handle transactions itself.

有一个使用注释的服务层@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());
    }

The error happens when doing a save in the AddressDAO. It is annotated as @Repository.

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

What I thought would happen was that @Transactionalmakes 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.

我认为会发生的是@Transactional让 spring 在服务层上创建一个会话和一个事务来保存,在 DAO 中,hibernate 模板将获取 spring 管理的当前会话和事务并使用它来保存对象。

The error message, though, makes me think that my service method and dao methods are not in the same transaction.

但是,错误消息让我认为我的 service 方法和 dao 方法不在同一个事务中。

In the servlet-context.xml there are these statements:

在 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>

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:

这里我不明白为什么,如果使用组件扫描,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

org.springframework.beans.factory.BeanCreationException:创建名为“addressController”的 bean 时出错:自动装配依赖项的注入失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:无法自动装配字段:private com.kog.fable.dao.AddressDAO com.kog.fable.controller.AddressController.addressDAO;嵌套异常是 org.springframework.beans.factory.BeanCreationException:在文件 [***\com\kog\fable\dao\AddressDAO.class] 中定义名称为“addressDAO”的 bean 创建时出错:调用 init 方法失败;嵌套异常是 java.lang.IllegalArgumentException: 'sessionFactory' 或 'hibernateTemplate' 是必需的

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.

在这里,我认为 HibernateDaoSupport 对我的 DAO 的扩展会使它们继承 sessionFactory 和相关方法,所以我不明白会发生什么。

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.

我已经读到我可以将刷新模式设置为 AUTO 或将模板上的 setCheckWriteOperations 设置为 FALSE 来解决这类问题,它似乎有效,但我想这不会像我一样确保所有情况下的事务一致性喜欢它。

Any help would be appreciated as I am quite new to Spring and Hibernate and am a bit stuck here.

任何帮助将不胜感激,因为我对 Spring 和 Hibernate 还很陌生,并且有点卡在这里。

采纳答案by M. Deinum

When extending HibernateDaoSupportyou will not benefit from autowiring, you will have to override the setSessionFactorymethod and put an @Autowiredannotation on it. Else it won't work.

扩展时,HibernateDaoSupport您不会从自动装配中受益,您必须覆盖该setSessionFactory方法并在其上添加@Autowired注释。否则它不会工作。

I would also expect a <tx:annotation-driven />without that the @Transactionalis pretty much useless and doesn't do anything.

我也希望<tx:annotation-driven />没有它@Transactional几乎没用并且不做任何事情。

回答by Seymur Asadov

if your app is Spring MVC based...

如果您的应用程序是基于 Spring MVC 的...

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 !!!)

但在调度程序 servlet 中(不在 appContext 中!!!)

<tx:annotation-driven />

Dont forget namespaces for tx, and jar libraries spring-tx,spring-orm,hibernate-core

不要忘记 tx 和 jar 库的命名空间 spring-tx、spring-orm、hibernate-core

回答by SAN

In configuration file

在配置文件中

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