Spring 3 maven 2 web application step by step

本文详细介绍了一个基于Spring MVC的实战项目,包括Maven配置、Hibernate持久化、Ehcache缓存支持等内容,并提供从零开始搭建项目的步骤。文章还涵盖了集成测试示例。

Before writting this I admin that there are lots of tutorials like this but what I found is that they are not step by step guides, with missing vital parts of process of making it all happen from scratch. This makes you revert to the documentation and therefore defies the point of going through the tutorial. With this one I hope to make it a bit clearer, though I do not attempt to make it IDE specific, therefore there will be no guidance on how to do it in specific IDE. The layout will be to work with standard Maven project structure.

What is in this tutorial?

  • Maven Spring project configurations
  • Persistence using Hibernate and HSQLDB
  • Cache support using Ehcahe
  • Spring MVC configurations
  • Example integration tests for DB and Caching (Please not there are no Unit tests as I assume that the reader is familiar with those already)

Link to the full set of sources for this tutorial:

denis-pavlov-spring3mvc-mvn2-example-updated.zip

First things first - the POM:

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
  2.     <modelversion>4.0.0</modelversion>  
  3.     <groupid>dp.examples</groupid>  
  4.     <artifactid>shoppingcart</artifactid>  
  5.     <version>0.0.1-SNAPSHOT</version>  
  6.     <packaging>war</packaging>  
  7.     <name>ShoppingCart</name>  
  8.   
  9.     <build>  
  10.         <plugins>  
  11.             <plugin>  
  12.                 <artifactid>maven-compiler-plugin</artifactid>  
  13.                 <configuration>  
  14.                     <source>1.5  
  15.                     <target>1.5</target>  
  16.                 </configuration>  
  17.             </plugin>  
  18.         </plugins>  
  19.     </build>  
  20.   
  21.     <!-- Shared version number properties -->  
  22.     <properties>  
  23.         <org.springframework.version>3.1.2.RELEASE</org.springframework.version>  
  24.   
  25.         <commons-dbcp.version>1.4</commons-dbcp.version>  
  26.         <sqlite-jdbc.version>3.7.2</sqlite-jdbc.version>  
  27.         <hsqldb-jdbc.version>2.2.9</hsqldb-jdbc.version>  
  28.   
  29.         <hibernate-core.version>4.1.9.Final</hibernate-core.version>  
  30.         <hibernate-search.version>4.2.0.Final</hibernate-search.version>  
  31.   
  32.         <xalan.version>2.7.0</xalan.version>  
  33.   
  34.         <backport-util-concurrent.version>3.1</backport-util-concurrent.version>  
  35.   
  36.         <ehcache.version>2.6.6</ehcache.version>  
  37.   
  38.     </properties>  
  39.   
  40.     <dependencies>  
  41.   
  42.         <dependency>  
  43.             <groupid>log4j</groupid>  
  44.             <artifactid>log4j</artifactid>  
  45.             <version>1.2.15</version>  
  46.             <exclusions>  
  47.                 <exclusion>  
  48.                     <groupid>javax.mail</groupid>  
  49.                     <artifactid>mail</artifactid>  
  50.                 </exclusion>  
  51.                 <exclusion>  
  52.                     <groupid>javax.jms</groupid>  
  53.                     <artifactid>jms</artifactid>  
  54.                 </exclusion>  
  55.                 <exclusion>  
  56.                     <groupid>com.sun.jdmk</groupid>  
  57.                     <artifactid>jmxtools</artifactid>  
  58.                 </exclusion>  
  59.                 <exclusion>  
  60.                     <groupid>com.sun.jmx</groupid>  
  61.                     <artifactid>jmxri</artifactid>  
  62.                 </exclusion>  
  63.             </exclusions>  
  64.             <scope>runtime</scope>  
  65.         </dependency>  
  66.   
  67.         <!-- 
  68.               JSP taglib support 
  69.           -->  
  70.         <dependency>  
  71.             <groupid>taglibs</groupid>  
  72.             <artifactid>standard</artifactid>  
  73.             <version>1.1.2</version>  
  74.         </dependency>  
  75.         <dependency>  
  76.             <groupid>javax.servlet</groupid>  
  77.             <artifactid>jstl</artifactid>  
  78.             <version>1.2</version>  
  79.         </dependency>  
  80.   
  81.   
  82.         <!--  
  83.               Core utilities used by other modules.  
  84.               Define this if you use Spring Utility APIs (org.springframework.core.*/org.springframework.util.*)  
  85.           -->  
  86.         <dependency>  
  87.             <groupid>org.springframework</groupid>  
  88.             <artifactid>spring-core</artifactid>  
  89.             <version>${org.springframework.version}</version>  
  90.         </dependency>  
  91.   
  92.         <!--  
  93.               Expression Language (depends on spring-core)  
  94.               Define this if you use Spring Expression APIs (org.springframework.expression.*)  
  95.           -->  
  96.         <dependency>  
  97.             <groupid>org.springframework</groupid>  
  98.             <artifactid>spring-expression</artifactid>  
  99.             <version>${org.springframework.version}</version>  
  100.         </dependency>  
  101.   
  102.         <!--  
  103.               Bean Factory and JavaBeans utilities (depends on spring-core)  
  104.               Define this if you use Spring Bean APIs (org.springframework.beans.*)  
  105.           -->  
  106.         <dependency>  
  107.             <groupid>org.springframework</groupid>  
  108.             <artifactid>spring-beans</artifactid>  
  109.             <version>${org.springframework.version}</version>  
  110.         </dependency>  
  111.   
  112.         <!--  
  113.               Aspect Oriented Programming (AOP) Framework (depends on spring-core, spring-beans)  
  114.               Define this if you use Spring AOP APIs (org.springframework.aop.*)  
  115.           -->  
  116.         <dependency>  
  117.             <groupid>org.springframework</groupid>  
  118.             <artifactid>spring-aop</artifactid>  
  119.             <version>${org.springframework.version}</version>  
  120.         </dependency>  
  121.   
  122.         <!--  
  123.               Application Context (depends on spring-core, spring-expression, spring-aop, spring-beans)  
  124.               This is the central artifact for Spring's Dependency Injection Container and is generally always defined  
  125.           -->  
  126.         <dependency>  
  127.             <groupid>org.springframework</groupid>  
  128.             <artifactid>spring-context</artifactid>  
  129.             <version>${org.springframework.version}</version>  
  130.         </dependency>  
  131.   
  132.         <!--  
  133.               Various Application Context utilities, including EhCache, JavaMail, Quartz, and Freemarker integration  
  134.               Define this if you need any of these integrations  
  135.           -->  
  136.         <dependency>  
  137.             <groupid>org.springframework</groupid>  
  138.             <artifactid>spring-context-support</artifactid>  
  139.             <version>${org.springframework.version}</version>  
  140.         </dependency>  
  141.   
  142.         <!--  
  143.               Transaction Management Abstraction (depends on spring-core, spring-beans, spring-aop, spring-context)  
  144.               Define this if you use Spring Transactions or DAO Exception Hierarchy  
  145.               (org.springframework.transaction.*/org.springframework.dao.*)  
  146.           -->  
  147.         <dependency>  
  148.             <groupid>org.springframework</groupid>  
  149.             <artifactid>spring-tx</artifactid>  
  150.             <version>${org.springframework.version}</version>  
  151.         </dependency>  
  152.   
  153.         <!--  
  154.               JDBC Data Access Library (depends on spring-core, spring-beans, spring-context, spring-tx)  
  155.               Define this if you use Spring's JdbcTemplate API (org.springframework.jdbc.*)  
  156.           -->  
  157.         <dependency>  
  158.             <groupid>org.springframework</groupid>  
  159.             <artifactid>spring-jdbc</artifactid>  
  160.             <version>${org.springframework.version}</version>  
  161.         </dependency>  
  162.   
  163.         <!--  
  164.               Object-to-Relation-Mapping (ORM) integration with Hibernate, JPA, and iBatis.  
  165.               (depends on spring-core, spring-beans, spring-context, spring-tx)  
  166.               Define this if you need ORM (org.springframework.orm.*)  
  167.           -->  
  168.         <dependency>  
  169.             <groupid>org.springframework</groupid>  
  170.             <artifactid>spring-orm</artifactid>  
  171.             <version>${org.springframework.version}</version>  
  172.         </dependency>  
  173.   
  174.         <!--  
  175.               Object-to-XML Mapping (OXM) abstraction and integration with JAXB, JiBX, Castor, XStream, and XML Beans.  
  176.               (depends on spring-core, spring-beans, spring-context)  
  177.               Define this if you need OXM (org.springframework.oxm.*)  
  178.           -->  
  179.         <dependency>  
  180.             <groupid>org.springframework</groupid>  
  181.             <artifactid>spring-oxm</artifactid>  
  182.             <version>${org.springframework.version}</version>  
  183.         </dependency>  
  184.   
  185.         <!--  
  186.               Web application development utilities applicable to both Servlet and Portlet Environments  
  187.               (depends on spring-core, spring-beans, spring-context)  
  188.               Define this if you use Spring MVC, or wish to use Struts, JSF, or another web framework with Spring (org.springframework.web.*)  
  189.           -->  
  190.         <dependency>  
  191.             <groupid>org.springframework</groupid>  
  192.             <artifactid>spring-web</artifactid>  
  193.             <version>${org.springframework.version}</version>  
  194.         </dependency>  
  195.   
  196.         <!--  
  197.               Spring MVC for Servlet Environments (depends on spring-core, spring-beans, spring-context, spring-web)  
  198.               Define this if you use Spring MVC with a Servlet Container such as Apache Tomcat (org.springframework.web.servlet.*)  
  199.           -->  
  200.         <dependency>  
  201.             <groupid>org.springframework</groupid>  
  202.             <artifactid>spring-webmvc</artifactid>  
  203.             <version>${org.springframework.version}</version>  
  204.         </dependency>  
  205.   
  206.         <!--  
  207.               Spring MVC for Portlet Environments (depends on spring-core, spring-beans, spring-context, spring-web)  
  208.               Define this if you use Spring MVC with a Portlet Container (org.springframework.web.portlet.*)  
  209.           -->  
  210.         <dependency>  
  211.             <groupid>org.springframework</groupid>  
  212.             <artifactid>spring-webmvc-portlet</artifactid>  
  213.             <version>${org.springframework.version}</version>  
  214.         </dependency>  
  215.   
  216.         <!--  
  217.               Support for testing Spring applications with tools such as JUnit and TestNG  
  218.               This artifact is generally always defined with a 'test' scope for the integration testing framework and unit testing stubs  
  219.           -->  
  220.         <dependency>  
  221.             <groupid>org.springframework</groupid>  
  222.             <artifactid>spring-test</artifactid>  
  223.             <version>${org.springframework.version}</version>  
  224.             <scope>test</scope>  
  225.         </dependency>  
  226.   
  227.         <!-- hibernate -->  
  228.         <dependency>  
  229.             <groupid>org.hibernate</groupid>  
  230.             <artifactid>hibernate-core</artifactid>  
  231.             <version>${hibernate-core.version}</version>  
  232.             <exclusions>  
  233.                 <exclusion>  
  234.                     <groupid>xml-apis</groupid>  
  235.                     <artifactid>xml-apis</artifactid>  
  236.                 </exclusion>  
  237.             </exclusions>  
  238.         </dependency>  
  239.         <dependency>  
  240.             <groupid>xalan</groupid>  
  241.             <artifactid>xalan</artifactid>  
  242.             <version>${xalan.version}</version>  
  243.         </dependency>  
  244.         <!-- dependency>  
  245.            <groupId>org.hibernate</groupId>  
  246.            <artifactId>hibernate-search</artifactId>  
  247.            <version>${hibernate-search.version}</version>  
  248.        </dependency -->  
  249.   
  250.         <!-- connection pooling -->  
  251.         <dependency>  
  252.             <groupid>commons-dbcp</groupid>  
  253.             <artifactid>commons-dbcp</artifactid>  
  254.             <version>${commons-dbcp.version}</version>  
  255.             <scope>runtime</scope>  
  256.         </dependency>  
  257.   
  258.         <!-- HSQLDB JDBC library -->  
  259.         <dependency>  
  260.             <groupid>org.hsqldb</groupid>  
  261.             <artifactid>hsqldb</artifactid>  
  262.             <version>${hsqldb-jdbc.version}</version>  
  263.             <scope>runtime</scope>  
  264.         </dependency>  
  265.   
  266.         <!-- EhCache -->  
  267.         <dependency>  
  268.             <groupid>net.sf.ehcache</groupid>  
  269.             <artifactid>ehcache-core</artifactid>  
  270.             <version>${ehcache.version}</version>  
  271.         </dependency>  
  272.   
  273.         <!-- Unit and integration testing -->  
  274.         <dependency>  
  275.             <groupid>junit</groupid>  
  276.             <artifactid>junit</artifactid>  
  277.             <version>4.9</version>  
  278.             <scope>test</scope>  
  279.         </dependency>  
  280.   
  281.   
  282.     </dependencies>  
  283. </project>  

