2013-12-14 18 views
12

我正在使用jpaMySQL開發文檔管理應用程序spring。該應用程序目前正在接受來自用戶Web表單createOrUpdateDocumentForm.jsp的文檔及其元數據到控制器DocumentController.java中。但是,數據並未進入MySQL數據庫。有人能告訴我如何改變我的代碼,以便文檔及其元數據存儲在底層數據庫中嗎?在spring jpa文件管理器應用程序中未保存的文檔

數據(包括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}"; 
    } 
} 

當我通過網絡的形式在提交的文件到controller,所述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: [[email protected] 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) 
); 

我對這個代碼什麼樣的變化,使其節省了documentdocuments表中MySQL數據庫使用jpa

+0

我認爲沒有錯誤,而其他數據被提交到數據庫? –

+0

@ user2310289在此操作期間,我在eclipse控制檯中沒有看到錯誤。此應用程序中的另一個模塊確實提交了數據以將人員添加到數據庫,但添加文檔模塊不提交任何數據。在tomcat服務器中加載應用程序時唯一的錯誤是,但應用程序確實加載了,然後當前問題中的代碼運行時沒有發生任何錯誤。如果你認爲加載應用程序時發生的錯誤是相關的,你可以看看這篇文章:http://stackoverflow.com/questions/20622746/exception-loading-sessions-from-persistent-storage – CodeMed

+0

「The只有在tomcat服務器中加載應用程序時出現錯誤「是什麼?另外,你在jsp上的表單是否具有這個屬性:'enctype =「multipart/form-data」' –

回答

3

你的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(即春季專有注入實體管理器transcations - 後來在Java EE 7中標準化的註釋)。

註釋和代碼應該給交易行爲。你有沒有正確配置JTA事務的Spring? (使用的JtaTransactionManager,不是的DataSourceTransactionManager這給JDBC驅動程序本地事務)的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必須做的手動編碼版本(僅用於理解 - 不要編碼)。型EntityTransaction的使用的UserTransaction(JTA),不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的數據類型

如圖here (at bottom)中,MySQL斑點都有點特殊相比其他數據庫。各種Blob和它們的最大存儲容量是:

TINYBLOB - 255個字節 BLOB - 65535個字節 MEDIUMBLOB - 16777215個字節(2^24 - 1) LONGBLOB - 4G字節(2^32 - 1)

如果(2)原來是你的問題:

  • 增加MySQL的類型MEDIUMBLOB或LONGBLOB
  • 調查爲什麼你沒有看到錯誤消息(v重要)。您的日誌記錄是否正確配置你檢查日誌嗎?
+0

不會工作,因爲他已經有'@ Transactional'管理他的交易 –

+0

謝謝。沒有看到那個春天的片段。修改了答案。 –

+0

ThéLob不是問題,如果他刪除它,他仍然會遇到問題。如果他嘗試從entityManager獲取會話並使用它來保存對象,則會出現錯誤和異常。 –

3

我不是Hibernate-with-annotations專家(自2004年以來我一直在使用它,但使用XML配置)。無論如何,我認爲你不正確地混合了註釋。您已表示不希望file字段持續存在@Transient,但您也表示它是@Lob,這意味着您確實希望它持續存在。看起來像@Lob正在獲勝,並且Hibernate正試圖通過使用字段名稱將字段解析爲列。

取下@Lob,我想你會被設置。

+0

+1感謝您指出可能的解決方案。我嘗試了你的建議,並且暴露了business-config.xml中的另一個錯誤。我將鏈接添加到堆棧跟蹤和business-config.xml中,作爲上述原始發佈的編輯。你可以看看它發現錯誤嗎?我不知道你的答案是否能解決我的問題,直到我運行應用程序。 – CodeMed

+0

我沒有這樣做,錯誤是與交易。它是共享的。 –

1

這不是一個直接回答你的問題(對不起,但我不是一個hibernate的粉絲,所以不能真正幫助你),但你應該考慮使用NoSQL數據庫,如MongoDB而不是MySQL的工作喜歡這個。我已經嘗試過兩種方法,而NoSQL數據庫更適合這種需求。

你會發現,在這種情況下,它比MySQL能做得更好,並且SpringData MongoDB允許你輕鬆地保存和加載自動映射到MongoDB的Java對象。

5

@CodeMed,它花了我一段時間,但我能夠重現這個問題。這可能是一個配置問題:@PersistenceContext可能會被掃描兩次,它可能會被您的根上下文和您的web上下文掃描。這會導致共享@PersistenceContext,因此它不會保存您的數據(Spring不允許這樣做)。我發現奇怪的是沒有消息或日誌顯示。如果你嘗試下面你保存這個片段(文檔文件),您將看到實際的錯誤:

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

爲了解決這個問題,你可以做以下(避免被掃描的@PersistenceContext兩次):

1 - 確保所有控制器都在一個單獨的包像com.mycompany.myapp.controller,並在你的網絡環境中使用的組件,掃描爲<context:component-scan annotation-config="true" base-package="com.mycompany.myapp.controller" />

2 - 確保其他組件處於型動物包比控制器封裝等例如:com.mycompany.myapp.dao,com.mycompany.myapp.service .... ,然後在根上下文中使用組件掃描作爲 <context:component-scan annotation-config="true" base-package="com.mycompany.myapp.service, com.mycompany.myapp.dao" />

還是讓我看看你的Spring XML配置和你的web.xml中,我會爲您正確的方向

+0

要說清楚,文檔文件(Lob)本身不是問題。我刪除了除Id和名稱之外的所有屬性,但我仍然無法保存它。配置更改使其工作。我添加了屬性,全部保存沒有任何問題。 –

+0

+1謝謝。別人寫了一個完整的應用程序,並將其上傳到github。我會用幾種方式來劃分賞金,但S.O.沒有給我這個界面。看起來你有4個upvotes到目前爲止。再次感謝你。 – CodeMed

2

創建了將文件存儲到mysql數據庫的示例應用程序,使用blob/clob存儲文件的小信息是非常古老的做法。現在所有數據庫都升級爲以字節數組格式存儲,此示例可能有助於您的要求。

技術應用於:春季3.2 + hibernate的4.x的

github上鍊接:https://github.com/uttesh/SpringHibernateUploader

相關問題