Java 如何使用JPA将大型Blob从数据库流到应用程序?
我有一个JPA实体类,它包含一个blob字段,如下所示:Java 如何使用JPA将大型Blob从数据库流到应用程序?,java,hibernate,spring-mvc,oracle11g,jpa-2.2,Java,Hibernate,Spring Mvc,Oracle11g,Jpa 2.2,我有一个JPA实体类,它包含一个blob字段,如下所示: @Entity public class Report { private Long id; private byte[] content; @Id @Column(name = "report_id") @SequenceGenerator(name = "REPORT_ID_GENERATOR", sequenceName = "report_sequence_id", allocationS
@Entity
public class Report {
private Long id;
private byte[] content;
@Id
@Column(name = "report_id")
@SequenceGenerator(name = "REPORT_ID_GENERATOR", sequenceName = "report_sequence_id", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "REPORT_ID_GENERATOR")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Lob
@Column(name = "content")
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
}
我在数据库中的记录上插入了一些大数据(超过3 Gig)(使用DBMS过程)。应用程序用户应该能够下载这些记录的内容,因此我实现了一种方法,将获取的结果流式传输到客户端浏览器
问题是,由于JPQL select查询倾向于首先从DB中获取整个对象,然后将其提供给应用程序,所以每当我尝试使用JPA访问此记录时,我都无法分配足够的内存异常 我已经看到了一些解决这个问题的方法,使用JDBC连接尝试从数据库流式传输数据,但我无法找到任何JPA特定的解决方案
有人知道如何解决这个问题吗?我应该解决这个问题吗?因为使用关系数据库将大型(千兆字节)数据文件作为BLOB存储在数据库中不是一个好做法。通常的做法是,数据本身以文件的形式存储在服务器上(可能是FTP),元数据(文件路径以及服务器等)存储在数据库列中。在这种情况下,将这些数据流式传输到客户端变得更加容易 也许您可以使用压缩算法(如有损压缩和无损压缩、Huffman、facebook的Zstandard)压缩文件,然后将其存储在数据库中,然后通过解压缩进行检索。我以以下方式解决了问题,请注意,此解决方案可能只适用于JPA的hibernate实现
@Repository
public class ReportDAO{
private static final Logger logger = LoggerFactory.getLogger(ReportDAO.class);
@PersistenceContext
private EntityManager entityManager;
//---streamToWrite is the stream that we used to deliver the content to client
public void streamReportContent(final Long id, final OutputStream streamToWrite) {
try{
entityManager=entityManager.getEntityManagerFactory().createEntityManager();
Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {
@Override
public void execute(Connection connection) throws SQLException
{
PreparedStatement stmt=connection.prepareStatement("SELECT content FROM report where id=?");
stmt.setLong(1,id);
ResultSet rs = stmt.executeQuery();
rs.next();
if(rs != null)
{
Blob blob = rs.getBlob(1);
InputStream input = blob.getBinaryStream();
byte[] buffer = new byte[1024];
try {
while (input.read(buffer) > 0) {
String str = new String(buffer, StandardCharsets.UTF_8);
streamToWrite.write(buffer);
}
input.close();
} catch (IOException e) {
logger.error("Failure in streaming report", e);
}
rs.close();
}
}
});
}
catch (Exception e){
logger.error("A problem happened during the streaming problem", e);
}
}
你应该看看社区项目。该项目为您提供了一种类似Spring数据的内容处理方法。对于非结构化数据(文档、图像、视频等),就像Spring数据对于结构化数据一样。您可以添加如下内容:- xml(也可以使用Spring引导启动器) 创建“存储”: ExampleStore.java
@StoreRestResource(path=“reportContent”)
公共接口ReportContentStore扩展了ContentStore{
}
这就是创建REST端点所需的全部内容@/reportContent
。当应用程序启动时,Spring内容将查看您的依赖项(查看Spring内容JPA/REST),查看您的ReportContentStore
接口,并为JPA注入该接口的实现。它还将注入一个@Controller
,将http请求转发给该实现。这就省去了你自己去实现这些
所以
curl-X POST/reportsContent/{reportId}
-F'data=@path/to/local/file'
将path/to/local/file
的内容存储在数据库中,并将其与id为reportId
的报表实体相关联
curl/reportContent/{reportId}
将再次获取它,等等…支持完整CRUD
这里有一些入门指南和视频。参考指南是
HTH这是一个迟来的答案,但对于那些仍在寻找解决方案的人,我在Java博客上找到了Thorben Janssen关于思考的一篇好文章。缺点是,它是特定于Hibernate的,但您似乎还是在使用它。 基本上,解决方案是在实体中使用java.sql.Blob数据类型属性
@Entity
public class Book {
@Id
@GeneratedValue
private Long id;
private String title;
@Lob
private Clob content;
@Lob
private Blob cover;
...
}
然后使用Hibernate的BlobProxy,它提供了一个输出流。但是看看这篇文章我和你一样有一个类似的问题我需要在字段中存储JSON,所以当我使用BLOB时,我给自己带来了很多不必要的麻烦。 如果您将blob用于内容类型的数据,我恭敬地建议您将CLOB用于数据,如果它是字符格式的 总结一下我的答案,如果您使用的是ORACLE数据库(这是一个经常导致语言表达问题的数据库),请使用以下格式作为指南或最佳做法(基于ORACLE文档本身)来解决您的问题:
@Lob @Basic(fetch=LAZY)
@Column(name="REPORT")
protected String report;
祝你好运 您是否在项目中使用了Spring数据?没有。但我们已经实现了一些通用类,它们具有相同的用途。服务器上的文件不是事务性的。如果您不想使用BLOB,并且您的数据库是Oracle,则可以使用BFILE。
@Entity
public class Report {
// replace @Lob field with:
@ContentId
private String contentId;
@ContentLength
private long contentLength = 0L;
// if you have rest endpoints
@MimeType
private String mimeType = "text/plain";
@StoreRestResource(path="reportContent")
public interface ReportContentStore extends ContentStore<Report, String> {
}
@Entity
public class Book {
@Id
@GeneratedValue
private Long id;
private String title;
@Lob
private Clob content;
@Lob
private Blob cover;
...
}
@Lob @Basic(fetch=LAZY)
@Column(name="REPORT")
protected String report;