小编典典

未在Spring JPA Document Manager应用程序中保存文档

spring-mvc

我正在spring使用jpa和开发文档管理应用程序MySQL。该应用程序当前正在将文档及其元数据从用户Web表单createOrUpdateDocumentForm.jsp接受到控制器中DocumentController.java。但是,数据没有进入MySQL数据库。有人可以告诉我如何更改代码,以便将文档及其元数据存储在基础数据库中吗?

数据流(包括pdf文档)似乎通过以下对象:

createOrUpdateDocumentForm.jsp  //omitted for brevity, since it is sending data to controller (see below)
Document.java  
DocumentController.java  
ClinicService.java
JpaDocumentRepository.java
The MySQL database

我将总结每个对象的相关部分,如下所示:

在中jsp触发以下方法DocumentController.java

@RequestMapping(value = "/patients/{patientId}/documents/new", headers = "content-type=multipart/*", method = RequestMethod.POST)
public String processCreationForm(@ModelAttribute("document") Document document, BindingResult result, SessionStatus status, @RequestParam("file") final MultipartFile file) {
    document.setCreated();
    byte[] contents;
    Blob blob = null;
    try {
        contents = file.getBytes();
        blob = new SerialBlob(contents);
    } catch (IOException e) {e.printStackTrace();}
    catch (SerialException e) {e.printStackTrace();}
    catch (SQLException e) {e.printStackTrace();}
    document.setContent(blob);
    document.setContentType(file.getContentType());
    document.setFileName(file.getOriginalFilename());
    System.out.println("----------- document.getContentType() is: "+document.getContentType());
    System.out.println("----------- document.getCreated() is: "+document.getCreated());
    System.out.println("----------- document.getDescription() is: "+document.getDescription());
    System.out.println("----------- document.getFileName() is: "+document.getFileName());
    System.out.println("----------- document.getId() is: "+document.getId());
    System.out.println("----------- document.getName() is: "+document.getName());
    System.out.println("----------- document.getPatient() is: "+document.getPatient());
    System.out.println("----------- document.getType() is: "+document.getType());        
    try {System.out.println("[[[[BLOB LENGTH IS: "+document.getContent().length()+"]]]]");}
    catch (SQLException e) {e.printStackTrace();}
    new DocumentValidator().validate(document, result);
    if (result.hasErrors()) {
        System.out.println("result.getFieldErrors() is: "+result.getFieldErrors());
        return "documents/createOrUpdateDocumentForm";
    }
    else {
        this.clinicService.saveDocument(document);
        status.setComplete();
        return "redirect:/patients?patientID={patientId}";
    }
}

当我通过Web表单提交的文档中jspcontroller时,System.out.println()在命令controller代码输出以下,这表明该数据实际上是越来越发送到服务器:

----------- document.getContentType() is: application/pdf
----------- document.getCreated() is: 2013-12-16
----------- document.getDescription() is: paper
----------- document.getFileName() is: apaper.pdf
----------- document.getId() is: null
----------- document.getName() is: apaper
----------- document.getPatient() is: [Patient@564434f7 id = 1, new = false, lastName = 'Frank', firstName = 'George', middleinitial = 'B', sex = 'Male', dateofbirth = 2000-11-28T16:00:00.000-08:00, race = 'caucasian']
----------- document.getType() is: ScannedPatientForms
[[[[BLOB LENGTH IS: 712238]]]]  //This indicates the file content was converted to blob

Document.java模型是:

@Entity
@Table(name = "documents")
public class Document {
    @Id
    @GeneratedValue
    @Column(name="id")
    private Integer id;

    @ManyToOne
    @JoinColumn(name = "client_id")
    private Patient patient;

    @ManyToOne
    @JoinColumn(name = "type_id")
    private DocumentType type;

    @Column(name="name")
    private String name;

    @Column(name="description")
    private String description;

    @Column(name="filename")
    private String filename;

    @Column(name="content")
    @Lob
    private Blob content;

    @Column(name="content_type")
    private String contentType;

    @Column(name = "created")
    private Date created;

    public Integer getId(){return id;}
    public void setId(Integer i){id=i;}

    protected void setPatient(Patient patient) {this.patient = patient;}
    public Patient getPatient(){return this.patient;}

    public void setType(DocumentType type) {this.type = type;}
    public DocumentType getType() {return this.type;}

    public String getName(){return name;}
    public void setName(String nm){name=nm;}

    public String getDescription(){return description;}
    public void setDescription(String desc){description=desc;}

    public String getFileName(){return filename;}
    public void setFileName(String fn){filename=fn;}

    public Blob getContent(){return content;}
    public void setContent(Blob ct){content=ct;}

    public String getContentType(){return contentType;}
    public void setContentType(String ctype){contentType=ctype;}

    public void setCreated(){created=new java.sql.Date(System.currentTimeMillis());}
    public Date getCreated() {return this.created;}

    @Override
    public String toString() {return this.getName();}
    public boolean isNew() {return (this.id == null);}

}

