大对象可能无法在自动提交模式下使用

Jav*_*avi 24 java postgresql hibernate jpa autocommit

我有一个Spring应用程序,它在PostgreSQL数据库上使用Hibernate.我正在尝试将文件存储在数据库的表中.它似乎将行存储在文件中(我只是在EntityManager上使用persist方法),但是当从数据库加载对象时,我得到以下异常:

org.postgresql.util.PSQLException: Large Objects may not be used in auto-commit mode.
Run Code Online (Sandbox Code Playgroud)

要加载数据,我正在使用MultipartFile瞬态属性,并在其setter中设置我想要保留的信息(byte [],fileName,size).我坚持的实体看起来像这个(我已经省略了其余的getter/setter):

@Entity
@Table(name="myobjects")
public class MyClass {

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="sequence")
    @SequenceGenerator(name="sequence", sequenceName="myobjects_pk_seq", allocationSize=1)
    @Column(name="id")
    private Integer id;

    @Lob
    private String description;

    @Temporal(TemporalType.TIMESTAMP)
    private Date creationDate;

    @Transient
    private MultipartFile multipartFile;

    @Lob
    @Basic(fetch=FetchType.LAZY, optional=true)
    byte[] file;

    String fileName;

    String fileContentType;

    Integer fileSize;

    public void setMultipartFile(MultipartFile multipartFile) {
        this.multipartFile = multipartFile;
        try {
            this.file = this.multipartFile.getBytes();
            this.fileName = this.multipartFile.getOriginalFilename();
            this.fileContentType = this.multipartFile.getContentType();
            this.fileSize = ((Long) this.multipartFile.getSize()).intValue();
        } catch (IOException e) {
            logger.error(e.getStackTrace());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我可以看到,当它持久存在时,我有行中的数据,但是当我调用此方法时它会失败:

public List<MyClass> findByDescription(String text) {
    Query query = getEntityManager().createQuery("from MyClass WHERE UPPER(description) like :query ORDER BY creationDate DESC");
    query.setParameter("query", "%" + text.toUpperCase() + "%");
    return query.getResultList();
}
Run Code Online (Sandbox Code Playgroud)

仅当结果包含具有文件的对象时,此方法才会失败.我试着在我的persistence.xml中设置

<property name="hibernate.connection.autocommit" value="false" />
Run Code Online (Sandbox Code Playgroud)

但它没有解决问题.

一般情况下,应用程序运行良好,它只在事务完成时提交数据,如果出现故障则执行回滚,所以我不明白为什么会发生这种情况.

任何的想法?

谢谢.

UPDATE

看看Shekhar给出的链接,建议将调用包含在一个转换中,所以我在一个事务中设置了服务调用它是否有效(我添加了@Transactional注释).

@Transactional
public List<myClass> find(String text) {
    return myClassDAO.findByDescription(text);
}
Run Code Online (Sandbox Code Playgroud)

问题是我不想保留任何数据,所以我不明白为什么它应该包含在一个事务中.当我只从数据库中加载一些数据时,提交是否有意义?

谢谢.

Fra*_*ens 34

大对象可以存储在多个记录中,这就是您必须使用事务的原因.所有记录都是正确的或根本没有.

https://www.postgresql.org/docs/current/static/largeobjects.html

  • 我不太确定这是如何回答原始问题的?原始海报不想在没有交易的情况下*写*LOB,他想在没有交易的情况下阅读它们. (7认同)
  • 这是一个怎样的答案? (2认同)

小智 8

@Transactional在您的存储库界面上使用。


Phi*_*ohn 7

@Transactional我没有使用,我为这个问题所做的只是将 PostgreSQL 数据库中的这一列从oid类型更新到bytea类型并@Type为该@Lob字段添加。

IE

ALTER TABLE person DROP COLUMN image;
ALTER TABLE person ADD COLUMN image bytea;
Run Code Online (Sandbox Code Playgroud)

并且改变了

@Lob
private byte[] image;
Run Code Online (Sandbox Code Playgroud)

@Lob
@Type(type = "org.hibernate.type.ImageType")
private byte[] image;
Run Code Online (Sandbox Code Playgroud)

  • 你是一个救星! (2认同)

Iog*_*gui 7

这是一个老问题,但我将与那些像我一样通过谷歌搜索到达这里的人分享我所发现的内容。

JHipster 项目中的一个问题中的此评论更好地解释了该问题,并描述了更多解决该问题的选项。

我在这里总结一下:

Spring boot 应用程序“开箱即用”,使用 HikariCP 连接池,默认情况下 autocommit=true。看起来,PostgreSQL jdbc 驱动程序要求对任何使用 Lob 的处理都通过 autocommit=false 或在正确的事务内完成。

Julien Dubois 在上面的链接中提出的解决方案(和解释)非常合理:

  • 将存储库标记为@transactional:我不太喜欢它,因为这给服务层带来了更多工作(必须加入事务等)。因此,性能会受到一点影响,但没什么大不了的。

  • 使用 @transactional(readOnly=true) 标记 REST 端点:我不喜欢在视图层中进行事务。上述这一点还有一个额外的好处:您可以将事务标记为只读,从而获得性能提升。

  • 将连接池配置为 autocommit = false。事实上,我认为我们已经这样做了,因为我使用的(较旧的)连接池正在这样做。但HikariCP却不是这样!!

也就是说,在我看来,关闭连接池中的自动提交似乎是更好的方法。它更安全,允许人们在没有事务成本的情况下进行读取操作,并且它是一种全局解决方案。

只需将其放在项目的 spring application.properties 中:

spring.datasource.hikari.auto-commit=false
Run Code Online (Sandbox Code Playgroud)


a_h*_*ame 5

除非您需要存储大于 1GB 的文件,否则我建议您使用 bytea 作为数据类型而不是大对象。

bytea 基本上是 BLOB 在其他数据库(例如 Oracle)中的内容,并且它的处理与 JDBC 更加兼容。


小智 5

如果可以,可以在MyClass和file属性之间创建一个中间实体.就像是:

@Entity
@Table(name="myobjects")
public class MyClass {
    @OneToOne(cascade = ALL, fetch = LAZY)
    private File file;
}

@Entity
@Table(name="file")
public class File {
     @Lob
     byte[] file;
}
Run Code Online (Sandbox Code Playgroud)

您不能使用@Lob并获取类型Lazy.它不起作用.你必须有一个中级课程.