工作笔记-Spring5的坑:Write operations are not allowed in read-only mode (FlushMode.MANUAL)

最近项目把Hibernate和Spring升级到了5,记录出现的常见问题。

在对数据源进行写操作(插入、更新、删除)的时候,遇到这样的报错

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.
	at org.springframework.orm.hibernate5.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1093)
	at org.springframework.orm.hibernate5.HibernateTemplate.lambda$save$11(HibernateTemplate.java:637)
	at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:383)
	at org.springframework.orm.hibernate5.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:349)
	at org.springframework.orm.hibernate5.HibernateTemplate.save(HibernateTemplate.java:636)

似乎和FlushMode有关系, 这是升级Spring5以后才遇到的,看来是Spring的实现有所改动。通过追踪代码的方式看看Spring5到底做了什么。

以save操作为例,以HibernateTemplate的save方法为起点

	@Override
	public Serializable save(final Object entity) throws DataAccessException {
		return nonNull(executeWithNativeSession(session -> {
			checkWriteOperationAllowed(session);
			return session.save(entity);
		}));
	}

直接返回executeWithNativeSession的返回结果,并创建了一个匿名类实现了回调方法。

进入executeWithNativeSession方法

	@Nullable
	public <T> T executeWithNativeSession(HibernateCallback<T> action) {
		return doExecute(action, true);
	}

继续跟踪doExecute

@Nullable
	protected <T> T doExecute(HibernateCallback<T> action, boolean enforceNativeSession) throws DataAccessException {
		Assert.notNull(action, "Callback object must not be null");

		Session session = null;
		boolean isNew = false;
		try {
			session = obtainSessionFactory().getCurrentSession();
		}
		catch (HibernateException ex) {
			logger.debug("Could not retrieve pre-bound Hibernate session", ex);
		}
		if (session == null) {
			session = obtainSessionFactory().openSession();
			session.setFlushMode(FlushMode.MANUAL);
			isNew = true;
		}

		try {
			enableFilters(session);
			Session sessionToExpose =
					(enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session));
			return action.doInHibernate(sessionToExpose);
		}
		catch (HibernateException ex) {
			throw SessionFactoryUtils.convertHibernateAccessException(ex);
		}
		catch (PersistenceException ex) {
			if (ex.getCause() instanceof HibernateException) {
				throw SessionFactoryUtils.convertHibernateAccessException((HibernateException) ex.getCause());
			}
			throw ex;
		}
		catch (RuntimeException ex) {
			// Callback code threw application exception...
			throw ex;
		}
		finally {
			if (isNew) {
				SessionFactoryUtils.closeSession(session);
			}
			else {
				disableFilters(session);
			}
		}
	}

先进行操作session = obtainSessionFactory().getCurrentSession();获取当前事务的session,如果当前不在事务中,这个操作会抛异常,被捕获后输出日志,代码继续往下走。此时session为null

在session为Null时,执行obtainSessionFactory().openSession()创建新的session,并且把flushMode设置为Manual。就是因为这里导致我们的报错。

注意这个FlushMode.MANUAL,这个模式是只读的,在这个模式的session下对数据源进行写操作,就会出现标题中的Write operations are not allowed in read-only mode (FlushMode.MANUAL)错误。

解决方法就是自己声明事务,替代spring自动生成的事务,并且把readOnly设置为false。

@Transactional(readOnly=false)

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值