错误:无法在只读事务中执行语句

时间:2019-02-10 18:08:45

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

我正在使用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(在我的情况下为MariaBb)连接时,相同的代码也可以正常工作。 即使与Azure MSSQL连接,此代码也可以正常工作。 但是与Azure MySQL连接时会发生错误。

1 个答案:

答案 0 :(得分:0)

不确定这是正确的解决方案,但是我对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()读取元数据时(必须满足两个条件以确保失败)。
  • 下一个读写事务使用相同的包装(物理)连接。

我检查了在成功情况下使用只读事务从空的SE​​LECT结果集中读取元数据时,是否确实使用了完全相同的JDBC连接。读写事务没有问题。我认为问题可能出在处理连接的元数据上,我在resultSet.close()的结果集中添加了MetaData.getColumns(),但这里没有帮助。最后,我完全避免了Connection.getMetaData()

因此,尽管我仍然不知道确切的问题出在哪里,但我可以解决它,并确保该连接似乎适用于下一个读写事务,表明没有丢失的清理/只读泄漏。 >

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

但是,很抱歉,它并不表示OP在MySQL上为什么有问题。