2017-07-01 127 views
0

我幾天前開始學習Spring MVC,現在我正在嘗試做一個butchUpdate。我發現一個article如何做到這一點,但我得到了一個例外,我正在使用內存數據庫中的HSQLDB。 這裏是我的代碼:Spring JdbcTemplate butchUpdate UPDATE org.springframework.jdbc.BadSqlGrammarException:

public void updateStockBatch(List<Product> allProducts, int addQuantity, int noOfUnits) { 
    String SQL = "UPDATE products SET UNITS_IN_STOCK = ? WHERE ID = ?"; 
    jdbcTemplate.batchUpdate(SQL, new BatchPreparedStatementSetter() { 
     int batchSize = 0; 
     @Override 
     public void setValues(PreparedStatement ps, int i) throws SQLException { 
      for (Product product : allProducts) { 
       if (product.getUnitsInStock() < noOfUnits) { 
        ps.setLong(1, product.getUnitsInStock() + addQuantity); 
        ps.setString(2, product.getProductId()); 
        batchSize++; 
       } 
      } 
     } 
     @Override 
     public int getBatchSize() { 
      return batchSize; 
     } 
    }); 
} 

Type Exception Report 

Message Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [UPDATE products SET UNITS_IN_STOCK = ? WHERE ID = ?]; nested exception is java.sql.SQLException: statement is not in batch mode 

Description The server encountered an unexpected condition that prevented it from fulfilling the request. 

Exception 

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [UPDATE products SET UNITS_IN_STOCK = ? WHERE ID = ?]; nested exception is java.sql.SQLException: statement is not in batch mode 
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982) 
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:634) 
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:741) 
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) 
Root Cause 

org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [UPDATE products SET UNITS_IN_STOCK =? WHERE ID = ?]; nested exception is java.sql.SQLException: statement is not in batch mode 
    org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:99) 
    org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73) 
    org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) 
    org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) 
    org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:649) 
    org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:662) 
    org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:950) 
    com.packt.webstore.domain.repository.impl.InMemoryProductRepository.updateStockBatch(InMemoryProductRepository.java:53) 
    com.packt.webstore.domain.repository.impl.ProductProductServiceImpl.updateAllStock(ProductProductServiceImpl.java:30) 
    com.packt.webstore.controller.ProductController.updateStock(ProductController.java:33) 
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    java.lang.reflect.Method.invoke(Method.java:498) 
    org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) 
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) 
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) 
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) 
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) 
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) 
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) 
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) 
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) 
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:634) 
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:741) 
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) 
Root Cause 

java.sql.SQLException: statement is not in batch mode 
    org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source) 
    org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source) 
    org.hsqldb.jdbc.JDBCUtil.sqlExceptionSQL(Unknown Source) 
    org.hsqldb.jdbc.JDBCPreparedStatement.executeBatch(Unknown Source) 
    org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:966) 
    org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:950) 
    org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633) 
    org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:662) 
    org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:950) 
    com.packt.webstore.domain.repository.impl.InMemoryProductRepository.updateStockBatch(InMemoryProductRepository.java:53) 
    com.packt.webstore.domain.repository.impl.ProductProductServiceImpl.updateAllStock(ProductProductServiceImpl.java:30) 
    com.packt.webstore.controller.ProductController.updateStock(ProductController.java:33) 
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    java.lang.reflect.Method.invoke(Method.java:498) 
    org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) 
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) 
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) 
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) 
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) 
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) 
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) 
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) 
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) 
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:634) 
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:741) 
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) 
Root Cause 

org.hsqldb.HsqlException: statement is not in batch mode 
    org.hsqldb.error.Error.error(Unknown Source) 
    org.hsqldb.error.Error.error(Unknown Source) 
    org.hsqldb.jdbc.JDBCUtil.sqlExceptionSQL(Unknown Source) 
    org.hsqldb.jdbc.JDBCPreparedStatement.executeBatch(Unknown Source) 
    org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:966) 
    org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:950) 
    org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633) 
    org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:662) 
    org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:950) 
    com.packt.webstore.domain.repository.impl.InMemoryProductRepository.updateStockBatch(InMemoryProductRepository.java:53) 
    com.packt.webstore.domain.repository.impl.ProductProductServiceImpl.updateAllStock(ProductProductServiceImpl.java:30) 
    com.packt.webstore.controller.ProductController.updateStock(ProductController.java:33) 
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    java.lang.reflect.Method.invoke(Method.java:498) 
    org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) 
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) 
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) 
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) 
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) 
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) 
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) 
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) 
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) 
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:634) 
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:741) 
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) 

回答

2

你誤會這個工作的方式。 batchUpdate()的原理是執行相同的參數化SQL查詢N次。

BatchPreparedStatementSetter首先由JdbcTemplate調用以知道批量大小。假設它返回10.JdbcTemplate然後將調用setValues()10次(0,然後1,然後2等,直到9)。

您的代碼返回0作爲批處理大小,這沒有意義,它的setValues方法在同一個語句上多次設置相同的參數,這也是沒有意義的。

正確實現的是:

public void updateStockBatch(List<Product> allProducts, int addQuantity, int noOfUnits) { 
    String sql = "UPDATE products SET UNITS_IN_STOCK = ? WHERE ID = ?"; 
    List<Product> productsToUpdate = 
     allProducts.stream() 
        .filter(p -> p.getUnitsInStock() < noOfUnits) 
        .collect(Collectors.toList()); 
    if (!productsToUpdate.isEmpty()) { 

     jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { 

      @Override 
      public int getBatchSize() { 
       return productsToUpdate.size(); 
      } 

      @Override 
      public void setValues(PreparedStatement ps, int i) throws SQLException { 

       Product product : productsToUpdate.get(i); 
       ps.setLong(1, product.getUnitsInStock() + addQuantity); 
       ps.setString(2, product.getProductId()); 
      } 
     }); 
    } 
} 
+0

驚人。現在一切都說得通了。謝謝。 – Sermilion