大对象不能在自动提交模式下使用
我有一个 Spring 应用程序,它在 PostgreSQL 数据库上使用 Hibernate。我正在尝试将文件存储在数据库的表中。它似乎将行与文件一起存储(我只是在 EntityManager 上使用 persist 方法),但是当从数据库加载对象时,我得到以下异常:
org.postgresql.util.PSQLException: Large Objects may not be used in auto-commit mode.
要加载数据,我使用 MultipartFile 瞬态属性及其设置器我正在设置我想要保留的信息(字节[]、文件名、大小)。 我正在持久化的实体看起来像这样(我省略了其余的 getters/setters):
@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);
}
问题是我不想保留任何数据,所以我不明白为什么它应该包含在事务中。当我只从数据库加载一些数据时进行提交是否有意义?
谢谢。
I have a Spring application which uses Hibernate on a PostgreSQL database. I'm trying to store files in a table of the database. It seems it stores the row with the file (I just use persist method on EntityManager), but when the object is loaded from the database I get the following exception:
org.postgresql.util.PSQLException: Large Objects may not be used in auto-commit mode.
To load the data I'm using a MultipartFile transient atribute and in its setter I'm setting the information I want to persist (byte[], fileName, size).
The entity I'm persisting looks like this one (I've ommitted the rest of getters/setters):
@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());
}
}
}
I can see that when it is persisted I have the data in the row but when I call this method it fails:
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();
}
This method only fails when the result has objects with files. I've tried to set in my persistence.xml
<property name="hibernate.connection.autocommit" value="false" />
but it doesn't solve the problem.
In general the application works well it only commit the data when the transaction is finished and it performs a rollback if something fails, so I don't understand why is this happening.
Any idea?
Thanks.
UPDATE
Looking at the link given by Shekhar it is suggested to include the call in a transation, so I've set the service call inside a transaction an it works (I've added @Transactional annotation).
@Transactional
public List<myClass> find(String text) {
return myClassDAO.findByDescription(text);
}
the problem is that I don't want to persist any data so I don't understand why it should be include inside a transaction. Does it make any sense to make a commit when I've only loaded some data form the database?
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
一个大对象可以存储在多个记录中,这就是您必须使用事务的原因。所有记录都是正确的或根本没有。
https://www.postgresql.org/docs/current/static/largeobjects.html
A large object can be stored in several records, that's why you have to use a transaction. All records are correct or nothing at all.
https://www.postgresql.org/docs/current/static/largeobjects.html
我没有使用@Transactional,而是将 PostgreSQL DB 中的这一列从 oid 类型更新为 bytea 类型,并添加了 <
@Lob
字段的 code>@Type。即
并更改
为
Instead of using
@Transactional
, all I did for this issue was to update this column in PostgreSQL DB fromoid
type tobytea
type and added@Type
for the@Lob
field.i.e.
And changed
To
在您的存储库接口上使用
@Transactional
。Use
@Transactional
on your Repository Interface.这是一个老问题,但我将与那些像我一样通过谷歌搜索到达这里的人分享我所发现的内容。
JHipster 项目中的一个问题中的此评论有更好的解释问题并描述更多解决方案。
我将在这里总结一下:
Spring boot 应用程序“开箱即用”,使用 HikariCP 连接池,默认情况下 autocommit=true。看起来,PostgreSQL jdbc 驱动程序要求对任何使用 Lob 的处理都通过 autocommit=false 或在正确的事务内完成。
Julien Dubois 在上面的链接中提出的解决方案(和解释)非常合理:
也就是说,在我看来,关闭连接池中的自动提交似乎是更好的方法。它更安全,它允许人们在没有事务成本的情况下进行读取操作,并且它是一种全局解决方案。
只需将其放在项目的 spring application.properties 中:
That's an old question but I will share what I've found for those who get here by a google search as I did.
This comment in an issue in JHipster project has a better explanation of the problem and also describe more options to solve it.
I will sumarize it here:
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:
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:
除非您需要存储大于 1GB 的文件,否则我建议您使用 bytea 作为数据类型而不是大型对象。
bytea 基本上与其他数据库(例如 Oracle)中的 BLOB 相同,并且它的处理与 JDBC 更加兼容。
Unless you need to store files large than 1GB I suggest you use bytea as the datatype instead of large object.
bytea is basically what BLOB is in other databases (e.g. Oracle) and it's handling is a lot more compatible with JDBC.
如果可以的话,例如在 MyClass 和文件属性之间创建一个中间实体。
例如:
您不能使用 @Lob 和获取类型 Lazy。这不起作用。你必须有一个中级课程。
If you can, create a intermediate Entity between MyClass and file property for instance.
Something like:
You can't use @Lob and fetch type Lazy. It doesnt work. You must have a a intermediate class.
编译器无法将“type”理解为参数
The compiler does not understand "type" as a parameter
您应该使用连接上的方法 getAutoCommit() 检查自动提交是否确实设置为 false。
在我的例子中
不起作用,因为我的数据库连接的类型要求自动提交为真。
就我而言,问题出在 JBoss 配置中。我正在使用 JTA 数据源,这导致了问题。使用 XA 数据源,它工作得很好。有关更多详细信息,请参阅这篇文章。
You should check if auto-commit is really set false with method getAutoCommit() on your Connection.
In my case
did not work, because the type of my db connection required auto-commit to be true.
In my case the problem was in the JBoss Configuration. I was using an JTA-datasource and that caused the problem. With an XA-datasource it worked fine. See this post for more details.
你可以试试他的:
You can try his :
我从实体中删除了@Lob,它正在工作。
I removed @Lob from entity and it's working.
我正在为我解决的实体字段上使用 postgres TEXT 类型和
@Type(PostgreSQLCITextType.class)
。实体:
I'm using postgres TEXT type, and
@Type(PostgreSQLCITextType.class)
on the entity field solved for me.Entity: