document not saving in spring jpa document manager application

前端 未结 4 1976
情歌与酒
情歌与酒 2020-12-10 13:38

I am developing a document management application in spring using jpa and MySQL. The application is currently accepting a document an

相关标签:
4条回答
  • 2020-12-10 14:18

    This is not a direct answer to your question (sorry but I'm not a fan of hibernate so can't really help you there) but you should consider using a NoSQL database such as MongoDB rather than MySQL for a job like this. I've tried both and the NoSQL databases are a much better fit to this sort of requirement.

    You will find that in situations like this it performs much better than MySQL can do and SpringData MongoDB allows you to very easily save and load Java objects that automatically get mapped to MongoDB ones.

    0 讨论(0)
  • 2020-12-10 14:33

    I'm not a Hibernate-with-annotations expert (I've been using it since 2004, but with XML config). Anyway, I'm thinking that you're mixing annotations incorrectly. You've indicated that you don't want the file field persisted with @Transient, but you've also said it's a @Lob, which implies you do want it persisted. Looks like @Lob is winning, and Hibernate is trying to resolve the field to a column by using the field name.

    Take off the @Lob and I think you'll be set.

    0 讨论(0)
  • 2020-12-10 14:42

    Your JPA mappings seem good. Obviously, @Lob requires data type to be byte[] / Byte[] / or java.sql.Blob. Based on that, plus your symptoms and debugging printout it seems your code doing the correct data manipulation (JPA annotations good), but the combination of spring + MySQL isn't commiting. This suggests a minor problem with your spring transactional config OR with your MySQL data type.

    1. Transactional Behaviour

    The relevant code in JpaDocumentRepository.java is:

    @PersistenceContext
    private EntityManager em;
    
    @Override
    public void save(Document document) {
        if (document.getId() == null) {this.em.persist(document);}
        else {this.em.merge(document);}
    }  
    
    • You're not using EJBs (hence no 'automatic' container-managed transactions).
    • You're using JPA within Servlets/java classes (hence you require 'manual' transaction demarcation - outside servlet container; in your code or via Spring config).
    • You are injecting the entity manager via @PersistenceContext (i.e. container-managed entity manager backed by JTA, not a Entity Manager resource-local transaction, em.getTransaction())
    • You have marked your 'parent' method as @Transactional (i.e. spring proprietary transcations - annotation later standardised in Java EE 7).

    The annotations and code should give transactional behaviour. Do you have a Spring correctly configured for JTA transactions? (Using JtaTransactionManager, not DataSourceTransactionManager which gives JDBC driver local transactions) Spring XML should contain something very similar to:

    <!-- 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>
    

    Be suspicious of additional parameters / settings.

    This is the manually coded version of what Spring must do (for understanding only - don't code this). Uses UserTransaction (JTA), not em.getTransaction() of type EntityTransaction (JDBC local):

    // 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 Data Type

    As shown here (at bottom), MySql Blobs are a bit special compared to other databases. The various Blobs and their maximum storage capacities are:

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

    If (2) turns out to be your problem:

    • increase the MySQL type to MEDIUMBLOB or LONGBLOB
    • investigate why you didn't see an error message (v important). Was your logging properly configured? Did you check logs?
    0 讨论(0)
  • 2020-12-10 14:45

    @CodeMed, it took me a while, but I was able to reproduce the issue. It might be a configuration issue : @PersistenceContext might be scanned twice, it might be scanned by your root-context and your web-context. This cause the @PersistenceContext to be shared, therefore it is not saving your data (Spring doesn't allow that). I found it weird that no messages or logs where displayed . if you tried this snippet below on you Save(Document document) you will see the actual error :

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

    To solve the problem, you can do the following (avoid the @PersistenceContext to be scanned twice) :

    1- Make sure that all your controller are in a separate package like com.mycompany.myapp.controller, and in your web-context use the component-scan as <context:component-scan annotation-config="true" base-package="com.mycompany.myapp.controller" />

    2- Make sure that others component are in differents package other than the controller package , for example : com.mycompany.myapp.dao, com.mycompany.myapp.service .... and then in your root-context use the component-scan as <context:component-scan annotation-config="true" base-package="com.mycompany.myapp.service, com.mycompany.myapp.dao" />

    Or show me yours spring xml configurations and your web.xml, I will point you to the right direction

    0 讨论(0)
提交回复
热议问题