【问题标题】:Error : Cannot execute statement in a READ ONLY transaction错误:无法在只读事务中执行语句
【发布时间】:2019-07-04 06:57:09
【问题描述】:

我正在使用 springboot 应用程序并连接到 Azure Mysql。 在应用程序内部的方法调用中,编写了一个围绕 Spring 的方面,它可以调用 Azure MySql db。 以下是从切面和方法执行的查询序列

@Autowired
EntityManager entityManager
  1. 来自方面:insert into <table name > values (); 使用以下代码来执行此查询
   EntityManager newEntityManager = entityManager.getEntityManagerFactory().createEntityManager();


       String nativeSql = "insert into table1 values('1','abc;)";

       newEntityManager.getTransaction().begin();

       try{
            newEntityManager.createNativeQuery(nativeSQL).executeUpdate();

            entityManager.getTransaction().commit();    
          } catch (RuntimeException e) {    
                   newEntityManager.getTransaction().rollback();
               }finally{

             newEntityManager.close();

          }
  1. 使用带有注释的 JPA 在数据库上完成读取调用
    @Transactional(readOnly=true)
  1. 下一个被执行的片段
EntityManager newEntityManager = entityManager.getEntityManagerFactory().createEntityManager();

    String nativeSql = "update table2 set status='dead' where name='abc'";

    newEntityManager.getTransaction().begin();

    try{

         newEntityManager.createNativeQuery(nativeSQL).executeUpdate(); //error occurs at this line

         entityManager.getTransaction().commit();

       } catch (RuntimeException e) {

                newEntityManager.getTransaction().rollback();

        }finally{

          newEntityManager.close();

       }

以下是完整的错误

2019-02-10 23:18:00.959 错误 [bootstrap,c577f32a3b673745,c577f32a3b673745,false] 10628 --- [nio-8106-exec-2] ohengine.jdbc.spi.SqlExceptionHelper:无法在只读事务。

但是当应用程序连接到本地 mysql(在我的例子中是 MariaDb)时,相同的代码可以正常工作。 即使与 Azure MSSQL 连接,此代码也能正常工作。 但是连接Azure MySQL时会出现错误。

【问题讨论】:

  • 检查您的连接详细信息。您的连接应该在主服务器上
  • 我遇到了完全相同的问题,但我没有找到要检查的内容。我试过show variables WHERE Variable_name like 'tx_%' 但这清楚地表明tx_read_only 是OFF。它适用于类似的 MySQL 服务器,到目前为止我迷路了。当我通过我的 IDE 使用相同的 JDBC URL、用户和密码连接到数据库时,我可以执行更改。即使应用程序之前成功调用了清理过程,但随后它拒绝在类似于上一个代码示例的块中执行一次 INSERT。不知道。

标签: mysql hibernate azure spring-boot spring-data-jpa


【解决方案1】:

不确定这是正确的解决方案,但我在 MariaDB 上遇到了同样的问题,而且非常混乱。我尝试使用show variables WHERE Variable_name like 'tx_%' 跟踪事务的状态,但它始终将tx_read_only 显示为OFF——即使我实际上运行的是只读事务。所以不知道如何判断事务是否为只读。

但我将我的问题归结为一个相当模糊的场景。在报告为只读的有问题的读写事务之前,我运行了一个不同的只读事务。两者都使用相同的物理事务,并且“只读”以某种方式泄露。但是当我在第一个 RO 事务中使用Connection.getMetaData() 然后使用metaData.getColumns(...) 时它失败了。我们需要检查单个表的列。

没有出现问题,当我将事务读取元数据切换为读写时,如果我们怀疑只读泄漏到另一个(逻辑)连接,这是有道理的。顺便说一句:我们在单个 Hikari 连接池之上使用 Hibernate 和普通 JDBC 的组合,所以这可能是一个因素。

后来我们改变了查找元数据的方式。我们准备了一份声明,SELECT 没有返回任何内容,然后询问resultSet.getMetaData()。这不仅对于单个表来说要快得多(尤其是在 Oracle 上,使用 Connection.getMetaData() 大约需要 5 分钟) - 但它确实没有导致问题,即使我在只读事务。

所以这很奇怪 - 问题发生在:

  • 使用了特定的数据库(MariaDB,但 MySQL 也可以,其他类型也可以)。
  • 当我在只读事务中使用Connection.getMetaData() 读取元数据时(必须同时满足这两个条件才能确保失败)。
  • 下一个读写事务使用相同的封装(物理)连接。

当使用只读事务从空的 SELECT 结果集中读取元数据时,我检查了在成功的场景中确实使用了相同的 JDBC 连接。读写事务没有问题。我认为问题可能在于处理连接的元数据,我将resultSet.close() 添加到MetaData.getColumns() 的结果集中,但这里没有帮助。最后我完全避开了Connection.getMetaData()。

所以虽然我仍然不知道问题出在哪里,但我可以绕过它并确保连接似乎适用于下一个读写事务,表明没有丢失的清理/只读泄漏。

另一个特点是:我们使用SET TRANSACTION READ ONLY 语句为 MySQL 和 MariaDB 启动只读事务。它应该在两个数据库上以相同的方式工作。当我改用START TRANSACTION READ ONLY 时,它运行良好。即使我使用“错误”的方式访问表元数据。

但是,抱歉,它并没有说明为什么 OP 在 MySQL 上出现问题。

【讨论】:

    猜你喜欢
    • 2019-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-20
    相关资源
    最近更新 更多