PSQLException:大对象不能在自动提交模式下使用

Posted

技术标签:

【中文标题】PSQLException:大对象不能在自动提交模式下使用【英文标题】:PSQLException: Large Objects may not be used in auto-commit mode 【发布时间】:2016-12-03 12:20:37 【问题描述】:

我正在使用 WildFly 10、Java EE、JPA 和 Hibernate。最近我将我的应用程序从 mysql 迁移到 PostgreSQL。在使用 MySQL 时,我会使用以下方法将图像存储在我的实体中:

@Lob
@Basic(fetch = FetchType.LAZY)
private byte[] image;

这很好用,MySQL 使用LONGBLOB 来存储数据。

切换到PostgreSQL后,列类型为OID,在持久化图片时收到这个错误:

Caused by: org.postgresql.util.PSQLException: Large Objects may not be used in auto-commit mode.
at org.postgresql.largeobject.LargeObjectManager.createLO(LargeObjectManager.java:308)
at org.postgresql.largeobject.LargeObjectManager.createLO(LargeObjectManager.java:296)
at org.postgresql.jdbc.PgPreparedStatement.createBlob(PgPreparedStatement.java:1202)
at org.postgresql.jdbc.PgPreparedStatement.setBlob(PgPreparedStatement.java:1243)
at org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.setBlob(WrappedPreparedStatement.java:1157)
at org.hibernate.type.descriptor.sql.BlobTypeDescriptor$4$1.doBind(BlobTypeDescriptor.java:112)
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:73)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:257)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:252)
at org.hibernate.type.AbstractSingleColumnStandardBasicType.nullSafeSet(AbstractSingleColumnStandardBasicType.java:39)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2598)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2883)
... 131 more

我是这样插入的:

@PersistenceContext
EntityManager entityManager;

...

//Simple insert method...
this.entityManager.persist(entity);

这是我的persistence.xml

<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="MyApp" transaction-type="JTA">
        <jta-data-source>java:jboss/datasources/PostgreSQLDS</jta-data-source>
        <shared-cache-mode>DISABLE_SELECTIVE</shared-cache-mode>
        <properties>
            <property name="hibernate.enable_lazy_load_no_trans" value="true"/>
            <property name="hibernate.cache.use_second_level_cache"
                      value="true"/>
            <property name="hibernate.cache.use_query_cache" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

我已经查看了有关此异常的多个其他问题,但是我无法解决该问题。

One suggestion 是创建一个仅用于存储数据的实体,并与它使用@OneToOne 关系,以便可以急切地获取存储数据的实体,但这并没有以任何方式改变结果。

另一个建议是禁用 Hibernate 的自动提交

<property name="hibernate.connection.autocommit" value="false"/>

...它什么也没做。

我完全不知道为什么这不起作用。看起来这似乎不是一个非常常见的例外,我觉得我正在以完全错误的方式处理存储图像数据的任务。我可以做些什么来解决这个问题,或者我应该如何存储数据?

【问题讨论】:

“我觉得我正在以完全错误的方式完成存储图像数据的任务。”正确的。图片根本不应该在数据库中 @e4c5 就我而言,我没有看到其他选择。此应用程序将在多个不同的 WildFly 安装中水平扩展。如果我将图像存储在本地磁盘上,则所有用户都无法访问它。我知道如何解决这个问题的唯一方法是使用数据库。每张图片不超过 1MB。 您不必存储在本地磁盘上。您可以存储在 S3、Google 文件或任意数量的 CDN 上。您将获得巨大的性能提升 “创建一个仅用于存储数据的实体并使用@OneToOne 关系”这是一种通常建议的解决方法,以在 Hibernate 中获得一种“属性”级别的延迟加载。如果您使用的是 JPA,您可以尝试支持延迟适当延迟加载的不同 JPA 实现 这能回答你的问题吗? Large Objects may not be used in auto-commit mode 【参考方案1】:

我不能告诉你这是如何在 Hibernate 中完成的,但是打开和读取/写入一个大对象必须发生在同一个数据库事务中。

禁用自动提交模式应该可以解决问题,也许你做错了什么。

但是我可以建议您根本不要使用大对象吗? 通常使用bytea PostgreSQL 数据类型要容易得多,它可以包含高达 1GB 大小的数据。除非您以块的形式存储和检索数据,否则大对象不会提供任何优势,而且我怀疑您是否可以通过 ORM 充分利用大对象的功能。

【讨论】:

虽然这并不能解决异常,但目前对我来说是最简单的替代解决方案。使用bytea 并删除@Lob 注释效果很好。【参考方案2】:

我遇到了同样的问题,并通过更改我的 JBOSS 配置文件(standalone.xml 或 domain.xml)解决了它。

首先分析问题我看到通过日志记录自动提交仍然是正确的

entityManager.unwrap(SessionImpl.class).connection().getAutoCommit()

我尝试将其设置为 false

entityManager.unwrap(SessionImpl.class).connection().setAutoCommit(false)

但这会导致异常“您无法在托管事务期间设置自动提交”。

研究我发现this article 的异常消息,它解释了为什么自动提交总是正确的。

我们使用的是这样配置的数据源:

<datasource jta="true"  . . . >

问题在于,在 JTA 数据源中,自动提交设置始终为 true,并且无法更改。 我将其更改为 xa-datasource,问题不再出现。

<xa-datasource . . . .>

还有 em.unwrap(SessionImpl.class).connection().getAutoCommit() 最后返回 false。

为了使其工作,我还必须在我的 Postgres 数据库的 postgres.conf 中将参数 max_prepared_transaction 设置为 500,以允许多个数据库连接/连接池:

max_prepared_transactions = 500     # zero disables the feature

【讨论】:

以上是关于PSQLException:大对象不能在自动提交模式下使用的主要内容,如果未能解决你的问题,请参考以下文章

jdbc插入大对象及读取大对象

org.postgresql.util.PSQLException:错误:关系不存在 PreparedStatement.executeQuery ()

PSQLException:在此 ResultSet 中找不到列名 clazz_

org.postgresql.util.PSQLException:错误无法打开文件“base/16384/19048”:没有这样的文件或目录

Heroku 上的 PSQLException

org.postgresql.util.PSQLException:连接尝试失败