对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);
}
}
因为注解默认使用byName方式注入属性值,所以定义的属性名要与spring中的bean的id完全一致,这里注意这一句的修改:
@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>
在spring的配置文件中添加department的映射文件:
<!-- 配置会话工厂() -->
<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;
}
}
}
添加了Hibernate.initialize(Department.class);用于显式初始化,这种方法好像有点问题,当没有下面几行
//测试看部门名称
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>
这两种方法都有一些弊端,看后台,不管我们是否使用department的信息,后台都会发起department的sql语句
解决方法3: spring专门提供了opensessioninview的方法来解决懒加载.(强烈推荐)
只需要在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语句。