对SSH整合的特别说明
1、spring可以使用注解的方式来配置属性
1.1)重新这样配置bean
<!-- 配置 employeeService对象通过注解的方式来注入属性值,这是一个知识点-->
<bean id="employeeService" class="com.cdtax.service.impl.EmployeeService" />
1.2)在EmployeeService的属性sessionFactory中添加一个注解@Resource
package com.cdtax.service.impl; import java.io.Serializable; import java.util.List; import javax.annotation.Resource; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.springframework.transaction.annotation.Transactional; import com.cdtax.domain.Employee; import com.cdtax.service.interfaces.EmployeeServiceInter; //这里配置@Tansactional用处是让spring的事务管理器接管该service的事务 //如果只想让事务管理器管理某个方法中的事务,那么就将注解加到方法上 @Transactional public class EmployeeService implements EmployeeServiceInter { //当我们给某个属性增加了@Resource后,spring就会启动byName的方式注入我们的sessionFactory @Resource private SessionFactory sessionFactory; public void setSessionFactory(SessionFactory sessionFactory) { System.out.println("注解方式注入,调用setSessionFactory方法"); this.sessionFactory = sessionFactory; } public void addEmployee(Employee e) { // Session s = sessionFactory.openSession(); // Transaction tx = s.beginTransaction(); // s.save(e); // tx.commit(); sessionFactory.getCurrentSession().save(e); } public List<Employee> showEmployee() { // TODO Auto-generated method stub return null; } public void updateEmployee(Employee e) { // TODO Auto-generated method stub } public void deleteEmployee(Serializable id) { // TODO Auto-generated method stub } public Employee getEmployeeById(Serializable id) { // TODO Auto-generated method stub return null; } public Employee checkEmployee(Employee e) { String hql="from Employee where id=? and pwd=?"; List<Employee> list = sessionFactory.getCurrentSession().createQuery(hql) .setString(0, e.getId()+"").setString(1, e.getPwd()).list(); if(list.size() == 1) { return list.get(0); } else { return null; } } }
1.3)在applicationContext.xml中启用注解
<!-- 启用注解扫描机制 -->
<context:annotation-config />
1.4)测试
1.5)action使用注解
配置文件:
<?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: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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <!-- 启用注解扫描机制 --> <context:annotation-config /> <!-- 配置一个testService对象,测试spring集成是否成功用 --> <bean id="testService" class="com.cdtax.test.TestService"> <property name="name" value="小明"></property> </bean> <!-- 配置数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/hibernate" /> <property name="username" value="root" /> <property name="password" value="root" /> <!-- 连接池启动时的初始值 --> <property name="initialSize" value="3" /> <!-- 连接池的最大值 --> <property name="maxActive" value="500" /> <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 --> <property name="maxIdle" value="2" /> <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 --> <property name="minIdle" value="1" /> </bean> <!-- 配置会话工厂() --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <!-- 设置数据源 --> <property name="dataSource" ref="dataSource" /><!-- 应该想到类中有setDataSource()方法 --> <!-- 接管了haibernate的对象映射文件 --> <property name="mappingResources"><!-- 应该想到类中有setMappingResources()方法 --> <list> <value>com/cdtax/domain/Employee.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.hbm2ddl.auto=update hibernate.show_sql=true hibernate.format_sql=true <!-- 配置hibernate二级缓存 --> hibernate.cache.use_second_level_cache=true hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider hibernate.generate_statistics=true </value> </property> </bean> <!-- 配置EmployeeService对象 --> <!-- <bean id="employeeService" class="com.cdtax.service.impl.EmployeeService"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> --> <!-- 配置 employeeService对象通过注解的方式来注入属性值,这是一个知识点--> <bean id="employeeService" class="com.cdtax.service.impl.EmployeeService" /> <!-- 配置struts的组件:Action --> <!-- <bean name="/login" scope="prototype" class="com.cdtax.web.action.LoginAction"> <property name="employeeServiceInter" ref="employeeService" /> </bean> --> <!-- 配置struts的组件:Action 使用注解方式 --> <bean name="/login" scope="prototype" class="com.cdtax.web.action.LoginAction" /> <!-- 配置事务管理器,统一管理sessionFactory的事务 --> <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 启用事务注解 --> <tx:annotation-driven transaction-manager="txManager"/> </beans>LoginAction进行修改增加@Resource
package com.cdtax.web.action; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.actions.DispatchAction; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import com.cdtax.domain.Employee; import com.cdtax.service.interfaces.EmployeeServiceInter; import com.cdtax.web.forms.EmployeeForm; public class LoginAction extends DispatchAction { // ApplicationContext ac = new ClassPathXmlApplicationContext(""); //为了使用spring属性注入,定义变量及set方法 @Resource private EmployeeServiceInter employeeService; //测试单例与否的变量 private int a = 0; public void setEmployeeServiceInter(EmployeeServiceInter employeeService) { System.out.println("setEmployeeServiceInter 方法被调用"); this.employeeService = employeeService; } //响应登陆请求 public ActionForward login(ActionMapping arg0, ActionForm arg1, HttpServletRequest arg2, HttpServletResponse arg3) throws Exception { System.out.println("------通过新的方式响应请求:spring管理-----"); //如果每次请求打印a都相同,证明不是单例,如果每次都是递增,说明是单例 System.out.println("a = " + (++a)); //通过下面语句,可以直接获取到spring容器实例,即我们前面讲的ApplicationContext //当通过spring来进行action的管理时,就不需要下面这行代码了,注释掉 // WebApplicationContext ctx = // WebApplicationContextUtils // .getWebApplicationContext(this.getServlet().getServletContext()); //从spring容器中获取bean //当使用spring来进行action的管理时,这个实例可以通过spring的依赖注入来注入进来,定义一个变量employeeServiceInter,生成set方法 //然后在spring配置文件中的bean中进行属性配置,如下: // <bean name="/login" class="com.cdtax.web.action.LoginAction"> // <property name="employeeServiceInter" ref="employeeService" /> // </bean> // EmployeeServiceInter employeeServiceInter = (EmployeeServiceInter) ctx.getBean("employeeService"); //取出表单,我们先打通练习,我们简单验证 EmployeeForm employeeForm = (EmployeeForm)arg1; //构建一个Employee对象 Employee e = new Employee(); e.setId(Integer.parseInt(employeeForm.getId())); e.setPwd(employeeForm.getPwd()); e = employeeService.checkEmployee(e); if(e != null) { //把雇员信息放入session,后面可以使用 arg2.getSession().setAttribute("loginer", e); return arg0.findForward("ok"); } else { return arg0.findForward("err"); } } //响应注销请求 public ActionForward logout(ActionMapping arg0, ActionForm arg1, HttpServletRequest arg2, HttpServletResponse arg3) throws Exception { // TODO Auto-generated method stub return super.execute(arg0, arg1, arg2, arg3); } }
@Resource
private EmployeeServiceInter employeeService;
名字与配置文件中的一致。
2、ssh整合的时候,如何解决懒加载的问题
问题?如果我们的雇员都属于一个部门
修改雇员类,创建部门类
package com.cdtax.domain; import java.util.Date; public class Employee { private Integer id; private String name; private String email; private String pwd; private Integer grade; private java.util.Date hiredate; private Float salary; //employee -> department private Department departmnet; public Department getDepartmnet() { return departmnet; } public void setDepartmnet(Department departmnet) { this.departmnet = departmnet; } public Employee() { } public Employee(String name, String email, String pwd, Integer grade, Date hiredate, Float salary) { this.name = name; this.email = email; this.pwd = pwd; this.grade = grade; this.hiredate = hiredate; this.salary = salary; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public java.util.Date getHiredate() { return hiredate; } public void setHiredate(java.util.Date hiredate) { this.hiredate = hiredate; } public Float getSalary() { return salary; } public void setSalary(Float salary) { this.salary = salary; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public Integer getGrade() { return grade; } public void setGrade(Integer grade) { this.grade = grade; } }
package com.cdtax.domain; import java.util.Set; public class Department { private Integer id; private String name; private Set<Employee> emps; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Employee> getEmps() { return emps; } public void setEmps(Set<Employee> emps) { this.emps = emps; } }
修改Department类,增加无参的构造方法,否则会出现错误:
org.hibernate.HibernateException: Javassist Enhancement failed:com.cdtax.domain.Department...
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.cdtax.domain"> <class name="Employee" table="employee"> <cache usage="read-write"/> <!-- 主键策略 --> <id name="id" type="java.lang.Integer"> <generator class="native" /> <!-- 主要有hilo sequence navtive increment uuid identity foreign --> </id> <!-- 属性与列的映射写法之一 --> <property name="name" type="java.lang.String" column="name" length="64" /> <!-- 属性与列的映射写法之一 --> <property name="email" type="java.lang.String"> <column name="email" length="64"></column> </property> <property name="hiredate" type="java.util.Date"> <column name="hiredate" /> </property> <property name="salary" type="java.lang.Float"> <column name="salary" /> </property> <property name="pwd" type="java.lang.String"> <column name="pwd" length="32" /> </property> <property name="grade" type="java.lang.Integer"> <column name="grade" length="4" /> </property> <!-- 一个雇员属于一个部门 --> <many-to-one name="department" column="department_id"></many-to-one> </class> </hibernate-mapping>
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.cdtax.domain"> <class name="Department" table="department" > <cache usage="read-write"/> <!-- 主键策略 --> <id name="id" type="java.lang.Integer"> <generator class="native"></generator> </id> <property name="name" type="java.lang.String"> <column name="name" length="32" /> </property> <!-- 一个部门可以有多个雇员 --> <set name="emps"> <key column="department_id"></key> <one-to-many class="com.cdtax.domain.Employee"/> </set> </class> </hibernate-mapping>
然后修改页面mainframe.jsp,也就是校验成功后的转向页面,因为我们的Employee中保存了Department,就让其显示部门名称:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'mainFrame.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <h2>欢迎 ${loginer.name} 所在部门: ${loginer.department.name}请选择你要的操作</h2> <a href="#">添加雇员</a> <a href="#">删除雇员</a> <a href="#">修改雇员</a> <a href="#">退出系统</a> </body> </html>
<!-- 配置会话工厂() --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <!-- 设置数据源 --> <property name="dataSource" ref="dataSource" /><!-- 应该想到类中有setDataSource()方法 --> <!-- 接管了haibernate的对象映射文件 --> <property name="mappingResources"><!-- 应该想到类中有setMappingResources()方法 --> <list> <value>com/cdtax/domain/Employee.hbm.xml</value> <value>com/cdtax/domain/Department.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.hbm2ddl.auto=update hibernate.show_sql=true hibernate.format_sql=true <!-- 配置hibernate二级缓存 --> hibernate.cache.use_second_level_cache=true hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider hibernate.generate_statistics=true </value> </property> </bean>部署重新运行,结果出现错误:
页面显示:HTTP Status 500 - An exception occurred processing JSP page /WEB-INF/mainFrame.jsp at line 28
后台显示:
------通过新的方式响应请求:spring管理-----
a = 1
Hibernate:
select
employee0_.id as id0_,
employee0_.name as name0_,
employee0_.email as email0_,
employee0_.hiredate as hiredate0_,
employee0_.salary as salary0_,
employee0_.pwd as pwd0_,
employee0_.grade as grade0_,
employee0_.department_id as department8_0_
from
employee employee0_
where
employee0_.id=?
and employee0_.pwd=?
2014-2-25 8:43:59 org.apache.catalina.core.ApplicationDispatcher invoke
严重: Servlet.service() for servlet jsp threw exception
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
。。。
提示错误no session
这就是懒加载的问题,看下面的流程图:
在值显示雇员名称情况下,流程从action沿虚线走,开始session,然后是数据库操作(查询或保存记录),完成后结果返回action,此时session正常结束,因为懒加载的原因,只会查询雇员信息,关联的部门没有查询,但是因为页面也不需要部门信息,所以没有问题。而在mainframe.jsp增加了显示部门名称的情况下,当程序执行到这一句时${loginer.department.name},又会去执行数据库查询,而此时session已经关闭。
解决方法1:在加载employee时显示初始化懒加载:
在session还没有关闭时,访问一次 xxx.getXxx(),强制访问数据库。或者Hibernate.initialize(xxx)
package com.cdtax.service.impl; import java.io.Serializable; import java.util.List; import javax.annotation.Resource; import org.hibernate.Hibernate; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.springframework.transaction.annotation.Transactional; import com.cdtax.domain.Department; import com.cdtax.domain.Employee; import com.cdtax.service.interfaces.EmployeeServiceInter; //这里配置@Tansactional用处是让spring的事务管理器接管该service的事务 //如果只想让事务管理器管理某个方法中的事务,那么就将注解加到方法上 @Transactional public class EmployeeService implements EmployeeServiceInter { //当我们给某个属性增加了@Resource后,spring就会启动byName的方式注入我们的sessionFactory @Resource private SessionFactory sessionFactory; public void setSessionFactory(SessionFactory sessionFactory) { System.out.println("注解方式注入,调用setSessionFactory方法"); this.sessionFactory = sessionFactory; } public void addEmployee(Employee e) { // Session s = sessionFactory.openSession(); // Transaction tx = s.beginTransaction(); // s.save(e); // tx.commit(); sessionFactory.getCurrentSession().save(e); } public List<Employee> showEmployee() { // TODO Auto-generated method stub return null; } public void updateEmployee(Employee e) { // TODO Auto-generated method stub } public void deleteEmployee(Serializable id) { // TODO Auto-generated method stub } public Employee getEmployeeById(Serializable id) { // TODO Auto-generated method stub return null; } public Employee checkEmployee(Employee e) { String hql="from Employee where id=? and pwd=?"; List<Employee> list = sessionFactory.getCurrentSession().createQuery(hql) .setString(0, e.getId()+"").setString(1, e.getPwd()).list(); //显示初始化懒加载 Hibernate.initialize(Department.class);//select预先查询 if(list.size() == 1) { //测试看部门名称 // Employee e2 = list.get(0); // System.out.println(e2.getDepartment().getName()); return list.get(0); } else { return null; } } }
//测试看部门名称
Employee e2 = list.get(0);
System.out.println(e2.getDepartment().getName());
时,会出错,也就是说单独一句Hibernate.initialize(Department.class);不行,还的显式调用一下,不知道什么原因。
解决方案2:在Department的配置文件中进行配置,修改Department.hbm.xml文件,增加lazy="false"项
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.cdtax.domain"> <class name="Department" table="department" lazy="false" > <cache usage="read-write"/> <!-- 主键策略 --> <id name="id" type="java.lang.Integer"> <generator class="native"></generator> </id> <property name="name" type="java.lang.String"> <column name="name" length="32" /> </property> <!-- 一个部门可以有多个雇员 --> <set name="emps"> <key column="department_id"></key> <one-to-many class="com.cdtax.domain.Employee"/> </set> </class> </hibernate-mapping>
只需要在web.xml文件中添加如下配置:
<!-- 配置opensessioninview解决懒加载问题,本质是一个过滤器 --> <filter> <filter-name>OpenSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>该方法可以有效的减少对数据库的查询,缺点是和数据保持的session,时间延长了.
这时的流程图
增加了一个过滤器,由过滤器来进行session的管理,同时判断是否需要发出sql语句。
这时测试如果页面中用到了department,后台就发两条sql语句,如果没有用到,只发一条sql语句。