At this point we have done all we need maven wise and can safely proceed to next step.

BTW if you are using eclipse and started of with a maven2 project and now trying to convert it to web projects I think you need to read this

We are making a webapp, so the second thing is web.xml (That should be in the src/main/webapp/WEB-INF/):

  1. <!--?xml version="1.0" encoding="UTF-8"?-->  
  2.   
  3. <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/j2ee   
  4.          http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">  
  5.     
  6.   <context-param>  
  7.     <param-name>log4jConfigLocation</param-name>  
  8.     <param-value>classpath:META-INF/properties/log4j.properties</param-value>  
  9.   </context-param>  
  10.   <listener>  
  11.     <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>  
  12.   </listener>  
  13.     
  14.   <!-- Spring context -->  
  15.   <context-param>  
  16.     <param-name>contextConfigLocation</param-name>  
  17.     <param-value>classpath*:WEB-INF/spring/*.xml</param-value>  
  18.   </context-param>  
  19.   <listener>  
  20.     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  21.   </listener>  
  22.     
  23.   <!-- Spring webapp  -->  
  24.   <servlet>  
  25.     <servlet-name>spring</servlet-name>  
  26.     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  27.     <init-param>  
  28.       <param-name>contextConfigLocation</param-name>  
  29.       <param-value>/WEB-INF/spring/*.xml</param-value>  
  30.     </init-param>  
  31.     <load-on-startup>1</load-on-startup>  
  32.   </servlet>  
  33.     
  34.   <servlet-mapping>  
  35.     <servlet-name>spring</servlet-name>  
  36.     <url-pattern>/</url-pattern>  
  37.   </servlet-mapping>  
  38.   
  39.   <welcome-file-list>  
  40.     <welcome-file>  
  41.       index.jsp  
  42.     </welcome-file>  
  43.   </welcome-file-list>  
  44.   
  45. </web-app>  

Then the Spring context configuration (for webapp that is located in WEB-INF/spring/ directory as specified in web.xml parameters to the servlet):

  1. <!--?xml version="1.0" encoding="UTF-8"?-->  
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
  3. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd  
  4. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
  5. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
  6. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">  
  7.   
  8.     <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
  9.         <property name="locations">  
  10.             <list>  
  11.                 <!-- You can have multiple files here -->  
  12.                 <value>classpath:spring-context.properties</value>  
  13.             </list>  
  14.         </property>  
  15.     </bean>  
  16.   
  17.   
  18.     <import resource="classpath*:spring/cache.xml">  
  19.     <import resource="classpath*:spring/persistence.xml">  
  20.   
  21.     <!-- Service to manipulate the cart -->  
  22.     <bean id="cartService" parent="txProxyTemplate">  
  23.         <property name="target">  
  24.             <bean class="dp.example.shoppingcart.service.impl.WebCartServiceCached">  
  25.                 <constructor-arg index="0" ref="cartDao">  
  26.             </constructor-arg></bean>  
  27.         </property>  
  28.     </bean>  
  29.       
  30.     <!-- Scans the classpath of this application for @Components to deploy as beans -->  
  31.     <context:component-scan base-package="dp.example.shoppingcart.web">  
  32.   
  33.     <!-- Configures the @Controller programming model -->  
  34.     <mvc:annotation-driven>  
  35.   
  36.     <!-- Forwards requests to the "/" resource to the "welcome" view -->  
  37.     <mvc:view-controller path="/" view-name="cart">  
  38.   
  39.     <!-- Configures Handler Interceptors -->      
  40.     <mvc:interceptors>  
  41.         <!-- Changes the locale when a 'locale' request parameter is sent; e.g. /?locale=de -->  
  42.         <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">  
  43.     </bean></mvc:interceptors>  
  44.   
  45.     <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources/ directory -->  
  46.     <mvc:resources mapping="/resources/**" location="/resources/">  
  47.   
  48.     <!-- Saves a locale change using a cookie -->  
  49.     <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">  
  50.   
  51.     <!-- Application Message Bundle -->  
  52.     <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
  53.         <property name="basename" value="/WEB-INF/messages/messages">  
  54.         <property name="cacheSeconds" value="0">  
  55.     </property></property></bean>  
  56.   
  57.     <!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->  
  58.     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  59.         <property name="prefix" value="/WEB-INF/views/">  
  60.         <property name="suffix" value=".jsp">  
  61.     </property></property></bean>  
  62.   
  63.   
  64. </bean></mvc:resources></mvc:view-controller></mvc:annotation-driven></context:component-scan></import></import></beans>  

The main context file contains two imports: spring/cache.xml that contains ehcache configuration and spring/persistence.xml that contains hibernate configuration

  1. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:util="http://www.springframework.org/schema/util" xsi:schemalocation="http://www.springframework.org/schema/beans  
  2.        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  3.        http://www.springframework.org/schema/cache  
  4.        http://www.springframework.org/schema/cache/spring-cache.xsd  
  5.        http://www.springframework.org/schema/util  
  6.        http://www.springframework.org/schema/util/spring-util-3.2.xsd">  
  7.   
  8.     <cache:annotation-driven>  
  9.   
  10.     <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cachemanager-ref="ehcache">  
  11.   
  12.     <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configlocation="classpath:ehcache.xml" p:shared="true">  
  13.   
  14. </bean></bean></cache:annotation-driven></beans>  

Note the p:shared="true" attribute. This ensures that we are using singleton manager for the Ehcache, which is important if you would like to use the same cache manager for your webapp and your hibernate 2nd level cache. "p:configLocation="classpath:ehcache.xml" tells Spring that we what to use the src/main/resources/ehcache.xml for our cache configurations

  1. <ehcache updatecheck="false">  
  2.   
  3.     <defaultcache maxelementsinmemory="10000" overflowtodisk="false" eternal="false" timetoidleseconds="1800" timetoliveseconds="3600">  
  4.   
  5.     <!-- ############################################# cluster cache #######################################-->  
  6.   
  7.     <cache name="cart" maxelementsinmemory="100" overflowtodisk="false" eternal="false" timetoliveseconds="86400" timetoidleseconds="7200">  
  8.   
  9. </cache></defaultcache></ehcache>  

Persistence condifuration is quite simple. We setup dataSource first. Note that we use Spring variable substitution, which is defined by propertyConfigurer bean in context.xml that takes values from spring-context.properties. Then we define the hibernate factory and txProxyTemplate, which is used by transactional beans. It is good practice to manage transaction boundaries on the service layer. I.e. each method of service beans represents a unit of work and hence is a good candidate for a single transaction. For that purpose our Data access object is specifically not marked as transactional to prevent using it directly and hence have transaction per single db operation, which is not optimal. Instead we will make our service (WebCartServiceCached) transactional which will be using DAO and hence the transaction will be propagated to DAO.

  1. <!--?xml version="1.0" encoding="UTF-8"?-->  
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xmlns:ctx="http://www.springframework.org/schema/context" xsi:schemalocation="http://www.springframework.org/schema/beans  
  3.                            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd  
  4.                            http://www.springframework.org/schema/util  
  5.                            http://www.springframework.org/schema/util/spring-util-3.1.xsd  
  6.                            http://www.springframework.org/schema/context  
  7.                            http://www.springframework.org/schema/context/spring-context-3.1.xsd">  
  8.   
  9.     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
  10.         <property name="driverClassName" value="${db.driverClassName}">  
  11.         <property name="url" value="${db.url}">  
  12.         <property name="username" value="${db.username}">  
  13.         <property name="password" value="${db.password}">  
  14.     </property></property></property></property></bean>  
  15.   
  16.     <util:properties id="hibernateProperties" location="classpath:hibernate/hibernate-hsqldb.properties">  
  17.   
  18.     <!-- Hibernate ******************************************************************* -->  
  19.   
  20.     <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">  
  21.         <property name="dataSource" ref="dataSource">  
  22.         <property name="hibernateProperties" ref="hibernateProperties">  
  23.         <property name="mappingResources">  
  24.             <list>  
  25.                 <value>hibernate/mapping.hbm.xml</value>  
  26.             </list>  
  27.         </property>  
  28.     </property></property></bean>  
  29.   
  30.     <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">  
  31.         <property name="sessionFactory" ref="sessionFactory">  
  32.     </property></bean>  
  33.   
  34.     <bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  
  35.         <description>  
  36.             NOTE: see org.springframework.transaction.TransactionDefinition  
  37.   
  38.             DAO: all beans must be wrapped into this proxy to allow seamless  
  39.             transaction management integration via AOP.  
  40.         </description>  
  41.         <property name="transactionManager" ref="transactionManager">  
  42.         <property name="transactionAttributes">  
  43.             <props>  
  44.                 <!--<prop key="add*">PROPAGATION_REQUIRED,-Throwable</prop>-->  
  45.                 <!--<prop key="remove*">PROPAGATION_REQUIRED,-Throwable</prop>-->  
  46.                 <prop key="*">PROPAGATION_REQUIRED,-Throwable</prop>  
  47.             </props>  
  48.         </property>  
  49.     </property></bean>  
  50.   
  51.     <bean id="cartDao" class="dp.example.shoppingcart.dao.impl.CartDaoImpl">  
  52.         <constructor-arg index="0" ref="sessionFactory">  
  53.     </constructor-arg></bean>  
  54.   
  55. </util:properties></beans>  

DAO object is very simple and uses Hibernates current session which is provided by txProxyTemplate from WebCartService to perform DB operations. Note that if we to use DAO bean directly we would have a Hibernate exception and no session is available standalone.

  1. package dp.example.shoppingcart.dao.impl;  
  2.   
  3. import dp.example.shoppingcart.dao.CartDao;  
  4. import dp.example.shoppingcart.domain.impl.CartEntity;  
  5. import dp.example.shoppingcart.dto.Cart;  
  6. import dp.example.shoppingcart.dto.Item;  
  7. import dp.example.shoppingcart.service.CartService;  
  8. import org.hibernate.SessionFactory;  
  9.   
  10. import java.util.List;  
  11.   
  12. /** 
  13.  * Web implementation of cart service 
  14.  *  
  15.  * @author DPavlov 
  16.  */  
  17. public class CartDaoImpl implements CartDao  
  18. {  
  19.   
  20.     private final SessionFactory sessionFactory;  
  21.   
  22.     public CartDaoImpl(final SessionFactory sessionFactory) {  
  23.         this.sessionFactory = sessionFactory;  
  24.     }  
  25.   
  26.     public Cart createCart() {  
  27.         final Cart cart = new CartEntity();  
  28.         save(cart);  
  29.         return cart;  
  30.     }  
  31.   
  32.     public Cart findCart(final long pk) {  
  33.         return (Cart) sessionFactory.getCurrentSession().get(CartEntity.class, pk);  
  34.     }  
  35.   
  36.     public void save(final Cart cart) {  
  37.         sessionFactory.getCurrentSession().save(cart);  
  38.     }  
  39. }  

