10、SSH整合的特别说明

对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...

修改Employee的hibernate配置文件,就是Employee.hbm.xml文件,创建部门的映射文件Department.hbm.xml

<?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

这就是懒加载的问题,看下面的流程图:

10、SSH整合的特别说明_第1张图片

在值显示雇员名称情况下,流程从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语句。


你可能感兴趣的:(10、SSH整合的特别说明)