-
背景:
基于之前两篇文章《Spring(三):Spring整合Hibernate》、《Spring(四):Spring整合Hibernate,之后整合Struts2》,了解了如何整合SSH的过程,但还不知道整合后在项目中该怎么开发使用,本文主要讲解的是基于SSH实现Employee信息查询功能的使用。
-
新建Employee,Department实体类,并添加对应类的hibernate实体配置文件
新建包com.dx.ssh.entities,在该包下创建Employee、Department实体类,并添加对应的hibernate实体配置文件Employee.hbm.xml、Department.hbm.xml
Employee.java
1 package com.dx.ssh.entities; 2 3 public class Department { 4 private Integer id; 5 private String deparmentName; 6 7 public Integer getId() { 8 return id; 9 } 10 11 public void setId(Integer id) { 12 this.id = id; 13 } 14 15 public String getDeparmentName() { 16 return deparmentName; 17 } 18 19 public void setDeparmentName(String deparmentName) { 20 this.deparmentName = deparmentName; 21 } 22 }
Employee.hbm.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 4 <!-- Generated 2017-5-9 16:33:32 by Hibernate Tools 3.5.0.Final --> 5 <hibernate-mapping> 6 <class name="com.dx.ssh.entities.Department" table="SSH_DEPARTMENT"> 7 <id name="id" type="java.lang.Integer"> 8 <column name="ID" /> 9 <generator class="native" /> 10 </id> 11 <property name="deparmentName" type="java.lang.String"> 12 <column name="DEPARMENTNAME" /> 13 </property> 14 </class> 15 </hibernate-mapping>
Department.java
1 package com.dx.ssh.entities; 2 3 import java.util.Date; 4 5 public class Employee { 6 private Integer id; 7 private String lastName; 8 private String email; 9 private Date birth; 10 // 不能被修改 11 private Date createTime; 12 // n:1的关系 13 private Department department; 14 15 public Integer getId() { 16 return id; 17 } 18 19 public void setId(Integer id) { 20 this.id = id; 21 } 22 23 public String getLastName() { 24 return lastName; 25 } 26 27 public void setLastName(String lastName) { 28 this.lastName = lastName; 29 } 30 31 public String getEmail() { 32 return email; 33 } 34 35 public void setEmail(String email) { 36 this.email = email; 37 } 38 39 public Date getBirth() { 40 return birth; 41 } 42 43 public void setBirth(Date birth) { 44 this.birth = birth; 45 } 46 47 public Date getCreateTime() { 48 return createTime; 49 } 50 51 public void setCreateTime(Date createTime) { 52 this.createTime = createTime; 53 } 54 55 public Department getDepartment() { 56 return department; 57 } 58 59 public void setDepartment(Department department) { 60 this.department = department; 61 } 62 }
Department.hbm.xml
1 <?xml version="1.0"?> 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 4 <!-- Generated 2017-5-9 16:33:32 by Hibernate Tools 3.5.0.Final --> 5 <hibernate-mapping> 6 <class name="com.dx.ssh.entities.Employee" table="SSH_EMPLOYEE"> 7 <id name="id" type="java.lang.Integer"> 8 <column name="ID" /> 9 <generator class="native" /> 10 </id> 11 <property name="lastName" type="java.lang.String"> 12 <column name="LASTNAME" /> 13 </property> 14 <property name="email" type="java.lang.String"> 15 <column name="EMAIL" /> 16 </property> 17 <property name="birth" type="java.util.Date"> 18 <column name="BIRTH" /> 19 </property> 20 <property name="createTime" type="java.util.Date"> 21 <column name="CREATETIME" /> 22 </property> 23 <!-- 单项N:1关联关系 --> 24 <many-to-one name="department" class="com.dx.ssh.entities.Department"> 25 <column name="DEPARTMENT_ID" /> 26 </many-to-one> 27 </class> 28 </hibernate-mapping>
-
添加Dao、Service、Action,并配置相关配置文件
添加包com.dx.ssh.dao,并新建EmployeeDao.java
package com.dx.ssh.dao; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; import com.dx.ssh.entities.Employee; public class EmployeeDao { private SessionFactory sessionFactory; public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } private Session getSession() { return this.sessionFactory.getCurrentSession(); } public List<Employee> getAll() { String hql = "From Employee e LEFT OUTER JOIN FETCH e.department"; return getSession().createQuery(hql).list(); } }
在com.dx.ssh.service包下,新建EmployeeService.java
package com.dx.ssh.service; import java.util.List; import com.dx.ssh.dao.EmployeeDao; import com.dx.ssh.entities.Employee; public class EmployeeService { private EmployeeDao employeeDao; public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; } public List<Employee> getAll() { List<Employee> items = employeeDao.getAll(); return items; } }
在com.dx.ssh.actions下,新建Action类EmployeeAction.java
package com.dx.ssh.service; import java.util.List; import com.dx.ssh.dao.EmployeeDao; import com.dx.ssh.entities.Employee; public class EmployeeService { private EmployeeDao employeeDao; public void setEmployeeDao(EmployeeDao employeeDao) { this.employeeDao = employeeDao; } public List<Employee> getAll() { List<Employee> items = employeeDao.getAll(); return items; } }
配置applicationContext-beans.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="employeeDao" class="com.dx.ssh.dao.EmployeeDao"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <bean id="employeeService" class="com.dx.ssh.service.EmployeeService"> <property name="employeeDao" ref="employeeDao"></property> </bean> <bean id="employeeAction" class="com.dx.ssh.actions.EmployeeAction" scope="prototype"> <property name="employeeService" ref="employeeService"></property> </bean> </beans>
配置struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="default" namespace="/" extends="struts-default"> <action name="emp-*" class="employeeAction" method="{1}"> <result name="list">/WEB-INF/views/emp-list.jsp</result> </action> </package> </struts>
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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <!-- 加载配置文件 --> <context:property-placeholder location="classpath:jdbc.properties" file-encoding="utf-8" ignore-unresolvable="true" /> <!-- 配置数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClassName}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="testConnectionOnCheckout" value="${jdbc.c3p0.testConnectionOnCheckout}"></property> <property name="testConnectionOnCheckin" value="${jdbc.c3p0.testConnectionOnCheckin}"></property> <property name="idleConnectionTestPeriod" value="${jdbc.c3p0.idleConnectionTestPeriod}"></property> <property name="initialPoolSize" value="${jdbc.c3p0.initialPoolSize}"></property> <property name="minPoolSize" value="${jdbc.c3p0.minPoolSize}"></property> <property name="maxPoolSize" value="${jdbc.c3p0.maxPoolSize}"></property> <property name="maxIdleTime" value="${jdbc.c3p0.maxIdleTime}"></property> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <!-- 数据源dataSource --> <property name="dataSource" ref="dataSource" /> <!-- hibernate的配置方案一 --> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> <!-- hibernate的配置方案二 --> <!-- <property name="hibernateProperties"> <props> <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <prop key="hibernate.format_sql">${hibernate.format_sql}</prop> <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop> </props> </property> --> <!-- 持久化类的位置方案一,通过包进行扫描 --> <property name="mappingLocations" value="classpath:com/dx/ssh/entities/*.hbm.xml"></property> <!-- spring的spring.jar的jar包内,在org.springframework.orm.hibernate3.annotation下, 有一个AnnotationSessionFactoryBean类,其中有一个属性叫做"packagesToScan", 有个方法叫setpackagesToScan(), 也就是说我可以再spring里面将这个属性给设定上。 packagesToScan是"包扫描"的意思,哪些包spring可以给我们扫描一下,看看有哪些实体类, 这一项在我们在配置文件中配置hibernate的实体类的时候可以这么配,只要给出具体的扫描范围就可以了, 不需要将实体类一个一个的写出来 --> <!-- 持久化类的位置方案二,通过包进行扫描 --> <!-- <property name="packagesToScan"> <list> <value>com.dx.ssh.entities</value> </list> </property> --> <!-- 持久化类的位置方案三,通过类进行扫描 --> <!-- <property name="annotatedClasses"> <list> <value>com.dx.ssh.entities.Member</value> <value>com.dx.ssh.entities.Log</value> </list> </property> --> </bean> <!-- 配置Hibernate事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 配置事务异常封装 <bean id="persistenceExceptionTranslationPostProcessor" class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" /> --> <!-- 基于数据源的事务管理器 --> <!-- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> --> <!-- 启用注解来管理事务 --> <!-- 配合<tx:advice>和<aop:advisor>完成了事务切面的定义 --> <!-- 使用强大的切点表达式是语言轻松定义目标方法 --> <aop:config proxy-target-class="true"> <!-- 通过aop定义事务增强切面 --> <aop:pointcut expression=" execution(* com.dx.ssh.service..*(..))" id="serviceMethod" /> <!-- 引用事务增强 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" /> </aop:config> <!-- 事务增强 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 事务属性定义 --> <tx:attributes> <tx:method name="*" /> </tx:attributes> </tx:advice> <!-- 第一种方式: 注解方式配置事物 <tx:annotation-driven transaction-manager="transactionManager" /> --> </beans>
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 把一下配置信息转移到jdbc.properties中。 <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">123456</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost/my_ssh</property> --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> <property name="hibernate.hbm2ddl.auto">update</property> <!-- <property name="hibernate.current_session_context_class">thread</property> --> </session-factory> </hibernate-configuration>
jdbc.properties
#----------------------------------------------------- # u6570u636Eu5E93u914Du7F6E #----------------------------------------------------- #u670Du52A1u5668u5730u5740 host=127.0.0.1 dbName=my_ssh jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://${host}:3306/${dbName} jdbc.username=root jdbc.password=123456 #----------------------------------------------------- # u9002u7528u4E8Ec3p0u7684u914Du7F6E #----------------------------------------------------- #----------------------------------------------------- # c3p0u53CDu7A7Au95F2u8BBEu7F6EuFF0Cu9632u6B628u5C0Fu65F6u5931u6548u95EEu989828800 #----------------------------------------------------- #idleConnectionTestPeriodu8981u5C0Fu4E8EMySQLu7684wait_timeout jdbc.c3p0.testConnectionOnCheckout=false jdbc.c3p0.testConnectionOnCheckin=true jdbc.c3p0.idleConnectionTestPeriod=3600 #----------------------------------------------------- # c3p0u8FDEu63A5u6C60u914Du7F6E #----------------------------------------------------- #initialPoolSize, minPoolSize, maxPoolSize define the number of Connections that will be pooled. #Please ensure that minPoolSize <= maxPoolSize. #Unreasonable values of initialPoolSize will be ignored, and minPoolSize will be used instead. jdbc.c3p0.initialPoolSize=10 jdbc.c3p0.minPoolSize=10 jdbc.c3p0.maxPoolSize=100 #maxIdleTime defines how many seconds a Connection should be permitted to go unused before being culled from the pool. jdbc.c3p0.maxIdleTime=3600 #----------------------------------------------------- # hibernateu8FDEu63A5u6C60u914Du7F6E #----------------------------------------------------- hibernate.connection.driverClass=com.mysql.jdbc.Driver hibernate.connection.url=jdbc:mysql://${host}:3306/${dbName} hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect hibernate.show_sql=true hibernate.format_sql=true hibernate.hbm2ddl.auto=update
在WebContent下创建index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <a href="emp-list">Employee List</a> </body> </html>
在WEB-INF下创建views文件夹,在views下创建emp-list.jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <s:if test="#request.employees==null || #request.employees.size()==0"> 暂无记录! </s:if> <s:else> <table border="1" cellpadding="10" cellspacing="0"> <tr> <td>ID</td> <td>LastName</td> <td>Email</td> <td>BirthDay</td> <td>CreateTime</td> <td>Department Name</td> <tr> <s:iterator value="#request.employees"> <tr> <td>${id }</td> <td>${lastName }</td> <td>${email }</td> <td>${birth }</td> <td>${createTime }</td> <td>${department.deparmentName }</td> <tr> </s:iterator> </table> </s:else> </body> </html>
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>My-SSH</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> <welcome-file>index</welcome-file> </welcome-file-list> <!-- 配置启动IOC容器的Listener --> <!-- needed for ContextLoaderListener --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param> <!-- Bootstraps the root web application context before servlet initialization --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Struts2 filter --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
项目结构如下:
-
测试结果:
-
常见抛出异常情况:
1、nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
错误原因:缺少aspectjweaver-1.8.9.jar包导致的错误。
从地址http://repo1.maven.org/maven2/org/aspectj/aspectjweaver/,下载包aspectjweaver-1.8.9.jar。
2、Action class [userAction] not found - action
在做一个 ssh三个框架集成的时候,经常遇到上诉问题。如果已经正确定义了action,仍报错,其实是缺少包struts2-sping-plugin 2.0.1.jar
解决方案一:
上诉问题说的 在struts.xml 没用找到 sping配置文件里的一个bean啊;也就是说:struts.xml 这个文件没有和sping配置文件关联起来;
在此 另一种解决方案是 在 lib 目录下加入一个jar包struts2-sping-plugin 2.0.1.jar问题就解决了.
解决方案二:
在struts.xml中还要加入这么一个bean
<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring"
class="org.apache.struts2.spring.StrutsSpringObjectFactory" />
这个bean要放在package的外面
3、Struts Problem Report
Struts has detected an unhandled exception:
Messages: |
|
File: | org/hibernate/context/internal/ThreadLocalSessionContext.java |
Line number: | 351 |
解决方案:
包hibernate.cfg.xml中的配置项注意掉:
<!-- <property name="hibernate.current_session_context_class">thread</property> -->
4、hibernate中采用了lazy加载,在查询hql=“From Employee e”,执行过程中dao被service调用,当action调用employeeService结束后,就立即关闭了session,导致页面没有版本加载数据导致的错误。
Struts has detected an unhandled exception:
Messages: |
|
File: | org/hibernate/proxy/AbstractLazyInitializer.java |
Line number: | 146 |
解决该错误有三种解决方案:
解决方案一:修改Employee.hbm.xml man-to-one节点添加上属性 lazy="false",比较笨的方法。因为这是在用效率作为代价。
<!-- 单项N:1关联关系 --> <many-to-one name="department" class="com.dx.ssh.entities.Department" lazy="false"> <column name="DEPARTMENT_ID" /> </many-to-one>
解决方案二:使用OpenSessionInViewFilter。这种方法是将session交给servlet filter来管理,每当一个请求来之后就会开
启一个session,只有当响应结束后才会关闭。如下:
<filter-name>hibernateFilter</filter-name> <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter </filter-class> </filter <filter-mapping> <filter-name>hibernateFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
解决方案三:修改hql
public List<Employee> getAll() { String hql = "From Employee e LEFT OUTER JOIN FETCH e.department"; return getSession().createQuery(hql).list(); }
My-SSH项目源代码下载地址:http://pan.baidu.com/s/1o87Ezge