SSH整合
一 spring 和 struts 整合 (案例一和案例二部分)
1. 初步整合
导入jar
整合两边,谁都不关联谁。
public class UserAction extends ActionSupport{
public String save(){
System.out.println("来调用了userAction的save方法 .222222..");
/*UserService userService = new UserServiceImpl();
userService.save();*/
//创建工厂
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService =(UserService) context.getBean("userService");
userService.save();
((AbstractApplicationContext) context).close();
return NONE;
}
}
2. 初步整合的问题
1. 每一个action的方法都必须去解析xml , 然后创建工厂,才能得到bean . 如此一来,工厂就会很多, 其实没有必要,只要一个工厂即可。
2. 工厂的创建只有在请求到来的时候才会执行,那么需要消耗一点时间,我们可以让工厂的创建时机提前。 ----> 部署项目的时候 | 启动tomcat (已经托管了该项目)
要想完成以上工作,可以在项目发布的时候,创建工厂。那么必须得知道现在项目到底有没有运行,是不是正在启动。 此处需要监听servletContext的创建,刚好spring提供了这样的监听器。我们只需要在web.xml中添加即可。
3. 终极整合 – Struts和Spring的究极整合
- 在web.xml中声明监听器
让Spring的工厂在一开始的时候就创建
<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>
把action的实例创建工作交给spring来完成 、并且注入action中的属性
一般spring创建实例是单例, 但是action要求是多例。 所以需要添加上scope
<bean id="userAction" class="com.itheima.web.action.UserAction" scope="prototype">
<property name="userService" ref="userService"></property>
</bean>
4. 背后细节
导入struts2-spring-plugin.jar 之后,在里面有一个struts-plugin.xml
这个包是Struts提供的,用于Sturts和Spring整合的主要的包;可以让action的实例由Spring的工厂来创建;注意在Spring的applicationContext.xml进行配置的时候,要在action的配置后面配置多例,因为action是多例的形式创建的struts的前端控制 ,一旦部署项目就会解析上面xml
在web.xml里面新建一个监听器,并配置全局参数:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
在struts-plugin.xml里面有两个关键性的代码
指定了struts的对象工厂,使用的是spring工厂。
<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" />
<!-- Make the Spring object factory the automatic default -->
<constant name="struts.objectFactory" value="spring" />
- 其实这个声明在struts 自己的default.properties里面也有,只不过它把这个工厂给注释掉了,使用自己的工厂而已。
### if specified, the default object factory can be overridden here
### Note: short-hand notation is supported in some cases, such as "spring"
### Alternatively, you can provide a com.opensymphony.xwork2.ObjectFactory subclass name here
# struts.objectFactory = spring //注释了工厂
### specifies the autoWiring logic when using the SpringObjectFactory.
### valid values are: name, type, auto, and constructor (name is the default)
struts.objectFactory.spring.autoWire = name //指定属性自动注入。
整合要点:
让Spring的工厂提前创建
让action由Spring来创建
二、整合Hibernate – 案例三
1. 初步整合
1. 创建持久化类
2. 创建映射文件
3. 创建核心配置文件
4. 在dao里面使用hibernate执行操作
public class UserDaoImpl implements UserDao{
public void save() {
System.out.println("调用了UserDaoImpl的save方法~~");
Configuration configuration = new Configuration();
configuration.configure();
SessionFactory factory = configuration.buildSessionFactory();
Session session = factory.openSession();
Transaction tr = session.beginTransaction();
User user = new User();
user.setName("奥巴马");
user.setAge(10);
session.save(user);
tr.commit();
session.close();
factory.close();
}
}
以上代码存在的问题
1. 每一次调用方法都需要解析xml 创建sessionFactory
2. 需要自己openSession 然后 打开事务。
3. 执行完自己的逻辑之后,还需要提交事务 、 关闭session...
2. Spring 和 Hibernate整合的问题 – 案例四
以上代码存在的问题
1. 每一次调用方法都需要解析xml 创建sessionFactory
2. 需要自己openSession 然后 打开事务。
3. 执行完自己的逻辑之后,还需要提交事务 、 关闭session...
解决方案:
1. 不创建工厂了,让spring创建工厂。
2. 统一让spring来管理hibernate。
3. 带hibernate配置方式
1. src下面还是存在hibernate.cfg.xml
2. 在 spring创建工厂的时候,指定使用hibernate.cfg.xml位置所在
<!-- 如果hibernate 和 spring整合 ,那么这里给定的工厂类是 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- 让spring帮忙创建工厂 -->
<property name="configLocations" value="classpath:hibernate.cfg.xml"></property>
<!-- dao层注入这个工厂,就不用单独new了 -->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</bean>
4. 不带hibernate配置方式(推荐) - 案例五
这个部分是完全没有hibernate.cfg.xml 把它原本的内容全部整合到applicationContext.xml去。
<!-- 创建数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///heima14"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 如果hibernate 和 spring整合 ,那么这里给定的工厂类是 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- 让spring帮忙创建工厂 -->
<!-- 三个部分 -->
<!-- 核心配置 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 可选配置 -->
<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/itheima/bean/User.hbm.xml</value>
</list>
</property>
</bean>
使用jdbc.properties 来配置数据库连接属性
- 在src下面创建文件 jdbc.properties
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql:///user
user=root
password=root
- 在 applicationContext里面引用属性
property-placeholder 这个标签的备注上面也说明了怎么取值。
<!-- 导入jdbc.properties -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 声明数据库连接源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>
三、HibernateTemplate API 介绍
配置事务 - 案例七
HibernateTemplate只有开启事务之后才能够运作
<!-- spring的配置 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- IOC+DI -->
<bean id="actionDemo" class="com.itheima.web.action.ActionDemo" scope="prototype">
<property name="userService" ref="userService"></property>
</bean>
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<!-- <property name="sessionFactory" ref="sessionFactory"></property> -->
<property name="hibernateTemplate" ref="hibernateTemplate"></property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
然后开启事务的开关。一般都是开在service层
- get方法
public User get(Integer id) {
return getHibernateTemplate().get(User.class, id);
}
- load方法
public User load(Integer id) {
return getHibernateTemplate().load(User.class, id);
}
- 使用 HQL 方式查询
public List<User> findByHQL() {
String sql = "from User";
List<User> list = (List<User>) getHibernateTemplate().find(sql);
return list;
}
- 使用QBC方式查询
public List<User> findByQBC() {
return (List<User>) getHibernateTemplate().findByCriteria(DetachedCriteria.forClass(User.class));
}
@Override
public User findById(Serializable id) {
DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
criteria.add(Restrictions.eq("uid", id));
List<User> list = (List<User>) getHibernateTemplate().findByCriteria(criteria);
if(list.size()>0){
User user = list.get(0);
System.out.println(user);
return user;
}
return null;
}
四、HibernateTemplate 懒加载的问题
抛出异常
could not initialize proxy - no Session
@Override
public User get(Serializable id) {
return (User) getHibernateTemplate().get(User.class, (int)id);
}
@Override //懒加载方法
public User load(Serializable id) {
return (User) getHibernateTemplate().load(User.class, (int)id);
}
- 原因
hibernateTemplate在我们的业务逻辑层方法走完之后,就提交事务了。并且立即关闭了session。但是等我们后面使用数据的时候,却没有了session,所以无法执行查询。
- 要在web.xml里面添加一个配置
注意: 要在struts的过滤器之前添加 否则无效
<filter>
<filter-name>Open</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Open</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
五、SSH 最终整合版本
1. struts.xml
<!-- 指定让项目一部署, 就马上创建spring的工厂 -->
<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>
<!-- 懒加载 -->
<filter>
<filter-name>lazy</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>lazy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- struts的配置 -->
<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>
2. applicationContext.xml
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- hibernate的配置 -->
<!-- 这里使用c3p0来作为数据库连接源 这里也可以直接丢出去,用jdbc.properties 来替代-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///user"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- 三个部分 -->
<!-- 核心必须 : 连接什么数据库,怎么连,连接哪一种数据库, 账号密码
这里其实就是对 LocalSessionFactoryBean 进行属性注入
-->
<property name="dataSource" ref="dataSource"></property>
<!-- 可选配置: 方言、 格式化sql \显示 sql \ 自动建表 -->
<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>
<!-- 资源的映射 : User.hbm.xml -->
<property name="mappingResources">
<list>
<value>com/itheima/bean/User.hbm.xml</value>
</list>
</property>
</bean>
<!-- spring自己的配置 -->
<!-- 开启事务 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 业务逻辑配置 -->
<!-- 先配Action 这里需要配置proerty ,创建为多例状态。-->
<bean id="userAction" class="com.itheima.web.action.UserAction" scope="prototype">
<property name="userService" ref="userService"></property>
</bean>
<!-- 配置service -->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<!-- 配置Dao -->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</beans>
3. 额外的配置
- 在业务逻辑类上面,需要使用事务的注解 @Transactional 否则无法执行。