Our service uses DAO to provide business functions represented by WebCartServiceCached methods. For example #addToCart(final long pk, String item) will either find or create cart with specific primary key, add and item and persist the cart. All these db operations will happen in a single transaction. And if we have an exception all these changes are rolled back. So we have a good "all or nothing" strategy that will keep our db in a consistent state.

Caching is very simple. Facilitated by two annotations: @Cacheable(value = "cart") and @CacheEvict(value = "cart", allEntries = false, key = "#pk"). There are lots of configurations for this but this is the basic setup. @Cacheable tells ehcache to use cache named "cart" (this is why we have this entry in ehcache.xml) to keep results of this method. @CacheEvict allows to remove cache entries. In this case we remove a single entry by key 'pk', but it is possible to evict all items. As I already said it is very configurable.

  1. package dp.example.shoppingcart.service.impl;  
  2.   
  3. import dp.example.shoppingcart.dao.CartDao;  
  4. import dp.example.shoppingcart.dto.Cart;  
  5. import dp.example.shoppingcart.dto.Item;  
  6. import dp.example.shoppingcart.service.CartService;  
  7. import org.springframework.cache.annotation.CacheEvict;  
  8. import org.springframework.cache.annotation.Cacheable;  
  9.   
  10. import java.util.List;  
  11.   
  12. /** 
  13.  * Web implementation of cart service 
  14.  *  
  15.  * @author DPavlov 
  16.  */  
  17. public class WebCartServiceCached implements CartService  
  18. {  
  19.   
  20.     private final CartDao cartDao;  
  21.   
  22.     public WebCartServiceCached(final CartDao cartDao) {  
  23.         this.cartDao = cartDao;  
  24.     }  
  25.   
  26.     @CacheEvict(value = "cart", allEntries = false, key = "#pk")  
  27.     public void addToCart(final long pk, String item) {  
  28.         final Cart cart = findOrCreateCart(pk);  
  29.         cart.addItem(item);  
  30.         cartDao.save(cart);  
  31.     }  
  32.   
  33.     @Cacheable(value = "cart")  
  34.     public List<item> getItemsInCart(final long pk) {  
  35.         final Cart cart = findOrCreateCart(pk);  
  36.         return cart.getItems();  
  37.     }  
  38.   
  39.     @CacheEvict(value = "cart", allEntries = false, key = "#pk")  
  40.     public void removeFromCart(final long pk, String item) {  
  41.         final Cart cart = findOrCreateCart(pk);  
  42.         cart.removeItem(item);  
  43.         cartDao.save(cart);  
  44.     }  
  45.   
  46.   
  47.     private Cart findOrCreateCart(final long pk) {  
  48.         Cart cart = cartDao.findCart(pk);  
  49.         if (cart == null) {  
  50.             cart = cartDao.createCart();  
  51.         }  
  52.         return cart;  
  53.     }  
  54.   
  55.   
  56. }  
  57. </item>  

