Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用JPA将大型Blob从数据库流到应用程序?_Java_Hibernate_Spring Mvc_Oracle11g_Jpa 2.2 - Fatal编程技术网

Java 如何使用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

我有一个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", 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实现

  • 首先,我从实体管理器获取了一个hibernate会话
  • 然后,我创建了一个准备好的语句,用于使用从会话中提取的连接来选择blob
  • 然后,我从prepared语句的结果集生成了一个输入流
  • 以下是用于流式传输内容的DAO类:

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