回顾:
1. AOP注解方式
编写切面类(包含通知和切入点)
开启自动代理
2. JDBC模板技术
Spring提供模板技术,数据库的操作
以后编写DAO层,都可以继承JdbcDaoSupport类(JDBC模板)
Spring框架可以整合开源连接池
3. Spring事务管理
Spring框架事务管理需要接口和概述
PlatformTransactionManager接口(平台事务管理器接口),不管使用哪种方式管理事务,这个类必须配置的!!
手动编码(了解)
声明式事务管理方式(重点掌握),默认使用AOP的技术来增强
XML的方式
注解的方式
SSH三大框架需要的配置文件
1. Struts2框架
*在web.xml中配置核心的过滤器
<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>
*在src目录下创建struts.xml,用来配置Action
2. Hibernate框架
* 在src目录创建hibernate.cfg.xml配置文件
* 在JavaBean所在的包下映射的配置文件
3. Spring框架
* 在src目录下创建applicationContext.xml
* 在src目录下log4j.proerties
* 在web.xml配置整合WEB的监听器
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
一、Spring框架整合Struts2框架
1. 导入CRM项目的UI页面,找到添加客户的页面,修改form表单,访问Action
编写Action类
public class CustomerAction extends ActionSupport {
private static final long serialVersionUID = -7334305113638651338L;
public String add() {
System.out.println("web层:保存客户...");
return NONE;
}
}
配置struts.xml
<struts>
<package name="crm" namespace="/" extends="struts-default">
<!-- 配置客户的Action -->
<action name="customer_*" class="com.ken.web.CustomerAction"
method="{1}"></action>
</package>
</struts>
action收到了来自前端的请求,接下来就是去调用service层了。
创建service接口和类。
然后,在applicationContext.xml中配置service bean
<!-- 配置客户模块 -->
<bean id="customerService" class="com.ken.service.CustomerServiceImpl">
</bean>
action通过web工厂来调用service
流程:服务器启动,web.xml中的ContextLoaderListener会执行,applicationContext.xml就被加载。applicationContext.xml中的所有的bean配置都会被解析。然后,bean就会被创建,并且默认是单例。然后,通过web工厂从容器中拿。拿到之后,下次使用就不用再次加载配置文件了,只要直接从工厂里面拿。
在Action中获取到service(开发不会使用,因为麻烦)
可以通过 WebApplicationContextUtils.getWebApplicationContext(ServletActionContext.getServletContext()); 来获取,但是这种方式编写代码太麻烦了。
Spring整合Struts2框架的第一种方式(Action由Struts2框架来创建)
在Action中添加:
private CustomerService customerService;
public void setCustomerService(CustomerService customerService) {
this.customerService = customerService;
}
就可以直接用了。因为,struts2-spring-plugin-2.3.24.jar帮我们自动注入了。
如此一来,action中只要提供一个变量的set方法,struts框架就会帮我们从spring工厂中按名称去拿bean。
Action的类是在struts.xml中配置的,那么action的对象就是由struts2框架来创建的,并且是多例的,有一次请求就创建一个action对象。
Spring整合Struts2框架的第二种方式(Action由Spring框架来创建)
(推荐大家来使用的)
上面那种方法中,action对象是由struts2框架来创建的。而我们的spring的核心ioc就是负责对象的创建以及依赖的管理。那么,action也可以由spring来创建嘛。然后,我们就可以直接注入service了。这样的好处是,action,service,dao,所有对象都由spring来管理。
做法是:
首先,在applicationContext.xml中配置这个action。
那么问题来了,我们在struts.xml中也配置了这个action,也会创建这个action的对象
怎么办呢?
要注意,action是多例的,要在applicationContext.xml中配置。
action交给了spring来管理,那么自动注入就失效了。我们要手动注入service。
二、Spring框架整合Hibernate框架
1. 编写CustomerDaoImpl的代码,加入配置并且在CustomerServiceImpl中完成注入
接下来就是考虑,dao层如何操作数据库了。
我们使用HibernateTemplate,它需要一个session。最终的操作数据库,就是使用session.get(), session.save()等方法。
我们可以在dao层的方法中new一个HibernateTemplate对象,来使用。如下图:
但是,我们用spring来做整合。那么,我们把模板对象也交给spring来管理。
我们要使用模板类的话,还要在它这里注入一些东西。
我们的dao实现类可以继承HibernateDaoSupport,它帮我们提供了hibernateTemplate成员变量和set方法。
2.1 带有hibernate.cfg.xml的配置文件
2. 编写映射的配置文件,并且在hibernate.cfg.xml的配置文件中引入映射的配置文件
3. 在applicationContext.xml的配置文件,配置加载hibernate.cfg.xml的配置
<!-- 编写bean,名称都是固定,加载hibernate.cfg.xml的配置文件 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
</bean>
LocalSessionFactoryBean用来加载hibernate的核心配置文件,并且,顺便帮我们构建sessionFactory对象。所以,我们一般把它的id值设为sessionFactory。
流程:当我们启动服务器,刚刚配置的这个对象会被创建。它一创建,就会加载hibernate的核心配置文件。然后,根据配置文件的信息,去生成一个SessionFactory对象。SessionFactory对象可以创建session。 SessionFactory是重量级的,不要轻易创建和销毁。那么,我们这个配置就正好,我们启动服务器,创建SessionFactory对象,这个对象就在内存中了。只要服务器不关,它就一直活着。
我们把SessionFactory对象注入到dao的实现类中。dao的实现类中,只需要使用hibernateTemplate即可。因为hibernateTemplate对session进行了封装。底层,还是session在做。
4. 在CustomerDaoImpl中想完成数据的添加,Spring框架提供了一个HibernateDaoSupport的工具类,以后DAO都可以继承该类
5. 开启事务的配置
先配置事务管理器,注意现在使用的是Hibernate框架,所以需要使用Hibernate框架的事务管理器。事务是又session来管理的。SessionFactory可以生产session。所以,我们把SessionFactory注入给事务管理器。
<!-- 先配置平台事务管理器 -->
<bean id=""
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
开启注解事务
<tx:annotation-driven transaction-manager="transactionManager" />
在Service类中添加事务注解
@Transactional
ssh整合带hibernate配置文件代码
2.2 不带有hibernate.cfg.xml的配置文件
不带有hibernate.cfg.xml配置文件,那么这个配置文件里的内容就要转移到applicationContext.xml文件中。
hibernate核心配置文件里面有:数据库的4大参数、c3p0连接池。我们的spring可以整合c3p0连接池。那么,这两个参数就完事了。还有,数据库方言、可选配置(show_sql, format_sql, hbm2ddl.auto)、映射文件。
1.先配置连接池相关的信息
<!-- 先配置c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql:///ssh_inte1" />
<property name="user" value="root" />
<property name="password" value="123456" />
</bean>
我们的hibernate核心配置文件的作用就是为了创建SessionFactory。我们在spring里面也可以创建
SessionFactory,通过LocalSessionFactoryBean。
2.修改 LocalSessionFactoryBean 的属性配置,因为已经没有了hibernate.cfg.xml的配置文件,所以需要修改该配置,注入连接池
3.在 LocalSessionFactoryBean 中配置,使用hibernateProperties属性继续来配置其他的属性,注意值是properties属性文件
<!-- 编写bean,名称都是固定,加载hibernate.cfg.xml的配置文件 -->
<!-- 之前是加载hibernate.cfg.xml。现在,我们取消hibernate核心配置文件,如下方这样配置 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- 先加载连接池 -->
<property name="dataSource" ref="dataSource" />
<!-- 加载方言,加载可选项 -->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<!-- 引入映射的配置文件 -->
<property name="mappingResources">
<list>
<value>com/ken/domain/Customer.hbm.xml</value>
</list>
</property>
</bean>
ssh整合,不带hibernate.cfg.xml
目前的代码没有绑定到当前线程的代码。如果有,就报错了。因为,我们现在用LocalSessionFactoryBean,它由spring提供的。它来帮我们生产session,会负责管理session,而且生成的是session的一个代理对象。所以,现在session不是由hibernate来维护了。现在的session是由spring来创建的。所以,不能配置绑定到当前线程的操作。
三、Hibernate的模板的常用的方法
1. 增删改的操作
- 添加:save(Object obj);
- 修改:update(Object obj);
- 删除:delete(Object obj);
查询一条记录:
- Object get(Class c,Serializable id);
- Object load(Class c,Serializable id);
4.QBC方式查询所有
findByCriteria(DetachedCriteria criteria, ...)
离线条件查询。我们可以提前把条件准备好,然后,当做参数传进来。
看上面的,参数列表里面有 int firstRresult, int maxResults, 很适合做分页查询。
/**
* QBC, 查询所有
*/
@Override
public List<Customer> findAllByQBC() {
DetachedCriteria criteria = DetachedCriteria.forClass(Customer.class);
// 设置条件查询
List<Customer> list = (List<Customer>) this.getHibernateTemplate().findByCriteria(criteria);
return list;
}
离线条件,为什么称为离线?因为这个对象不是由session来创建的,是我们自己new的,所以,它放在任何一层都可以。
四、延迟加载问题
在action中创建loadById方法,并在service和dao层分别创建,dao层的代码如下:
访问这个接口:
这是包冲突了:
包删除后,再访问,有另一个异常:
load方法是延迟加载。延迟加载,当你用到它属性的时候才会去发送sql语句。
原因在于,system.out.println(c)的时候,才发sql语句。然而,这个时候session已经销毁了,session销毁了,就没法发sql。
web层调用service层。请求到了service层之后,service就帮我们创建session,管理事务。service接着调用dao层。在dao层,load方法,延迟加载,并没有去数据库面查数据,返回给service层的对象(代理对象)里面只有一个id。service把对象返回给web层,并且关闭了session。web层拿到对象(代理对象)之后,进行打印操作,这时用到了客户的属性,就发sql,可是session已经关闭了。就报这个错:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
解决方案1:不使用延迟加载,那么第一次查询的时候就把所有的属性查出来了。
加了lazy="false"就不延迟加载了。我们延迟加载是为了提高程序的性能。那我们怎么样做更好呢?
解决方案2:Spring框架提供了一个过滤器,让session对象在WEB层就创建,在WEB层销毁。只需要配置该过滤器即可。但是,要注意需要在struts2的核心过滤器之前进行配置
<!-- 解决延迟加载的问题 -->
<filter>
<filter-name>OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
看名字:open session in view,在视图层打开session。也就是在web层开启session。