Basic controller uses @RequestMapping and @PathVariable to give a nice looking and SEO friendly url pattern.

  1. package dp.example.shoppingcart.web;  
  2.   
  3. import dp.example.shoppingcart.dto.Item;  
  4. import dp.example.shoppingcart.service.CartService;  
  5. import org.springframework.beans.factory.annotation.Autowired;  
  6. import org.springframework.stereotype.Controller;  
  7. import org.springframework.web.bind.annotation.PathVariable;  
  8. import org.springframework.web.bind.annotation.RequestMapping;  
  9. import org.springframework.web.bind.annotation.RequestMethod;  
  10. import org.springframework.web.servlet.ModelAndView;  
  11.   
  12. import java.util.List;  
  13.   
  14. /** 
  15.  * Basic controller 
  16.  *  
  17.  * @author DPavlov 
  18.  */  
  19. @Controller  
  20. public class CartController {  
  21.       
  22.     private CartService cartService;  
  23.       
  24.     private int counter = 1;  
  25.   
  26.     @Autowired  
  27.     public CartController(CartService cartService) {  
  28.         this.cartService = cartService;  
  29.     }  
  30.       
  31.     @RequestMapping(value = "/cart/{pk}", method = RequestMethod.GET)  
  32.     public ModelAndView getCart(@PathVariable final long pk) {  
  33.         final List<item> list = cartService.getItemsInCart(pk);  
  34.         ModelAndView mav = new ModelAndView("cart");  
  35.         mav.addObject("pk", pk);  
  36.         mav.addObject("items", list);  
  37.         mav.addObject("add""Item" + counter++);  
  38.         return mav;  
  39.           
  40.     }  
  41.       
  42.     @RequestMapping(value = "/cart/{pk}/addtocart/{item}", method = RequestMethod.GET)  
  43.     public String addToCart(@PathVariable final long pk, @PathVariable final String item) {  
  44.           
  45.         cartService.addToCart(pk, item);  
  46.         return "redirect:/cart/" + pk;  // Do redirect to prevent double post  
  47.           
  48.     }  
  49.       
  50.     @RequestMapping(value = "/cart/{pk}/removefromcart/{item}", method = RequestMethod.GET)  
  51.     public String removeFromCart(@PathVariable final long pk, @PathVariable final String item) {  
  52.           
  53.         cartService.removeFromCart(pk, item);  
  54.         return "redirect:/cart/" + pk;  // Do redirect to prevent double post  
  55.           
  56.     }  
  57.   
  58. }  
  59. </item>  

