文档没有保存在spring jpa文档管理器应用程序中

我正在使用jpaMySQLspring开发文档管理应用程序。 该应用程序当前正在将文档及其元数据从用户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 

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

jspDocumentController.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}"; } } 

当我通过jsp的Web表单向controller提交文档时, controller代码中的System.out.println()命令输出以下内容,表明数据实际上已发送到服务器:

 ----------- 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);} } 

DocumentController调用的ClinicService.java代码是:

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

我对此代码进行了哪些更改,以便使用jpadocuments保存在MySQL数据库的documents表中?

你的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配置)。
  • 您通过@PersistenceContext注入实体管理器(即由JTA支持的容器管理的实体管理器,而不是实体管理器资源本地事务, em.getTransaction()
  • 您已将“父”方法标记为@Transactional (即弹簧专有的转录 – 后来在Java EE 7中标准化的注释)。

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

          

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

这是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)certificate是你的问题:

  • 将MySQL类型增加到MEDIUMBLOB或LONGBLOB
  • 调查您没有看到错误消息的原因(v重要)。 您的日志配置是否正确? 你检查了日志吗?

@CodeMed,我花了一段时间,但我能够重现这个问题。 这可能是一个配置问题: @PersistenceContext可能会被扫描两次,它可能会被您的根上下文和您的Web上下文扫描。 这会导致@PersistenceContext被共享,因此它不会保存您的数据(Spring不允许这样做)。 我发现没有显示的消息或日志很奇怪。 如果您在保存(文档文档)上尝试了下面的代码片段,您将看到实际错误:

 Session session = this.em.unwrap(Session.class); session.persist(document); 

要解决此问题,您可以执行以下操作(避免@PersistenceContext被扫描两次):

1-确保所有控制器都在com.mycompany.myapp.controller类的独立包中,并在您的Web上下文中使用component-scan作为

2-确保其他组件位于控制器包以外的其他组件中,例如: com.mycompany.myapp.daocom.mycompany.myapp.service ….然后在根上下文中使用组件 -扫描为

或者告诉我你的spring xml配置和你的web.xml,我会指出你正确的方向

我不是Hibernate-with-annotations专家(我从2004年开始使用它,但是使用XML配置)。 无论如何,我认为你正在混合注释错误。 你已经表明你不希望file字段与@Transient保持一致,但你也说它是@Lob ,这意味着你确实希望它保持@Lob 。 看起来@Lob正在获胜,而Hibernate正试图通过使用字段名称将字段解析为列。

脱掉@Lob,我想你会被设置好。

创建了将文件存储到mysql数据库中的示例应用程序,使用blob / clob的小信息存储文件是非常古老的做法。现在所有数据库都升级为以字节数组格式存储,此示例可能对您的要求有所帮助。

使用的技术:spring 3.2 + hibernate 4.x.

github链接: https : //github.com/uttesh/SpringHibernateUploader

这不是你问题的直接答案(对不起,但我不是hibernate的粉丝,因此无法真正帮助你)但你应该考虑使用NoSQL数据库,如MongoDB而不是MySQL来完成这样的工作。 我已经尝试了两种,NoSQL数据库更适合这种要求。

您会发现,在这种情况下,它的性能远远超过MySQL所能做的,SpringData MongoDB允许您非常轻松地保存和加载自动映射到MongoDB的Java对象。