35

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

org.postgresql.util.PSQLException: Large Objects may not be used in auto-commit mode.

为了加载数据,我使用了 MultipartFile 瞬态属性,并在其设置器中设置了我想要保留的信息(字节 []、文件名、大小)。我坚持的实体看起来像这个(我省略了其余的 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());
        }
    }
}

我可以看到,当它被持久化时,我在行中有数据,但是当我调用这个方法时它失败了:

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();
}

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

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

但这并不能解决问题。

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

任何的想法?

谢谢。

更新

查看 Shekhar 给出的链接,建议将调用包含在事务中,因此我在事务中设置了服务调用,它可以工作(我添加了 @Transactional 注释)。

@Transactional
public List<myClass> find(String text) {
    return myClassDAO.findByDescription(text);
}

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

谢谢。

4

8 回答 8

41

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

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

于 2010-07-02T08:59:03.337 回答
9

而不是使用@Transactional,我为这个问题所做的只是将 PostgreSQL DB 中的这一列从oid一个类型更新到另一个类型,bytea@Type为该@Lob字段添加。

IE

ALTER TABLE person DROP COLUMN image;
ALTER TABLE person ADD COLUMN image bytea;

并改变了

@Lob
private byte[] image;

@Lob
@Type(type = "org.hibernate.type.ImageType")
private byte[] image;
于 2018-11-28T13:38:57.697 回答
6

如果可以,例如在 MyClass 和文件属性之间创建一个中间实体。就像是:

@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;
}

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

于 2013-07-05T01:36:58.977 回答
5

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

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

于 2010-07-04T17:05:59.750 回答
5

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

于 2020-08-13T21:34:10.520 回答
1

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

JHipster 项目中的问题中的此评论对问题有更好的解释,并且还描述了解决问题的更多选项。

我在这里总结一下:

Spring boot app, "out of the box", uses HikariCP connection pool and with autocommit=true by default. As it seems, the PostgreSQL jdbc driver requires that the handling of anything that uses a Lob be done with autocommit=false or inside a proper transaction.

The solutions (and explanations) proposed by Julien Dubois in the link above are very reasonable:

  • Mark the repository @transactional : I don't like it very much, because that gives more work to the service layer (which has to join the transaction, etc). So there's a little performance hit, for nothing much.

  • Mark the REST endpoint with @transactional(readOnly=true) : I don't like having transactions in the view layer. There's one added benefit from the point above: you can mark your transaction as readonly, so you have a performance gain.

  • Configure the connection pool to have autocommit = false. In fact, I thought we were already doing that, as the (older) connections pools I used were doing this. But that's not the case with HikariCP!!

That said, in my opinion, to turn off the autocommit in the connection pool seems as the better approach. It is more secure, it allows for one to work with read operations without the cost of a transaction and it is a global solution.

Just place this on the spring application.properties of your project:

spring.datasource.hikari.auto-commit=false
于 2021-10-06T03:09:08.260 回答
0

您应该使用 Connection 上的getAutoCommit()方法检查自动提交是否真的设置为 false 。

就我而言

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

没用,因为我的数据库连接类型需要自动提交为真。

就我而言,问题出在 JBoss 配置中。我使用的是 JTA 数据源,这导致了问题。使用 XA 数据源,它工作得很好。有关更多详细信息,请参阅此帖子

于 2019-06-14T10:11:02.563 回答
0

You can try his :

@Column(name = "LONG_TEXT", columnDefinition="TEXT")
private String longText;
于 2021-10-29T20:29:36.893 回答