Basic view (if you look at the spring context.xml file you will see that we are using spring resolver that look for views in WEB-INF/views/ and uses view name as file name with extension .jsp):

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>  
  2. <%@ page isELIgnored="false" %>  
  3. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
  4. <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>  
  5.   
  6. <title>Items</title>  
  7.   
  8.   <h3>Items:</h3>  
  9.   <c:foreach var="item" items="${items}">  
  10.     <div>${item.articleNo} @ ${item.price}, qty: ${item.quantity}  <a href="${pageContext.request.contextPath}/cart/${pk}/removefromcart/${item.articleNo}">[-] remove</a> <a href="${pageContext.request.contextPath}/cart/${pk}/addtocart/${item.articleNo}">[+] add</a></div>  
  11.   </c:foreach>  
  12.   <a href="${pageContext.request.contextPath}/cart/${pk}/addtocart/${add}">add "${add}"</a>  

${pageContext.request.contextPath} in jsp allows to reference the root of your webapp. This is very usefull if your webapp context name changes from environment to environment as this kind of url building approach will work everywhere consistently. Now if you navigate to localhost:8080/cart/1 you will see contents for cart with PK 1.

Here is screenshot of the outcome:

This sums up the basic Spring webapp project. Now we can have a look at how we can do integration tests for what we have written.

