SSH框架整合(XML方式)
一、搭建环境
新建web工程,准备搭建环境
1. Struts2环境搭建
1.1 导包
1. 导入Struts2的必需jar包(通过模板的jar包导入)
其中log4j的两个jar包不导,因为我们使用的是log4j.properties的配置文件,在Spring导入时使用log4j 1.x的版本.
2. 导入Sturts2与Spring的整合包struts2-spring-plugin-2.3.32.jar
3.导入Struts2的jar包如下,共12个包.(Spring处导入log4j的日志包)
1.2 Sturts2的配置文件
1. 修改web.xml配置Struts2的前端控制器
struts2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
struts2
/*
2. 配置Sturts2的核心配置文件struts2.xml到src(从模板中复制,删除到最简单的配置)
如果要添加配置提示,根据DTD文件添加到Xml Catalog中
1. 关闭开发者模式(常量设置从/org/apache/struts2/default.properties中查找)
缺点:开发者模式不能处理ajax的异常
但我们需要自动加载配置文件的功能,因此要开启自动加载配置文件的开关
2. 修改Sturts2的前端样式为simple
3. 拷贝log4j.properties日志配置文件到src目录
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=d:\\mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=info, stdout,file
4. Sturts2环境搭建完成
2. Spring4环境搭建
2.1 导包
1. Spring必需包(4+2) :
beans,context,core,expression.+ 两个日志jar包:apache-commen logging 1.x.jar与log4j.jar
2. Spring在web中使用的jar包: spring-web-4.2.4.RELEASE.jar
3. Spring操作数据库相关jar包:
spring-jdbc-4.2.4.RELEASE.jar spring-tx-4.2.4.RELEASE.jar(事务包,与jdbc包耦合)
4. 事务管理的jar包:(依赖AOP)
tx jdbc
aop的4个jar包 : aop联盟,Aspectj,Spring整合它们的两个jar包
5. Spring整合ORM框架的jar包
spring-orm-4.2.4.RELEASE.jar
6. Spring测试包:
spring-test-4.2.4.RELEASE.jar
这里共15个jar包.
与Struts2的jar包总共27个jar包。
2.2 Spring的核心监听器
以前获取ApplicationContext(Spring
容器)都是直接获取,每次都要根据配置文件重新创建一个Spring
容器.
在创建Spring
容器同时,需要对容器中对象初始化。而每次初始化容器的时候,都创建了新的容器对象,消耗了资源,降低了性能。
解决思路 : 保证Spring容器只有一个
解决方案:将Spring容器绑定到Web Servlet容器上,让Web容器来管理Spring容器的创建和销毁。
编写一个ServletContextListener监听器,在监听ServletContext到创建的时候,创建Spring容器,
并将其放到ServletContext的属性中保存(setAttribute(Spring容器名字,Spring容器对象) )。
我们无需手动创建该监听器,因为Spring提供了一个叫ContextLoaderListener的监听器,它位于spring-web.jar中。
在web.xml
中配置Spring
的核心监听器,将Spring
容器的创建绑定到ServletContext
上
Spring的核心监听器必须写在Struts2的前端控制前,才能对ServletContext的创建起到监听作用。
org.springframework.web.context.ContextLoaderListener
contextConfigLocation
classpath:applicationContext.xml
2.3 Spring的核心配置文件applicationContext.xml
复制核心配置文件到src目录下:
头约束包括:beans,context,tx,aop
-----------------------------------------------------------------------------
2.4 日志配置文件(Struts2搭建已经完成)
3 Hibernate5环境搭建
3.1 导包
1. 导入Hibernate5必需required文件夹中的jar包(9-1)
其中,javasist的版本与Struts2中的javasist版本冲突了,留下高版本的jar包。
2. 导入数据库驱动 1
mysql-connector-java-5.1.18-bin.jar
3. 导入连接池(数据源)C3P0的jar包 1
这里不用Hibernate提供的c3p0的jar包,Hibernate提供的3个c3p0的jar包全整合只需要一个.
我这里就从Spring提供的c3p0拷贝一个jar包过来。
com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
Hibernate导入jar包10个。
3.2 Hibernate核心配置文件hibernate.cfg.xml
拷贝Hibernate的核心配置文件到src目录下
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/hibernate_day02
root
root
thread
org.hibernate.dialect.MySQL5Dialect
true
true
update
4
org.hibernate.c3p0.internal.C3P0ConnectionProvider
5
20
120
120
120
2
false
到这里,SSH三个框架的环境基本就搭建完成了,下面去完成SSH框架的互相整合。
二、 整合Spring与Hibernate5框架
2.1 首先继续完成Hibernate5的环境搭建
根据需求完成Hibernate(ORM)框架的搭建。
例: 持久化类:Product
. 数据库表:product
. 建立持久化类与数据库表的映射Product.hbm.xml
//持久化类Product
public class Product {
private Integer pid;
private String pname;
private Double price;
public Integer getPid() {
return pid;
}
public void setPid(Integer pid) {
this.pid = pid;
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "Product [pid=" + pid + ", pname=" + pname + ", price=" + price + "]";
}
}
---------------------------------------------------------------------------
根据Product持久化类创建映射关系配置文件Product.hbm.xml:
-----------------------------------------------------------------------------
在Hibernate.cfg.xml中加载该映射文件:
2.2 Spring与Hibernate框架的整合
Spring与Hibernate框架的整合有两种方式:完全整合【推荐】,半整合
我们的原则是能交给Spring托管的都给Spring框架管理,因此这里就不详解半整合
思想:将Hibernate中SessionFactory对象,由Spring管理。
通过 spring-orm包 提供 LocalSessionFactoryBean 实现。
Spring与Hibernate的完全整合:
优点:将hibernate参数配置到spring文件,没有hibernate配置文件 !!!SessionFactory由Spring管理.
根据Hibernate.cfg.xml: 需要三个方面的配置 (数据源配置、 常用属性配置、 hbm映射加载 )
1. 因此在applicationContext.xml中先注册c3p0数据源的bean:
从applicationContext中抽取数据源的变量,便于维护:
db.properties:
jdbc.dataSourceClass=com.mchange.v2.c3p0.ComboPooledDataSource
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=root
2. 将hibernate.cfg.xml的参数完全配置到applicationContext.xml中,由Spring管理SessionFactory.这样Spring容器创建的时候,会创建注入了数据源的SessionFacotory.
**这里注意导入LocalSessionFactoryBean的时候,不要导错版本的全包名,选择Hibernate5**
org.hibernate.dialect.MySQL5Dialect
true
true
update
com/itdream/ssh/domain/Product.hbm.xml
修改hiberante.cfg.xml的名称为hibernate.cfg.bak.xml表示已经不用,或直接删除。
这里将hibernate.cfg.xml的参数全都配置到Spring中了,已经完成了完全整合。
测试是否整合成功:启动服务器,观察指定的数据库ssh中是否自动建表product
2.3 使用HibernateTemplate完成数据库操作
上面Spring与Hibernate的完全整合基本完成(还差事务管理的配置,这里根据实际操作待会设置)。
Spring提供了一个HibernateTemplate模板类大大简化了对数据库的操作.
为了简化HibernateTemplate的操作,Spring又提供了HibernateDaoSupport类
在开发时,只需要让DAO层继承HibernateDaoSupport
,Spring容器创建的时候,会创建注入了数据源的SessionFacotory
。在配置文件中,将SessionFactory
注入到DAO中,会调用父类的setter方法,它的setter方法内部会根据这个SessionFactory
生成一个HibernateTemplate
的对象,子类DAO可以直接获取使用。
ProductDAO:
//dao层,继承HibernateDaoSupport简化操作
public class ProductDAO extends HibernateDaoSupport {
// 添加商品
public void save(Product product) {
getHibernateTemplate().save(product);
}
// 删除商品
public void delete(Product product) {
getHibernateTemplate().delete(product);
}
// 修改商品信息(底层根据id修改)
public void update(Product product) {
getHibernateTemplate().update(product);
}
// 查询单个商品
public Product findByID(Integer pid) {
// 方式一:
return getHibernateTemplate().get(Product.class, pid);
// 方式二:(使用时才发送sql语句)
// return getHibernateTemplate().load(Product.class, pid);
}
// 查询所有商品
public List findAll() {
// 方式一:Spring提供了简单的方式
return getHibernateTemplate().loadAll(Product.class);
// 方式二:使用hql查询
// return getHibernateTemplate().find("from Product");
}
// ------复杂查询-----------
// 查询id大于x的商品(HQL语句查询)
public List findGreaterthanID(Integer pid) {
return (List) getHibernateTemplate().find("form Product where pid > ?", pid);
}
// 命名查询(sql语句与java代码解耦,写在Product.hbm.xml中)
// 模糊查询
public List findByNamedQuery(String name) {
return (List) getHibernateTemplate().findByNamedQuery("Product.findByNameLike", "%" + name + "%");
}
// 模糊查询(Criteria离线查询)
public List findByCriteria(DetachedCriteria criteria) {
return (List) getHibernateTemplate().findByCriteria(criteria);
}
}
---------------------------------------------------------------------------
ProductService:
public class ProductService {
// 注入ProductDAO
private ProductDAO productDAO;
public void setProductDAO(ProductDAO productDAO) {
this.productDAO = productDAO;
}
public void saveProduct(Product product) {
productDAO.save(product);
}
public void deleteProduct(Product product) {
productDAO.delete(product);
}
public void updateProduct(Product product) {
productDAO.update(product);
}
public Product findProductByID(Integer pid) {
return productDAO.findByID(pid);
}
public List findAllProduct() {
return productDAO.findAll();
}
public List findProductsGreaterthanID(Integer pid) {
return productDAO.findGreaterthanID(pid);
}
// Criteria离线查询
public List findProductsByCriteria(DetachedCriteria criteria) {
return productDAO.findByCriteria(criteria);
}
// 命名查询
public List findByNamedQuery(String name) {
return productDAO.findByNamedQuery(name);
}
}
Product.hbm.xml中配置命名查询的语句:
from Product where pname like ?
-------------------------------------------------------------------------
在applicationContext.xml中注册ProductDAO和ProductService:
---------------------------------------------------------------------------
为上面ProductService中的所有方法配置事务管理:(基于AOP的Spring写好的增强类)
----------------------------------------------------------------------------
测试:
@RunWith(SpringJUnit4ClassRunner.class) // Spring整合Junit
@ContextConfiguration(locations ={"classpath:applicationContext.xml"}) // 指定配置文件
public class SpringTest {
// 注入测试bean:ProductService
@Autowired
@Qualifier("productService") // 根据bean的id注入
private ProductService productService;
@Test
public void saveTest() {
// 在实体类中提供无参与有参构造
Product product = new Product(null, "苹果8", 5999D);
Product product2 = new Product(null, "华为8", 6000D);
Product product3 = new Product(null, "小米6", 999D);
Product product4 = new Product(null, "vivo", 1699D);
Product product5 = new Product(null, "大米1", 100D);
productService.saveProduct(product);
productService.saveProduct(product2);
productService.saveProduct(product3);
productService.saveProduct(product4);
productService.saveProduct(product5);
}
@Test
public void updateTest() {
Product product = new Product(1, "大米3", 300D);
productService.updateProduct(product);
}
@Test
public void deleteTest() {
Product product = new Product();
product.setPid(4);
productService.deleteProduct(product);
}
@Test
public void findByIDTest() {
Product product = productService.findProductByID(2);
System.out.println(product);
}
@Test
public void findAllTest() {
List allProducts = productService.findAllProduct();
System.out.println(allProducts);
}
@Test
public void findProductsByCriteriaTest() {
DetachedCriteria criteria = DetachedCriteria.forClass(Product.class);
criteria.add(Restrictions.like("pname", "%米%"));
List productListByCriteria = productService.findProductsByCriteria(criteria);
System.out.println(productListByCriteria);
}
// 命名查询测试
@Test
public void findProductsByNameQueryTest() {
List listByNameQuery = productService.findByNamedQuery("%米%");
System.out.println(listByNameQuery);
}
}
测试成功
上面就完成了Hibernate与Spring整合的所有步骤:
- 导包
- Hibernate将SessionFactory交由Spring管理
- Spring对Service层的方法进行事务管理
applicationContext.xml的完全配置:(以后可以直接复制修改)
org.hibernate.dialect.MySQL5Dialect
true
true
update
com/itdream/ssh/domain/Product.hbm.xml
注意:在导HibernateDaoSupport
包时注意版本
2.3 Spring与Struts2框架的整合
有完全整合与半整合两种方式。两种方式方法类似,完全整合在半整合的基础上使用applicationContext.xml
中不使用真正的Action
的全包名,而是使用伪类名,Struts2拿着这个伪类名去Spring
容器里面查找是否注册有该bean
,如果有直接从Spring
容器中取,否则自己创建。
这是导入了struts2-spring-plugin-2.3.32.jar
包,修改修struts的对象工厂为 spring (StrutsSpringObjectFactory),将默认Struts2
创建Action动作类对象的权利优先交给了Spring管理。
半整合:
由于有plugin的jar包,对象工厂变为spring之后, autoWire(自动绑定)机制被激活,默认按名称自动注入 bean.在struts.xml中配置jsp页面跳转Action动作类,Action动作类中提供需要注入的ProductService,提供它的setter方法,即完成了半整合.
完全整合[推荐]:
因为pluginjar包将对象工厂变为spring,开启了自动绑定机制,它会优先根据struts.xml中配置的class伪类名先去
Spring工厂中看有没有以这个伪类名注册的bean对象,如果有,从Spring工厂中直接取出来使用,如果没有,Struts2就自己
new 一个Action对象.
因此,在半整合的基础上,将Struts2页面访问Action的class全包名,改为在applicationContext.xml中注册的bean
的id名,这样优先查找spring容器时,就能通过Spring创建Action.
============================================================================
表单页面提交参数:
商品名称:
商品价格:
struts.xml配置文件配置Action:
(class内使用伪类名,即applicationContext.xml中Action动作类的注册bean的id)
applicationContext.xml注册Action动作类 :
============================================================================
按照上面这么配置,Sturts2与Spring就完成了完全整合,Action动作类的创建交由给了Spring管理.并且可以使用Spring的AOP,属性注入等高级功能。
注意:Action
是多实例的,而Spring管理的对象是单实例的,因此注册bean
的时候,使用scope
设置其为多实例prototype
小结:
三、解决延迟加载问题
什么是延迟加载问题 ?
多表关系时,Spring的查找机制默认是懒加载的,在查找了员工数据,返回到表现层,Session关闭以后,表现层使用list时,
会发送sql语句查询关联的信息部门,这时候就会出错,因为session已经关闭了。
如何解决延迟加载问题?
方案一: 配置为立即加载 lazy=false (不推荐 )
我们查询的数据不一定都需要关联数据,太浪费资源。
方案二: Service方法返回前, 对延迟数据进行初始化 (缺点多写代码 )
List list = dao.findAll ();
for(Employee e : list ){
Hibernate.initialize(e.getDepartment() );
}
【推荐】方案三: OpenSessionInView 机制 (将Session开启到表现层 最前面 Filter )
Spring框架提供了一个过滤器OpenSessionInViewFilter,让session对象在WEB层就创建,在WEB层销毁。
只需要配置该过滤器即可[需要配置在struts2 Filter前面]
web.xml:
OpenSessionInViewFilter
org.springframework.orm.hibernate5.support.OpenSessionInViewFilter
OpenSessionInViewFilter
/*
注意:需要在struts2的核心过滤器之前进行配置
OpenSessionInViewFilter原理:
在request过程中维持session。延迟session的关闭,直到request结束,再自动关闭session。
缺点:
如果没有被事务管理的方法, OpenSessionInViewFilter 会将这些方法的事务变为 readOnly 的。
但是这个问题可以避免,在application的事务管理中:将事务拦截到的方法,都配置事务的属性。都设置好read-only的事务策略。
四、 各配置文件总结
SSH框架完全整合完毕后,各配置文件的最终形态:(以后使用直接修改)
此次整合用到的jar包37个。
4.1 web.xml配置文件
-
Spring
的核心监听器,将Spring容器与ServletContext
绑定,保证Spring
容器只有一个.- 前提:导入
Spring
的web
的jar包
- 前提:导入
-
OpenSessionView
过滤器,避免Spring与Hibernate
整合后的延迟加载问题,将Session
对象的关闭延迟到表现出。 -
Struts2
的前端控制器,拦截所有请求,进行统一处理(初始化,拦截器...)
web.xml :
day42_ssh2
org.springframework.web.context.ContextLoaderListener
contextConfigLocation
classpath:applicationContext.xml
struts2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
struts2
/*
index.html
index.htm
index.jsp
default.html
default.htm
default.jsp
4.2 struts.xml配置文件
- 开启配置文件自动加载
- 配置
Struts2
简单样式 - 根据页面请求,配置对应的
Action
- 这里的
class
使用伪类名,在applicationContext.xml
中配置对应的Action
动作类的bean
,完成整合
- 这里的
struts.xml :
4.3 applicationContext.xml配置文件
- 配置数据源
- 这里是与
Hibernate
整合,因此配置SessionFactory
,将hibernate.cfg.xml的所有参数配置都配置到applicationContext.xml配置文件的sessionFactory
中- 注入数据源
- 配置Hibernate属性
- 加载Hibernate的映射文件
- 事务管理的配置
- 确定增强类Service(注册)
- 注册Spring提供的事务增强类
相当于注册- 注入事务管理器,属性transaction-manager="transactionManager"
- 配置事务的属性策略read-only等
- 注册事务管理器
- 注入数据源提供连接Session管理事务
- 配置切面aspect:
- 定义切入点
- 配置切入点与通知的关联
- 定义切入点
-
Spring容器bean
的装配注册- 其中注册DAO层,需要注入数据源,使
HibernateDaoSupport
能够生成HibernateTemplate
.
- 其中注册DAO层,需要注入数据源,使
appliactionContext.xml:
org.hibernate.dialect.MySQL5Dialect
true
true
update
com/itdream/ssh/domain/Product.hbm.xml
4.4 log4j.properties与自定义的db.properties
log4j.properties:
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=d:\\mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=info, stdout,file
----------------------------------------------------------------------------
db.properties:
jdbc.dataSourceClass=com.mchange.v2.c3p0.ComboPooledDataSource
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssh
jdbc.username=root
jdbc.password=root
4.5 持久化类对应的映射文件Xxx.hbm.xml
这里面主要为了展示一下,命名查询的hql
语句的定义:
from Product where pname like ?
总结完毕。