ClinicService.java从中调用的代码DocumentController是:

private DocumentRepository documentRepository;
private PatientRepository patientRepository;

@Autowired
public ClinicServiceImpl(DocumentRepository documentRepository, PatientRepository patientRepository) {
    this.documentRepository = documentRepository;
    this.patientRepository = patientRepository;
}

@Override
@Transactional
public void saveDocument(Document doc) throws DataAccessException {documentRepository.save(doc);}

中的相关代码JpaDocumentRepository.java是:

@PersistenceContext
private EntityManager em;

@Override
public void save(Document document) {
    if (document.getId() == null) {this.em.persist(document);}
    else {this.em.merge(document);}
}

最后,创建数据库的SQL代码的相关部分包括:

CREATE TABLE IF NOT EXISTS documenttypes (
  id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(80),
  INDEX(name)
);

CREATE TABLE IF NOT EXISTS patients (
  id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  first_name VARCHAR(30),
  middle_initial VARCHAR(5), 
  last_name VARCHAR(30),
  sex VARCHAR(20), 
  date_of_birth DATE,
  race VARCHAR(30), 
  INDEX(last_name)
);

CREATE TABLE IF NOT EXISTS documents (
  id int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  client_id int(4) UNSIGNED NOT NULL,
  type_id INT(4) UNSIGNED, 
  name varchar(200) NOT NULL,
  description text NOT NULL,
  filename varchar(200) NOT NULL,
  content mediumblob NOT NULL, 
  content_type varchar(255) NOT NULL,
  created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (client_id) REFERENCES patients(id),
  FOREIGN KEY (type_id) REFERENCES documenttypes(id)
);

我要对此代码进行哪些更改,以便使用将代码保存document在数据库documents表中?MySQL``jpa


阅读 223

收藏
2020-06-01

共1个答案

小编典典

您的JPA映射似乎不错。显然,@ Lob要求数据类型为byte [] / Byte []
/或java.sql.Blob。基于此,再加上您的症状和调试打印输出,看来您的代码在执行正确的数据操作(JPA批注很好),但是spring +
MySQL的组合没有提交。这表明您的Spring事务性配置或MySQL数据类型存在一个小问题。

1.交易行为

JpaDocumentRepository.java中的相关代码是:

@PersistenceContext
private EntityManager em;

@Override
public void save(Document document) {
    if (document.getId() == null) {this.em.persist(document);}
    else {this.em.merge(document);}
}
  • 您没有使用EJB(因此没有“自动”容器管理的事务)。
  • 您正在Servlets / java类中使用JPA(因此,您需要在Servlet容器外部,在代码中或通过Spring config进行“手动”事务划分)。
  • 您通过以下方式注入实体管理器@PersistenceContext(即,由JTA支持的容器管理的实体管理器,而不是实体管理器资源本地事务em.getTransaction()
  • 您已将“父”方法标记为@Transactional(即,spring专有事务-注释后来在Java EE 7中进行了标准化)。

注释和代码应具有交易行为。您是否为JTA事务正确配置了Spring?(使用JtaTransactionManager,而不是提供JDBC驱动程序本地事务的DataSourceTransactionManager)Spring
XML应该包含与以下内容非常相似的内容:

<!-- JTA requires a container-managed datasource -->
<jee:jndi-lookup id="jeedataSource" jndi-name="jdbc/mydbname"/>

<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager"/>

<!-- a PlatformTransactionManager is still required -->
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" >
  <!-- (this dependency "jeedataSource" must be defined somewhere else) -->
  <property name="dataSource" ref="jeedataSource"/>  
</bean>

怀疑其他参数/​​设置。

这是Spring必须执行的手动编码版本(仅出于理解目的,请不要对此进行编码)。使用UserTransaction(JTA),而不是EntityTransaction类型的em.getTransaction()(JDBC本地):

// inject a reference to the servlet container JTA tx
@Resource UserTransaction jtaTx;

// servlet container-managed EM
@PersistenceContext private EntityManager em;

public void save(Document document) {
    try {
        jtaTx.begin();
        try {
            if (document.getId() == null) {this.em.persist(document);}
            else {this.em.merge(document);}
            jtaTx.commit();
        } catch (Exception e) {
             jtaTx.rollback();
             // do some error reporting / throw exception ...
        }
    } catch (Exception e) {
        // system error - handle exceptions from UserTransaction methods
        // ...
    }
}

2. MySQL数据类型

如此处所示(底部),与其他数据库相比,MySql
Blob有点特殊。各种Blob及其最大存储容量为:

TINYBLOB-255字节BLOB-65535字节MEDIUMBLOB-16,777,215字节(2 ^ 24-1)LONGBLOB-4G字节(2 ^
32 – 1)

如果(2)成为您的问题:

  • 将MySQL类型增加到MEDIUMBLOB或LONGBLOB
  • 调查为什么您没有看到错误消息(非常重要)。是否正确配置了日志记录?您检查日志了吗?
2020-06-01