Firstly we need an alternative Spring context where we can remove all the MVC part so we can use it for core testing (i.e. services, dao). Note: that I am jumping to integration tests straight away since I assume that you know (and already done) unit tests. Integration tests is the final frontier! Before doing such tests appropriate unit test needs to be done! Please do not substitute these kind of tests for unit tests as they are considerably slower to run and much harder to test thoroughly.

  1. package dp.example.shoppingcart.service.impl;  
  2.   
  3. import dp.example.shoppingcart.dto.Item;  
  4. import dp.example.shoppingcart.service.CartService;  
  5. import org.junit.Test;  
  6. import org.junit.runner.RunWith;  
  7. import org.springframework.beans.factory.annotation.Autowired;  
  8. import org.springframework.test.context.ContextConfiguration;  
  9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
  10. import org.springframework.transaction.annotation.Transactional;  
  11.   
  12. import java.math.BigDecimal;  
  13. import java.util.HashMap;  
  14. import java.util.List;  
  15. import java.util.Map;  
  16.   
  17. import static org.junit.Assert.*;  
  18.   
  19. /** 
  20.  * User: denispavlov 
  21.  * Date: 29/11/2013 
  22.  * Time: 13:17 
  23.  */  
  24. @RunWith(SpringJUnit4ClassRunner.class)  
  25. @ContextConfiguration(locations = { "/spring/testContext.xml" })  
  26. @Transactional  
  27. public class WebCartServiceCachedTest {  
  28.   
  29.     @Autowired  
  30.     private CartService cartService;  
  31.   
  32.     /** 
  33.      * This is NOT unit test but rather an integration suite that proves that 
  34.      * our business logic works as expected using actual db and all the wiring. 
  35.      */  
  36.     @Test  
  37.     public void testCartCRUDOperationsIntegrationTest() throws Exception {  
  38.   
  39.         List<item> items;  
  40.   
  41.         // Check empty initially  
  42.         items = cartService.getItemsInCart(1L);  
  43.   
  44.         assertNotNull(items);  
  45.         assertTrue(items.isEmpty());  
  46.   
  47.         // Add one item and check contents of cart  
  48.         cartService.addToCart(1L, "ABC");  
  49.         items = cartService.getItemsInCart(1L);  
  50.   
  51.         assertNotNull(items);  
  52.         assertFalse(items.isEmpty());  
  53.         assertEquals(1, items.size());  
  54.         assertEquals("ABC", items.get(0).getArticleNo());  
  55.         assertEquals(new BigDecimal(1).compareTo(items.get(0).getQuantity()), 0);  
  56.   
  57.         // Add same item and check contents of cart  
  58.         cartService.addToCart(1L, "ABC");  
  59.         items = cartService.getItemsInCart(1L);  
  60.   
  61.         assertNotNull(items);  
  62.         assertFalse(items.isEmpty());  
  63.         assertEquals(1, items.size());  
  64.         assertEquals("ABC", items.get(0).getArticleNo());  
  65.         assertEquals(new BigDecimal(2).compareTo(items.get(0).getQuantity()), 0);  
  66.   
  67.         // Add another item and check contents of cart  
  68.         cartService.addToCart(1L, "DEF");  
  69.         items = cartService.getItemsInCart(1L);  
  70.   
  71.         assertNotNull(items);  
  72.         assertFalse(items.isEmpty());  
  73.         assertEquals(2, items.size());  
  74.         // we re-map the items in list to have deterministic assertions  
  75.         final Map<string, item=""> byArticle = new HashMap<string, item="">();  
  76.         for (final Item item : items) {  
  77.             byArticle.put(item.getArticleNo(), item);  
  78.         }  
  79.         // deterministic assertions by article no  
  80.         assertTrue(byArticle.containsKey("ABC"));  
  81.         assertEquals(new BigDecimal(2).compareTo(byArticle.get("ABC").getQuantity()), 0);  
  82.         assertTrue(byArticle.containsKey("DEF"));  
  83.         assertEquals(new BigDecimal(1).compareTo(byArticle.get("DEF").getQuantity()), 0);  
  84.   
  85.         // Remove one item  
  86.         cartService.removeFromCart(1L, "DEF");  
  87.         items = cartService.getItemsInCart(1L);  
  88.   
  89.         assertNotNull(items);  
  90.         assertFalse(items.isEmpty());  
  91.         assertEquals(1, items.size());  
  92.         assertEquals("ABC", items.get(0).getArticleNo());  
  93.         assertEquals(new BigDecimal(2).compareTo(items.get(0).getQuantity()), 0);  
  94.   
  95.         // Remove last item  
  96.         cartService.removeFromCart(1L, "ABC");  
  97.         items = cartService.getItemsInCart(1L);  
  98.   
  99.         assertNotNull(items);  
  100.         assertTrue(items.isEmpty());  
  101.   
  102.     }  
  103.   
  104.     /** 
  105.      * Since we already established that the CRUD works assertions in this test are specific 
  106.      * to making sure that we get the cached list back, so we do not need to check individual elements. 
  107.      * 
  108.      * @throws Exception 
  109.      */  
  110.     @Test  
  111.     public void testCachingIsWorking() throws Exception {  
  112.   
  113.         // Check empty initially  
  114.         List<item> items1st = cartService.getItemsInCart(2L);  
  115.         assertEquals(0, items1st.size());  
  116.   
  117.         // add cart item  
  118.         cartService.addToCart(2L, "ABC");  
  119.         List<item> items2nd = cartService.getItemsInCart(2L);  
  120.         assertEquals(1, items2nd.size());  
  121.   
  122.         // now we can ask for the cart contents again and assert that is it same instance of list  
  123.         List<item> items3rd = cartService.getItemsInCart(2L);  
  124.         assertSame(items3rd, items2nd);  
  125.   
  126.         // we expect that adding items will evict cache  
  127.         cartService.addToCart(2L, "ABC");  
  128.         List<item> items4th = cartService.getItemsInCart(2L);  
  129.         assertEquals(1, items4th.size());  
  130.         // but these list should be different now  
  131.         assertNotSame(items2nd, items4th);  
  132.   
  133.         // now check the cart contents again which should be cached object from 4th call  
  134.         List<item> items5th = cartService.getItemsInCart(2L);  
  135.         assertSame(items4th, items5th);  
  136.   
  137.         // make sure that after adding to a different cart we still have cached version of list for 2L  
  138.         cartService.addToCart(1L, "ABC");  
  139.         List<item> items6th = cartService.getItemsInCart(2L);  
  140.         assertSame(items4th, items6th);  
  141.   
  142.   
  143.     }  
  144. }  
  145. </item></item></item></item></item></item></string,></string,></item>  

