我最近遇到了很多烦人的异常,经过对 Google 和这个论坛的一些研究,我仍然没有找到可以解决我的问题的答案。
事情是这样的 - 有时,当我尝试使用 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、类型、长度。可能发生的情况是,如果我通过 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);
}
当使用 Spring OpenSessionInViewFilter 并尝试在 Spring 管理的事务之外执行持久化操作时,通常会看到该错误消息。过滤器将会话设置为 FlushMode.NEVER/MANUAL (取决于您使用的 Spring 和 Hibernate 版本 - 它们大致相同)。当Spring事务机制开始一个事务时,它会将flush模式更改为“COMMIT”。事务完成后,它会根据需要将其设置回 NEVER/MANUAL。如果您“绝对确定”这不会发生,那么下一个最可能的罪魁祸首是会话的非线程安全使用。 Hibernate Session 只能在一个线程中使用。如果它跨越线程,就会发生各种混乱。请注意,从 Hibernate 加载的实体可以保存对其加载所在的 Session 的引用,并且跨线程处理该实体也可能导致从另一个线程访问该 Session。
@Transactional
在你的函数之上
<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>
OpenSessionInViewFilter
中的刷新模式更改为手动,突然我开始遇到此异常。我发现问题发生在没有注释为
@Transactional
的方法中,所以我猜测 Spring 隐式地将此类方法之外的所有数据访问代码视为只读。对方法进行注释解决了问题。另一种方法是在 setCheckWriteOperations
HibernateTemplate
方法。中使用以下 bean 来实现 HibernateTemplate。
<bean id="template" class="org.springframework.orm.hibernate4.HibernateTemplate">
<property name="sessionFactory" ref="mysessionFactory"></property>
<property name="checkWriteOperations" value="false"></property>
</bean>
hibernateTemplate = new HibernateTemplate(sessionFactory);
hibernateTemplate.setCheckWriteOperations(false);
错误应该消失,因为模板不会检查您是否正在使用交易。
hibernateTemplate = new HibernateTemplate(sessionFactory);
hibernateTemplate.setCheckWriteOperations(false);
@Transactional
<tx:annotation-driven/>
文件中的
HibernateTransactionManager
配置上方添加applicationContext.xml
类上添加
@Transactional
注释
@Repository
@Transactional
public class XxxRepository {
@Autowired
private HibernateTemplate hibernateTemplate;
.
.
.
}
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
我删除了
mode="aspectj"
问题就消失了
this.getHibernateTemplate().setCheckWriteOperations(false);
如果您使用 hibernate4 并且无法更新实体/表,那么您需要创建会话并需要刷新会话,即
this.getHibernateTemplate().setCheckWriteOperations(false);
Session session = this.getHibernateTemplate().getSessionFactory().openSession();
session.saveOrUpdate(entityName,app);
session.flush();
session.close();
<tx:annotation-driven />
你错过了。