如何通过 EntityManager 从 InputStream 更新 BLOB?

How can I update a BLOB from an InputStream via an EntityManager?

我有一个 table:

mysql> desc documents;
+---------+--------------+------+-----+---------------------+----------------+
| Field   | Type         | Null | Key | Default             | Extra          |
+---------+--------------+------+-----+---------------------+----------------+
| id      | int(11)      | NO   | PRI | NULL                | auto_increment |
| item_id | int(11)      | YES  | MUL | NULL                |                |
| doctype | int(11)      | YES  | MUL | NULL                |                |
| name    | varchar(128) | YES  | MUL | NULL                |                |
| descr   | text         | YES  |     | NULL                |                |
| created | timestamp    | NO   |     | CURRENT_TIMESTAMP   |                |
| changed | timestamp    | NO   |     | 0000-00-00 00:00:00 |                |
| doc     | longblob     | YES  |     | NULL                |                |
+---------+--------------+------+-----+---------------------+----------------+

我已将其映射为 Entity,如下所示:

@Entity
@Table(name = "documents", schema = "office_db")
@XmlRootElement
@NamedQueries({
    ...,
    @NamedQuery(name = "Documents.updateDocById", query = "UPDATE Documents d SET d.doc = :document WHERE d.id = :id")
})
public class Documents implements Serializable {

    @Column(name = "name", length = 128)
    private String name;
    @Lob
    @Column(name = "descr", length = 65535)
    private String descr;
    @Lob
    @Column(name = "doc")
    private byte[] doc;
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;
    @Basic(optional = false)
    @Column(name = "created")
    @Temporal(TemporalType.TIMESTAMP)
    private Date created;
    @Basic(optional = false)
    @Column(name = "changed")
    @Temporal(TemporalType.TIMESTAMP)
    private Date changed;
    ...
}

我想用文件的内容更新 LONGBLOB DOC 列。为此,我获得了一个 InputStream 并想将它与我的 NamedQuery 相关联 - 我天真地尝试了以下操作,并期望它会失败(当然它会失败):

public String updateDocument(Integer id,InputStream is){
    String msg="";
    try{
        Query stmt=em.createNamedQuery("Documents.updateDocById")
                .setParameter("document",is);
        stmt.executeUpdate();
    }
    ...
}

如果我使用 PreparedStatement,我可以使用 setBinaryStream 方法,但那真的是正确的方法吗?

因此,回答我自己的问题:根据 What’s new in JPA 2.2 – Stream the result of a Query execution | Vlad Mihalcea's Blog,JPA 似乎不支持流式传输,至少在 JPA 2.2 之前不支持 - 看起来它仅用于流式传输查询结果,而不是更新 BLOB,这正是我要找的。

看起来又回到了使用 PreparedStatementsetBinaryStream() 的状态。我还没有尝试过,但是基于 How to get DataSource or Connection from JPA2 EntityManager in Java EE 6,我的策略是:

  • 开始交易。
  • EntityManager.
  • 中提取连接
  • 创建 PreparedStatement.
  • ...
  • 提交或回滚事务。

我觉得有点恶心,但你就是这样。