The important elements are: include JUnit dependency in pom.xml, use @RunWith and @ContextConfiguration to tell JUnit what context configuration file you want to use. If you are testing DAO objects in isolation you can add @Transactional annotation to your Test suite at class level to provide the transaction support.

You also may have noticed that we use alternative context file /spring/testContext.xml. This context imports our original cache.xml and persistence.xml but provides override for spring-context.properties to supply alternative data source configurations and also does not contain the MVC configuration which are not needed for this test.

  1. <!--?xml version="1.0" encoding="UTF-8"?-->  
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
  3. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd  
  4.   
  5.   
  6. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">  
  7.   
  8.   
  9.     <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
  10.         <property name="locations">  
  11.             <list>  
  12.                 <!-- spring-testContext will override any properties in spring-context as it comes after -->  
  13.                 <value>classpath:spring-context.properties</value>  
  14.                 <value>classpath:spring-testContext.properties</value>  
  15.             </list>  
  16.         </property>  
  17.     </bean>  
  18.   
  19.     <import resource="classpath*:spring/cache.xml">  
  20.     <import resource="classpath*:spring/persistence.xml">  
  21.   
  22.     <!-- Service to manipulate the cart -->  
  23.     <bean id="cartService" parent="txProxyTemplate">  
  24.         <property name="target">  
  25.             <bean class="dp.example.shoppingcart.service.impl.WebCartServiceCached">  
  26.                 <constructor-arg index="0" ref="cartDao">  
  27.             </constructor-arg></bean>  
  28.         </property>  
  29.     </bean>  
  30.   
  31. </import></import></beans>  

Hope you enjoyed this tutorial and let me know if any information is inaccurate or incomplete. Here is link to the full source for this:

denis-pavlov-spring3mvc-mvn2-example-updated.zip

PLEASE BARE IN MIND THIS IS AN EXAMPLE TO SHOW HOW SPRING MAVEN PROJECT IS SETUP - THIS IS NOT PRODUCTION CODE AND NEVER WAS INTENDED TO IMITATE ONE.

