복붙노트

[SPRING] spring jpa 문서 관리자 응용 프로그램에서 저장하지 않는 문서

SPRING

spring jpa 문서 관리자 응용 프로그램에서 저장하지 않는 문서

나는 jpa와 MySQL을 사용하여 봄에 문서 관리 애플리케이션을 개발 중이다. 애플리케이션은 현재 사용자 웹 양식 인 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}";
    }
}

jsp의 웹 양식을 통해 컨트롤러에 문서를 제출하면 컨트롤러 코드의 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)
);  

이 코드를 어떻게 변경하여 jpa를 사용하여 MySQL 데이터베이스의 문서 테이블에 문서를 저장합니까?

해결법

  1. ==============================

    1.JPA 매핑이 좋게 보입니다. 분명히, @Lob은 데이터 타입이 byte [] / Byte [] / 또는 java.sql.Blob이어야한다. 이를 기반으로 증상과 디버깅 출력이 정확한 데이터 조작 (JPA 주석이 좋은)을하는 코드처럼 보이지만 spring + MySQL의 조합은 커밋하지 않습니다. 이것은 Spring 트랜잭션 설정이나 MySQL 데이터 유형에 사소한 문제가 있음을 나타냅니다.

    JPA 매핑이 좋게 보입니다. 분명히, @Lob은 데이터 타입이 byte [] / Byte [] / 또는 java.sql.Blob이어야한다. 이를 기반으로 증상과 디버깅 출력이 정확한 데이터 조작 (JPA 주석이 좋은)을하는 코드처럼 보이지만 spring + MySQL의 조합은 커밋하지 않습니다. 이것은 Spring 트랜잭션 설정이나 MySQL 데이터 유형에 사소한 문제가 있음을 나타냅니다.

    1. 거래 행동

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

    주석과 코드는 트랜잭션 동작을 제공해야합니다. Spring이 JTA 트랜잭션을 위해 올바르게 설정 되었습니까? (JDBC 드라이버 로컬 트랜잭션을 제공하는 DataSourceTransactionManager가 아닌 JtaTransactionManager 사용) 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 유형의 em.getTransaction ()이 아닌 UserTransaction (JTA)을 사용합니다 (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)가 문제가된다면 :

  2. ==============================

    2.@CodeMed, 시간이 좀 걸렸지 만 문제를 재현 할 수있었습니다. 구성 문제 일 수 있습니다. @PersistenceContext가 두 번 스캔 될 수 있으며 루트 컨텍스트와 웹 컨텍스트로 스캔 될 수 있습니다. 이로 인해 @PersistenceContext가 공유되므로 데이터를 저장하지 않습니다 (스프링은 허용하지 않습니다). 나는 메시지 나 로그가 표시되지 않는 이상한 것을 발견했습니다. 아래에서이 스 니펫을 저장 (문서)하면 실제 오류가 표시됩니다.

    @CodeMed, 시간이 좀 걸렸지 만 문제를 재현 할 수있었습니다. 구성 문제 일 수 있습니다. @PersistenceContext가 두 번 스캔 될 수 있으며 루트 컨텍스트와 웹 컨텍스트로 스캔 될 수 있습니다. 이로 인해 @PersistenceContext가 공유되므로 데이터를 저장하지 않습니다 (스프링은 허용하지 않습니다). 나는 메시지 나 로그가 표시되지 않는 이상한 것을 발견했습니다. 아래에서이 스 니펫을 저장 (문서)하면 실제 오류가 표시됩니다.

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

    이 문제를 해결하려면 다음을 수행 할 수 있습니다 (@PersistenceContext가 두 번 스캔되지 않도록).

    1- 모든 컨트롤러가 com.mycompany.myapp.controller와 같은 별도 패키지에 있는지 확인하고 웹 컨텍스트에서 component-scan을

    2- 다른 구성 요소가 컨트롤러 패키지 이외의 다른 패키지에 있는지 확인하십시오 (예 : com.mycompany.myapp.dao, com.mycompany.myapp.service ....). 그런 다음 루트 컨텍스트에서 구성 요소 스캔을 다음과 같이 사용합니다.

    또는 스프링 XML 구성과 web.xml을 보여주십시오. 올바른 방향으로 안내해 드리겠습니다.

  3. ==============================

    3.저는 Hibernate-with-annotations 전문가는 아니지만 (저는 2004 년 이후로 XML을 사용하고 있습니다). 어쨌든 주석을 잘못 혼합하고 있다고 생각합니다. 당신은 파일 필드가 @Transient와 함께 지속되는 것을 원하지 않는다고 지적했지만, 당신은 그것이 @Lob이라고 말했고, 그것은 당신이 그것을 지속하기를 원한다는 것을 의미합니다. @Lob이 성공한 것 같고, Hibernate는 필드 이름을 사용하여 필드를 열로 해석하려고 시도하고있다.

    저는 Hibernate-with-annotations 전문가는 아니지만 (저는 2004 년 이후로 XML을 사용하고 있습니다). 어쨌든 주석을 잘못 혼합하고 있다고 생각합니다. 당신은 파일 필드가 @Transient와 함께 지속되는 것을 원하지 않는다고 지적했지만, 당신은 그것이 @Lob이라고 말했고, 그것은 당신이 그것을 지속하기를 원한다는 것을 의미합니다. @Lob이 성공한 것 같고, Hibernate는 필드 이름을 사용하여 필드를 열로 해석하려고 시도하고있다.

    @ Lob을 벗어나면 당신이 설정 될 것 같아요.

  4. ==============================

    4.blob / clob을 사용하여 파일을 저장하는 작은 정보는 바이트 배열 형식으로 저장하기 위해 업그레이드 된 모든 데이터베이스를 매우 오래된 방법입니다.이 샘플은 요구 사항에 도움이 될 수있는 샘플 응용 프로그램을 만들었습니다. .

    blob / clob을 사용하여 파일을 저장하는 작은 정보는 바이트 배열 형식으로 저장하기 위해 업그레이드 된 모든 데이터베이스를 매우 오래된 방법입니다.이 샘플은 요구 사항에 도움이 될 수있는 샘플 응용 프로그램을 만들었습니다. .

    기술 사용 : 봄 3.2 + 최대 절전 모드 4.x

    github Link : https://github.com/uttesh/SpringHibernateUploader

  5. ==============================

    5.이것은 귀하의 질문에 대한 직접적인 대답은 아니지만 (미안하지만 나는 절전 모드의 팬이 아니므로 실제로 거기에서 당신을 도울 수는 없습니다)하지만 MongoDB와 같은 NoSQL 데이터베이스를 MySQL 대신이 용도로 사용하는 것을 고려해야합니다. 나는 둘 다 시도하고 NoSQL 데이터베이스는 이런 종류의 요구 사항에 훨씬 더 적합하다.

    이것은 귀하의 질문에 대한 직접적인 대답은 아니지만 (미안하지만 나는 절전 모드의 팬이 아니므로 실제로 거기에서 당신을 도울 수는 없습니다)하지만 MongoDB와 같은 NoSQL 데이터베이스를 MySQL 대신이 용도로 사용하는 것을 고려해야합니다. 나는 둘 다 시도하고 NoSQL 데이터베이스는 이런 종류의 요구 사항에 훨씬 더 적합하다.

    이런 상황에서 MySQL이 할 수있는 것보다 훨씬 뛰어나다는 것을 알게 될 것입니다. 그리고 SpringData MongoDB는 MongoDB에 자동 매핑되는 Java 객체를 매우 쉽게 저장하고로드 할 수있게합니다.

  6. from https://stackoverflow.com/questions/20586865/document-not-saving-in-spring-jpa-document-manager-application by cc-by-sa and MIT license