Java / Hibernate - 在只读模式下不允许写操作

时间:2011-07-24 22:22:58

标签: java hibernate spring readonly

我最近经常遇到一个烦人的例外,经过对谷歌和这个论坛的一些研究后,我仍然没有找到可以解决我问题的答案。

这就是事情 - 有时,我在尝试用hibernate更新或创建一个新对象时遇到以下错误:

org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
at org.springframework.orm.hibernate3.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1186)
at org.springframework.orm.hibernate3.HibernateTemplate$12.doInHibernate(HibernateTemplate.java:696)
at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:419)
at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374)
at org.springframework.orm.hibernate3.HibernateTemplate.save(HibernateTemplate.java:694)

真正奇怪的是,有时用方法getHibernateTemplate().saveOrUpdate(object);更新对象时它会起作用,但有时使用相同的对象并通过调用相同的方法它不起作用,但它似乎依赖关于我如何获得对象。

示例:假设我有一个包含3个字段的表:id,type,length。可能发生的是,如果我通过id获取对象并更新长度,那么它将起作用。如果我按类型获取并更新长度,那么它将无法工作。所以到目前为止我一直在做的是避免这个问题,就是获取对象后面不会引起问题的方法,但是尝试找到一种有效的方法会变得越来越烦人。

此外,现在我在尝试创建一个对象(但不是所有对象,仅在一个特定的表上)时遇到此异常,并且找不到解决方法的方法。我尝试在事务中添加@Transactional(readOnly = false),但它没有改变任何内容,显示模式就是说我不是以只读方式。

有什么建议吗?

编辑7月26日: 这里有一些与hibernate相关的配置

<property name="hibernateProperties">
    <props>
        <prop key="jdbc.fetch_size">20</prop>
        <prop key="jdbc.batch_size">25</prop>
        <prop key="cglib.use_reflection_optimizer">true</prop>
        <prop key="hibernate.show_sql">true</prop>
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
        <prop key="connection.autoReconnect">true</prop>
        <prop key="connection.autoReconnectForPools">true</prop>
        <prop key="connection.is-connection-validation-required">true</prop>
    </props>
</property>

另外,如果可以提供帮助

<property name="transactionAttributes">
    <props>
        <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
        <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
        <prop key="execute*">PROPAGATION_REQUIRED</prop>
        <prop key="add*">PROPAGATION_REQUIRED</prop>
        <prop key="create*">PROPAGATION_REQUIRED</prop>
        <prop key="update*">PROPAGATION_REQUIRED</prop>
        <prop key="delete*">PROPAGATION_REQUIRED</prop>
    </props>
</property>

编辑8月31日: 我的类中扩展HibernateDaoSupport以保存对象的相关代码是:

public void createObject(Object persisObj) {
    getHibernateTemplate().save(persisObj);
}

10 个答案:

答案 0 :(得分:11)

当使用Spring OpenSessionInViewFilter并尝试在Spring管理的事务之外执行持久性操作时,通常会看到该错误消息。过滤器将会话设置为FlushMode.NEVER / MANUAL(取决于您使用的Spring和Hibernate的版本 - 它们大致相当)。当Spring事务机制开始一个事务时,它将刷新模式更改为“COMMIT”。事务完成后,它会根据需要将其设置为NEVER / MANUAL。如果你绝对确定没有发生这种情况,那么下一个最可能的罪魁祸首就是非线程安全地使用Session。 Hibernate Session必须只在一个线程中使用。如果它穿过线程之间,就会发生各种混乱。请注意,从Hibernate加载的实体可以保存对加载它的Session的引用,因此跨线程处理实体也可以导致从另一个线程访问Session。

答案 1 :(得分:7)

添加

@Transactional

在你的功能之上

答案 2 :(得分:6)

我也偶然发现了这一点。我需要将Spring的OpenSessionInViewFilter中的刷新模式更改为手动,突然我开始得到这个异常。我发现问题是在没有注释为@Transactional的方法中出现问题,所以我猜Spring会隐式地将所有数据访问代码视为只读方法。注释该方法解决了这个问题。

另一种方法是在HibernateTemplate对象上调用setCheckWriteOperations方法。

答案 3 :(得分:6)

我从视图过滤器更改了单个会话属性。问题解决:

  <filter>
    <filter-name>hibernateFilter</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>

答案 4 :(得分:3)

尝试使用此

hibernateTemplate = new HibernateTemplate(sessionFactory);
hibernateTemplate.setCheckWriteOperations(false);

错误应该消失,因为模板没有检查您是否正在使用交易。

答案 5 :(得分:2)

您必须忘记将@Transactional注释添加到您的DAO服务类/方法,这将指定您的数据库操作将由Spring托管事务处理。

答案 6 :(得分:1)

下面的代码对我有用。

hibernateTemplate = new HibernateTemplate(sessionFactory);
hibernateTemplate.setCheckWriteOperations(false);

答案 7 :(得分:1)

1)在数据库操作方法和上面添加@Transactional 2)确保你有注释驱动的转换管理器

添加  

在applicationContext.xml文件中的HibernateTransactionManager配置上面

答案 8 :(得分:0)

我遇到了同样的问题,经过一天的调查后,我发现了以下声明

<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>

我删除了

mode="aspectj"

并且问题消失了

答案 9 :(得分:0)

尝试使用此

this.getHibernateTemplate().setCheckWriteOperations(false);

如果您使用的是hibernate4并且无法更新实体/表,那么您需要创建会话并刷新会话,即

this.getHibernateTemplate().setCheckWriteOperations(false);
Session session = this.getHibernateTemplate().getSessionFactory().openSession();
session.saveOrUpdate(entityName,app);
session.flush();
session.close();