This page was last updated on: 29/11/2013 15:23
<think>嗯,用户想做一个Maven项目,集成MyBatis和Spring,并且是一个批处理项目。首先,我需要确认用户的需求,批处理通常指的是批量处理数据,可能涉及定时任务或者处理大量数据。然后,用户提到了MyBatis和Spring的集成,所以需要确保这两个框架能够协同工作,尤其是在Spring Batch的环境中。 接下来,我应该考虑如何构建一个Maven项目的基本结构,添加必要的依赖项。Spring Boot可能是一个好的起点,因为它简化了配置。需要包括Spring Batch、MyBatis、Spring JDBC以及数据库驱动,比如H2或者MySQL,具体取决于用户的实际数据库选择。 然后,配置数据库连接。在Spring Boot中,通常使用application.properties或application.yml来设置数据源。这里需要确保数据源正确配置,以便MyBatis和Spring Batch都能使用。同时,可能需要配置MyBatis的SqlSessionFactory和Mapper扫描,以便MyBatis能够正确映射接口和XML文件。 接下来,创建批处理作业。Spring Batch的作业通常由JobBuilderFactory和StepBuilderFactory构建。需要定义一个Job,包含一个或多个Step,每个Step可能有ItemReader、ItemProcessor和ItemWriter。这里需要考虑如何从数据库读取数据,处理数据,然后写回数据库或其他存储。 用户可能还需要定义自己的业务实体和Mapper接口,用于MyBatis操作数据库。比如,一个User实体类,对应的UserMapper接口,以及XML映射文件,实现CRUD操作。 在编写批处理逻辑时,可能需要实现ItemReader,例如使用MyBatis的Mapper来分页读取数据,或者使用JdbcCursorItemReader。然后,在ItemProcessor中进行业务处理,最后用ItemWriter写入数据,这里可能需要使用MyBatis的批处理功能,或者MyBatisBatchItemWriter。 还需要考虑事务管理和批处理的配置,确保每一步的事务正确提交,避免重复处理或数据不一致。此外,可能需要配置JobLauncher和JobRegistry,以便在应用启动时运行批处理作业,或者通过REST接口触发。 测试也是一个重要环节,需要确保每个步骤都能正确执行,数据能够正确读取和处理,并且最终结果符合预期。可能需要使用Spring的测试框架,比如@SpringBootTest,来编写集成测试。 另外,用户可能对批处理的性能优化感兴趣,比如分页读取、批处理写入、并发处理等。这时候可以建议使用MyBatis的批处理模式,或者调整Spring Batch的chunk大小,以提高处理效率。 最后,可能需要考虑异常处理和作业的持久化,使用Spring Batch的元数据表来记录作业的执行状态,方便监控和重启失败的作业。 总结一下,步骤大致是:创建Maven项目,添加依赖,配置数据源和MyBatis,定义实体和Mapper,配置Spring Batch作业,实现Reader、Processor、Writer,测试和优化。需要确保各部分正确集成,并且处理流程符合业务需求。</think>好的,我将分步骤为您介绍如何构建一个基于MavenSpring+MyBatis批处理项目: --- ### 一、项目准备 1. **创建Maven项目** ```xml <!-- pom.xml核心依赖 --> <dependencies> <!-- Spring Batch --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> </dependency> <!-- MyBatis-Spring集成 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency> <!-- 数据库驱动(以MySQL为例) --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies> ``` --- ### 二、配置集成 1. **数据库配置(application.yml)** ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/batch_db username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver ``` 2. **MyBatis配置类** ```java @Configuration @MapperScan("com.example.mapper") // MyBatis Mapper接口扫描 public class MyBatisConfig { @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); return sessionFactory.getObject(); } } ``` --- ### 三、批处理实现 1. **定义批处理作业** ```java @Configuration @EnableBatchProcessing public class BatchConfig { @Autowired private JobBuilderFactory jobBuilderFactory; @Autowired private StepBuilderFactory stepBuilderFactory; @Bean public Job dataMigrationJob(Step processStep) { return jobBuilderFactory.get("dataMigrationJob") .start(processStep) .build(); } @Bean public Step processStep(ItemReader<User> reader, ItemProcessor<User, User> processor, ItemWriter<User> writer) { return stepBuilderFactory.get("processStep") .<User, User>chunk(100) // 每100条数据提交一次 .reader(reader) .processor(processor) .writer(writer) .build(); } } ``` --- ### 四、核心组件实现 1. **MyBatis Mapper接口** ```java public interface UserMapper { @Select("SELECT * FROM source_table WHERE status = 0") List<User> selectUnprocessed(); @Update("UPDATE source_table SET status = 1 WHERE id = #{id}") void markAsProcessed(Long id); } ``` 2. **自定义ItemReader(分页读取)** ```java public class MyBatisPagingItemReader implements ItemReader<User> { private int currentPage = 0; private final int pageSize = 100; @Autowired private UserMapper userMapper; @Override public User read() { List<User> users = userMapper.selectUnprocessed(currentPage, pageSize); // 分页逻辑实现... } } ``` 3. **批处理Writer** ```java public class MyBatisBatchWriter implements ItemWriter<User> { @Autowired private UserMapper userMapper; @Override public void write(List<? extends User> items) { items.forEach(user -> { userMapper.insertProcessed(user); // 批量插入 userMapper.markAsProcessed(user.getId()); // 标记处理状态 }); } } ``` --- ### 五、优化建议 1. **事务管理** ```java @Bean public Step processStep() { return stepBuilderFactory.get("processStep") .transactionManager(new DataSourceTransactionManager(dataSource)) // ...其他配置 } ``` 2. **性能调优** ```properties # application.yml mybatis: executor-type: batch # 启用批处理模式 ``` --- ### 六、项目结构示例 ``` src/main/java ├── com.example │ ├── config │ │ ├── BatchConfig.java │ │ └── MyBatisConfig.java │ ├── mapper │ │ └── UserMapper.java │ ├── domain │ │ └── User.java │ └── batch │ ├── reader │ ├── processor │ └── writer ``` --- ### 七、运行与测试 ```java @SpringBootApplication public class BatchApplication implements CommandLineRunner { @Autowired private JobLauncher jobLauncher; @Autowired private Job dataMigrationJob; public static void main(String[] args) { SpringApplication.run(BatchApplication.class, args); } @Override public void run(String... args) throws Exception { JobParameters params = new JobParametersBuilder() .addLong("time", System.currentTimeMillis()) .toJobParameters(); jobLauncher.run(dataMigrationJob, params); } } ``` --- 关键点总结: 1. 通过`@EnableBatchProcessing`启用批处理功能 2. 使用`Chunk-oriented`处理模型实现分片提交 3. MyBatis与Spring事务的深度集成 4. 合理配置数据库连接池(建议使用HikariCP) 5. 批处理元数据表自动创建(需要初始化`spring-batch`数据库表)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值