SpringMVC是越來越火,自己也弄一個Spring+SpringMVC+JPA的簡單框架。
1、搭建環境。
1)下載Spring3.1.2的發布包;Hibernate4.1.7的發布包(沒有使用hibernate的API,只是使用了它對JPA的實現);下載 BoneCP 連接池框架及其依賴的jar,下載緩存框架ehcache,全部所用到的jar包如下:
antlr-2.7.7.jar bonecp-0.7.1.RELEASE.jar bonecp-provider-0.7.1-rc2.jar bonecp-spring-0.7.1-rc1.jar com.springsource.net.sf.cglib-2.2.0.jar com.springsource.org.aopalliance-1.0.0.jar com.springsource.org.apache.commons.logging-1.1.1.jar com.springsource.org.aspectj.weaver-1.6.12.RELEASE.jar dom4j-1.6.1.jar ehcache-core-2.5.2.jar
ehcache-spring-annotations-1.2.0.jar guava-12.0.jar hibernate-commons-annotations-4.0.1.Final.jar hibernate-core-4.1.7.Final.jar hibernate-entitymanager-4.1.7.Final.jar hibernate-jpa-2.0-api-1.0.1.Final.jar javassist-3.15.0-GA.jar jboss-logging-3.1.0.GA.jar jboss-transaction-api_1.1_spec-1.0.0.Final.jar log4j-1.2.17.jar mysql-connector-java-5.1.6-bin.jar org.springframework.aop-3.1.2.RELEASE.jar org.springframework.asm-3.1.2.RELEASE.jar org.springframework.aspects-3.1.2.RELEASE.jar org.springframework.beans-3.1.2.RELEASE.jar org.springframework.context-3.1.2.RELEASE.jar org.springframework.context.support-3.1.2.RELEASE.jar org.springframework.core-3.1.2.RELEASE.jar org.springframework.expression-3.1.2.RELEASE.jar org.springframework.instrument-3.1.2.RELEASE.jar org.springframework.instrument.tomcat-3.1.2.RELEASE.jar org.springframework.jdbc-3.1.2.RELEASE.jar org.springframework.jms-3.1.2.RELEASE.jar org.springframework.orm-3.1.2.RELEASE.jar org.springframework.oxm-3.1.2.RELEASE.jar org.springframework.test-3.1.2.RELEASE.jar org.springframework.transaction-3.1.2.RELEASE.jar org.springframework.web-3.1.2.RELEASE.jar org.springframework.web.portlet-3.1.2.RELEASE.jar org.springframework.web.servlet-3.1.2.RELEASE.jar slf4j-api-1.6.1.jar slf4j-nop-1.6.1.jar
2)建立一個Web Project,引入上述jar包
2.1)web.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <!-- 配置Log4j --> <context-param> <param-name>webAppRootKey</param-name> <param-value>spring_springmvc_jpa.root</param-value> </context-param> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>classpath:log4j.properties</param-value> </context-param> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <!-- 配置編碼過濾器 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置Spring監聽器 --> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <!-- Spring 刷新Introspector防止內存泄露 --> <listener> <listener-class> org.springframework.web.util.IntrospectorCleanupListener </listener-class> </listener> <!-- SpringMVC核心分發器 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- 映射*.do的請求 --> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
2.2)在src目錄下建立log4j.properties日志配置文件,內容如下:
log4j.rootLogger=INFO,stdout,file
#應用於控制台
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss,SSS} [%c]-[%p] %m%n
#應用於文件
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File=${spring_springmvc_jpa.root}/app.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c] [%p] - %m%n
log4j.logger.org.hibernate.tool.hbm2ddl=debug
2.3)在src目錄下建立緩存的配置文件ehcache.xml,內容如下:
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true"> <!-- <diskStore path="java.io.tmpdir"/> --> <!-- 配置緩存文件存放路徑 --> <diskStore path="E:/CacheTmpDir" /> <!-- 1.必須要有的屬性: name: cache的名字,用來識別不同的cache,必須惟一。 maxElementsInMemory: 內存管理的緩存元素數量最大限值。 maxElementsOnDisk: 硬盤管理的緩存元素數量最大限值。默認值為0,就是沒有限制。 eternal: 設定元素是否持久話。若設為true,則緩存元素不會過期。 overflowToDisk: 設定是否在內存填滿的時候把數據轉到磁盤上。 2.下面是一些可選屬性: timeToIdleSeconds: 設定元素在過期前空閑狀態的時間,只對非持久性緩存對象有效。默認值為0,值為0意味着元素可以閑置至無限長時間。 timeToLiveSeconds: 設定元素從創建到過期的時間。其他與timeToIdleSeconds類似。 diskPersistent: 設定在虛擬機重啟時是否進行磁盤存儲,默認為false.(我的直覺,對於安全小型應用,宜設為true)。 diskExpiryThreadIntervalSeconds: 訪問磁盤線程活動時間。 diskSpoolBufferSizeMB: 存入磁盤時的緩沖區大小,默認30MB,每個緩存都有自己的緩沖區。 memoryStoreEvictionPolicy: 元素逐出緩存規則。共有三種,Recently Used (LRU)最近最少使用,為默認。 First In First Out (FIFO),先進先出。Less Frequently Used(specified as LFU)最少使用 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" /> <!-- User cache --> <cache name="userCache" maxElementsInMemory="10000" maxElementsOnDisk="1000" eternal="false" overflowToDisk="true" diskSpoolBufferSizeMB="20" timeToIdleSeconds="300" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LFU" /> </ehcache>
2.4)在src目錄下建立數據庫連接屬性配置文件jdbc.properties,內容如下:
#mysql數據庫用 jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring_springmvc_jpa?useUnicode=true&characterEncoding=UTF-8 jdbc.username=root jdbc.password=root ##===============BoneCP配置==============## #檢查數據庫連接池中空閑連接的間隔時間,單位是分,默認值:240,如果要取消則設置為0# BoneCP.idleConnectionTestPeriod=60 #連接池中未使用的鏈接最大存活時間,單位是分,默認值:60,如果要永遠存活設置為0# BoneCP.idleMaxAge=60 #每個分區最大的連接數# BoneCP.maxConnectionsPerPartition=5 #每個分區最小的連接數# BoneCP.minConnectionsPerPartition=1 #分區數 ,默認值2,最小1,推薦3-4,視應用而定# BoneCP.partitionCount=3 #每次去拿數據庫連接的時候一次性要拿幾個,默認值:2# BoneCP.acquireIncrement=2 #緩存prepared statements的大小,默認值:0# BoneCP.statementsCacheSize=0 #每個分區釋放鏈接助理進程的數量,默認值:3,除非你的一個數據庫連接的時間內做了很多工作,不然過多的助理進程會影響你的性能# BoneCP.releaseHelperThreads=3
2.5)在WEB-INF目錄下建立Spring的配置文件applicationContext.xml,內容如下:<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd
http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring
http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd">
<!-- 注解支持 --> <context:annotation-config /> <!-- 啟動組件掃描,排除@Controller組件,該組件由SpringMVC配置文件掃描 --> <context:component-scan base-package="cn.luxh.app"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 屬性文件位置 --> <context:property-placeholder location="classpath:jdbc.properties" /> <!-- 數據源 --> <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close"> <!-- 數據庫驅動 --> <property name="driverClass" value="${jdbc.driverClassName}" /> <!-- 相應驅動的jdbcUrl--> <property name="jdbcUrl" value="${jdbc.url}" /> <!-- 數據庫的用戶名 --> <property name="username" value="${jdbc.username}" /> <!-- 數據庫的密碼 --> <property name="password" value="${jdbc.password}" /> <!-- 檢查數據庫連接池中空閑連接的間隔時間,單位是分,默認值:240,如果要取消則設置為0 --> <property name="idleConnectionTestPeriod" value="${BoneCP.idleConnectionTestPeriod}" /> <!-- 連接池中未使用的鏈接最大存活時間,單位是分,默認值:60,如果要永遠存活設置為0 --> <property name="idleMaxAge" value="${BoneCP.idleMaxAge}" /> <!-- 每個分區最大的連接數 --> <property name="maxConnectionsPerPartition" value="${BoneCP.maxConnectionsPerPartition}" /> <!-- 每個分區最小的連接數 --> <property name="minConnectionsPerPartition" value="${BoneCP.minConnectionsPerPartition}" /> <!-- 分區數 ,默認值2,最小1,推薦3-4,視應用而定 --> <property name="partitionCount" value="${BoneCP.partitionCount}" /> <!-- 每次去拿數據庫連接的時候一次性要拿幾個,默認值:2 --> <property name="acquireIncrement" value="${BoneCP.acquireIncrement}" /> <!-- 緩存prepared statements的大小,默認值:0 --> <property name="statementsCacheSize" value="${BoneCP.statementsCacheSize}" /> <!-- 每個分區釋放鏈接助理進程的數量,默認值:3,除非你的一個數據庫連接的時間內做了很多工作,不然過多的助理進程會影響你的性能 --> <property name="releaseHelperThreads" value="${BoneCP.releaseHelperThreads}" /> </bean> <!-- JPA實體管理器工廠 --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="persistenceProvider" ref="persistenceProvider"/> <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/> <property name="jpaDialect" ref="jpaDialect"/> <property name="packagesToScan" value="cn.luxh.app.entity"/> <property name="jpaProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> <prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop> <prop key="hibernate.max_fetch_depth">3</prop> <prop key="hibernate.jdbc.fetch_size">18</prop> <prop key="hibernate.jdbc.batch_size">10</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="javax.persistence.validation.mode">none</prop> </props> </property> </bean> <!-- 用於指定持久化實現廠商類 --> <bean id="persistenceProvider" class="org.hibernate.ejb.HibernatePersistence"/> <!-- 用於設置JPA實現廠商的特定屬性 --> <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="database" value="MYSQL"/> </bean> <!-- 用於指定一些高級特性 --> <bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/> <!-- 事務管理器 --> <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!-- 注解式事務 --> <tx:annotation-driven transaction-manager="txManager"/> <!-- 啟用緩存注解功能 --> <ehcache:annotation-driven cache-manager="ehCacheManager" /> <!-- cacheManager工廠類,指定ehcache.xml的位置 --> <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="classpath:/ehcache.xml"/> </beans>
2.6)在WEB-INF目錄下建立SpringMVC的配置文件dispatcher-servlet.xml,內容如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd"> <mvc:annotation-driven/> <context:component-scan base-package="cn.luxh.app.controller"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
2、編寫代碼
1)在WEB-INF目錄下建立名叫jsp的文件夾,用於放置jsp頁面,和SpringMVC配置文件中的<property name="prefix" value="/WEB-INF/jsp/"/>相對應。
2)創建一個實體。
package cn.luxh.app.entity.privilege; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; /** * 用戶信息 * @author Luxh */ @Entity @Table(name="t_user_info") public class UserInfo implements Serializable{ private static final long serialVersionUID = -3838732995856086555L; @Id @GeneratedValue private Long id; //賬號 @Column(length=20,nullable=false) private String account; //密碼 @Column(length=20,nullable=false) private String password; //姓名 @Column(length=32,nullable=false) private String name; //getter、setter //...... }
3)通用DAO,用於其他DAO繼承,
3.1)接口
package cn.luxh.app.dao.base; /** * 通用的DAO * @author Luxh */ public interface BaseDao { /** * 持久化實體 * @param entity */ void save(Object entity); /** * 根據主鍵查詢實體 * @param <T> * @param clazz 實體類 * @param id 主鍵 * @return */ <T> T getById(Class<T> clazz,Object id); }
3.2)實現
package cn.luxh.app.dao.base; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.springframework.stereotype.Repository; /** * 通用DAO實現 * @author Luxh */ @Repository public class BaseDaoImpl implements BaseDao{ //注入實體管理器 @PersistenceContext protected EntityManager em; public <T> T getById(Class<T> clazz, Object id) { return em.find(clazz, id); } public void save(Object entity) { em.persist(entity); } }
4)UserDao,繼承於通用DAO
4.1)接口
package cn.luxh.app.dao.privilege; import cn.luxh.app.dao.base.BaseDao; /** * User Dao Interface * @author Luxh */ public interface UserDao extends BaseDao{ }
4.2)實現
package cn.luxh.app.dao.privilege; import org.springframework.stereotype.Repository; import cn.luxh.app.dao.base.BaseDaoImpl; /** * User DAO Implement * @author Luxh */ @Repository public class UserDaoImpl extends BaseDaoImpl implements UserDao{ }
5)通用的業務接口
這里只寫一個接口,留給其他Service實現。
package cn.luxh.app.service.base; /** * 通用業務接口 * @author Luxh */ public interface BaseService { /** * 保存實體 * @param entity */ void save(Object entity); /** * 根據主鍵獲取對象 * @param <T> * @param clazz 實體類 * @param id 主鍵 * @return */ <T> T getById(Class<T> clazz,Object id); }
6)用戶業務接口UserService,實現通用業務接口的方法,並加上緩存
6.1)接口
package cn.luxh.app.service.privilege; import cn.luxh.app.service.base.BaseService; /** * User Service * @author Luxh */ public interface UserService extends BaseService { }
6.2)實現
package cn.luxh.app.service.privilege; import org.springframework.beans.factory.annotation.Autowired; import com.googlecode.ehcache.annotations.TriggersRemove; import com.googlecode.ehcache.annotations.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import cn.luxh.app.dao.privilege.UserDao; /** * User Service Implement * @author Luxh */ @Service public class UserServiceImpl implements UserService{ @Autowired private UserDao userDao; @Cacheable(cacheName="userCache")//緩存數據 public <T> T getById(Class<T> clazz, Object id) { return userDao.getById(clazz, id); } @Transactional(propagation=Propagation.REQUIRED) @TriggersRemove(cacheName="userCache",removeAll=true)//清除緩存 public void save(Object entity) { userDao.save(entity); } }
7)控制層,暫時還沒有寫一個公共的Controller用於被繼承,就直接寫UserController吧
package cn.luxh.app.controller.privilege; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import cn.luxh.app.entity.privilege.UserInfo; import cn.luxh.app.service.privilege.UserService; /** * User Controller * @author Luxh */ @Controller @RequestMapping(value="/user") public class UserController { private static final Log LOG = LogFactory.getLog(UserController.class); @Autowired private UserService userService;//注入業務接口 /** * 用戶列表 */ @RequestMapping(value="/userList.do") public String userList(ModelMap modelMap) { LOG.info("訪問用戶列表"); //打印一句話測試,不查數據庫了 return "user/userList"; //根據SpringMVC配置文件配好的前綴和后綴,自動轉為:/WEB-INF/jsp/user/userList.jsp } /** * 根據主鍵查找用戶 */ @RequestMapping(value="/getUserById.do") public String getUserById(@RequestParam long id) { LOG.info("id is :"+id); UserInfo userInfo = userService.getById(UserInfo.class,id); LOG.info("user's name is :"+userInfo.getName()); return "user/userList"; } /** * 保存用戶 */ @RequestMapping(value="/saveUser.do") public String saveUser(UserInfo userInfo,RedirectAttributes redirectAttributes) { LOG.info("保存用戶"); userService.save(userInfo); //重定向后的提示信息,使用RedirectAttributes傳遞,在JSP頁面可以用${message}獲取 //提示信息只出現一次,刷新也不會重復提示, redirectAttributes.addFlashAttribute("message", "操作成功"); //重定向,防止表單重復提交 return "redirect:userList.do";//相對於當前路徑 //return "redirect:/user/userList.do";//相對於當前項目根路徑 } }
訪問用戶列表就是:http://localhost:8080/APP/user/userList.do
根據主鍵查找用戶:http://localhost:8080/APP/user/getUserById.do?id=1
保存用戶:http://localhost:8080/APP/user/saveUser.do
APP是我的應用名稱。