ssh(Spring+Struts2+hibernate)整合

需求:整合ssh框架作一个保存用户的业务,业务比较简单,重在ssh框架整合。
建立数据库和表java

CREATE DATABASE ssh01;
USE DATABASE;
表由Hibernate建立,能够看配置是否成功

一:导入jar包mysql

  1. Hibernate须要jarweb

    Hibernate基本jar
       mysql驱动  
       c3p0链接池
       日志包
       jpa
  2. Struts须要jar
    Struts2基本jar
  3. Spring须要jarspring

    Ioc(6个):beans,context,expression,core+两个日志
       Aop(4个):
           spring-aop-4.2.4.RELEASE
           spring整合aspect
           aspectj:com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
           aop联盟:com.springsource.org.aopalliance-1.0.0.jar
       spring声明式事务:
           spring-jdbc-4.2.4.RELEASE.jar
           spring-tx-4.2.4.RELEASE.jar
  4. Spring整合websql

    spring-web-4.2.4.RELEASE.jar
  5. Spring整合Hibernate数据库

    spring-orm-4.2.4.RELEASE.jar
  6. Spring整合Struts2express

    struts2-spring-plugin-2.3.24.jar
       **注意**
           Spring整合Struts2包先不要导入,由于若是导入在项目启动的时候,
           会在ServetContext中查找spring工厂,会报错,抛出下面异常
    You might need to add the following to web.xml: 
                <listener>
                    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
                </listener>
            17:46:11,396 ERROR Dispatcher:42 - Dispatcher initialization failed
            java.lang.NullPointerException

    在配置ContextLoaderListener监听器在项目启动的时候建立spring容器的时候导入apache

    最后:去除重复的jar struts2基本Jar和Hibernate基本Jar中都有
    javassist-3.18.1-GA.jar,删除一个低版本的,不然在使用的时候会出现问题。浏览器

二:须要的配置文件安全

  1. Hibernate须要的配置文件

    Hibernate.cfg.xml:src路径下
    <hibernate-configuration>
        <session-factory>
            <!-- 必要的配置信息:链接数据库的基本参数 -->
            <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
            <property name="hibernate.connection.url">jdbc:mysql:///ssh01</property>
            <property name="hibernate.connection.username">root</property>
            <property name="hibernate.connection.password">root</property>
            
            <!-- Hibernate的属性 -->
            <!-- Hibernate的方言:做用,根据配置的方言生成相应的SQL语句 -->
            <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
            
            <!-- Hibernate显示SQL语句: -->
            <property name="hibernate.show_sql">true</property>
            <!-- Hibernate格式化SQL语句: -->
            <property name="hibernate.format_sql">true</property>
            <!-- Hibernate的hbm2ddl(数据定义语言:create drop alter ...)属性 -->
            <property name="hibernate.hbm2ddl.auto">update</property>
            
            <!-- 配置C3P0链接池 -->
            <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
            <!-- Hibernate加载映射 -->
            <mapping resource="com/itheima/domain/Customer.hbm.xml"/>
            
        </session-factory>
    </hibernate-configuration>

    jdbc.properties:(对数据库参数的封装) src下

    jdbc.driver=com.mysql.jdbc.Driver;
    jdbc.url=jdbc:mysql://localhost:3306/ssh01
    jdbc.username=root
    jdbc.password=root

    log4J.properties日志文件 src下

    Customer.hbm.xml 须要保存客户实体的映射文件

    <hibernate-mapping package="com.itheima.domain">
        <class name="Customer" table="cst_customer">
            <!-- 数据库表中的字段名和实体类属性名相同的时候能够省略Column属性 -->
            <id name="cust_id">
                <!-- 配置主键生成策略 -->
                <generator class="native"></generator>
            </id>
            <property name="cust_name"></property>
            <property name="cust_source"></property>
            <property name="cust_industry"></property>
            <property name="cust_level"></property>
            <property name="cust_phone"></property>
            <property name="cust_mobile"></property>
        </class>
    </hibernate-mapping>
  2. Struts须要的配置文件

    web.xml:配置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>
    struts2.xml:配置action 位置src下
    <struts>
        <constant name="struts.devMode" value="true"></constant>
        <package name="ssh" extends="struts-default" namespace="/">
            <action name="customer_*" class="com.itheima.web.action.CustomerAction" method="{1}">
                <result name="success">/success.jsp</result>
            </action>
        </package>
    </struts>
  3. applicationContext.xml src下(待会配置)

三:建立service,dao,domain,action

  1. 建立Customer实体类,以及实体类对应的映射文件映射文件看上面)

    伪代码(为属性提供get/set方法)
    public class Customer implements Serializable{
        private static final long serialVersionUID = 1L;
        private Long cust_id;
        private String cust_name;
        private String cust_source;
        private String cust_industry;
        private String cust_level;
        private String cust_phone;
        private String cust_mobile;
  2. CustomerService,CustomerServiceImpl

    将customerService对象交给spring容器管理
        service须要调用dao层的方法,进行属性注入
    public class CustomerServiceImpl implements CustomerService {
        //建立dao,并提供set方法,进行属性注入
        private CustomerDao customerDao;
        public void setCustomerDao(CustomerDao customerDao) {
            this.customerDao = customerDao;
        }
        @Override
        public void save(Customer customer) {
            customerDao.save(customer);
        }
    }
  3. 建立CustomerDao,CustomerDaoImpl
    将CustomerDao对象交给spring容器管

    public class CustomerDaoImpl implements CustomerDao {
        @Override
        public void save(Customer customer) {
            //获取session对象,来操做数据库
            Configuration config = new Configuration().configure();
            SessionFactory factory = config.buildSessionFactory();
            Session session = factory.openSession();
            Transaction tx = session.beginTransaction();
            session.save(customer);
            tx.commit();
            session.close();
        }
    }
  4. 建立CustomerAction并在struts.xml中进行配置(struts.xml文件)

    public class CustomerAction extends ActionSupport implements ModelDriven<Customer> {
        private static final long serialVersionUID = 1L;
        //使用ModelDriven模型驱动进行数据封装,必须手动来建立对象
        private Customer customer = new Customer();
        @Override
        public Customer getModel() {
            return customer;
        }
        /*
         * 建立CustomerService对象调用service层的方法
         * 由于action是由struts2建立service交给spring容器管理因此不能够直接注入
         */
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 
        CustomerService customerService = (CustomerService) context.getBean("customerService");
        //保存用户的方法
        public String save(){
            customerService.save(customer);
            return SUCCESS;
        }
    }
  5. 在spring容器中管理CustomerService,CustomerDao

    <!-- 配置dao -->
        <bean id="customerDao" class="com.itheima.dao.impl.CustomerDaoImpl"></bean>
        <!-- 配置service并注入customerDao属性 -->
        <bean id="customerService" class="com.itheima.service.impl.CustomerServiceImpl">
            <property name="customerDao" ref="customerDao"></property>
        </bean>
  6. 建立save.jsp

    <form action="${pageContext.request.contextPath}/customer_save.action" method="post">
            客户名称:<input type="text" name="cust_name"><br/>
            客户等级:<input type="text" name="cust_level"><br/>
            客户来源:<input type="text" name="cust_source"><br/>
            客户行业:<input type="text" name="cust_industry"><br/>
            客户电话:<input type="text" name="cust_mobile"><br/>
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <input type="submit" value="保存">
        </form>

三:测试

在浏览器输入http://localhost/ssh01/save.jsp输入数据,点击提交,
数据库表建立成功,数据成功保存

这样,最简单最原始的ssh框架整合完成。


问题一:在CustomerAction中的获取service
图片描述

解决方案:

使用监听器,当项目启动的时候监听ServletContext对象的建立,
当ServletContext对象建立的时候加载applicationContext.xml文件,
建立spring工厂初始化bean将spring容器放置在ServletContext域对象中

spring为咱们提供了ContextLoaderListener,在web.xml文件中配置该监听器,
它会加载applicationContext.xml,建立spring工厂,
并存放在ServletContext域对象中

代码实现

在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>
在CustomerService中获取service
ServletContext servletContext = ServletActionContext.getServletContext();
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
CustomerService customerService = (CustomerService) context.getBean("customerService");
问题二:Action由struts2容器管理,service是交给spring容器管理,不能直接注入
    如何在action中注入service

解决方案:spring整合struts
前提:导入struts-spring-plugin.jar,在项目启动的时候建立spring工厂,
    放置到context域中
    
方式一:Action仍是struts建立,Spring负责为Struts中的Action注入匹配的属性
//由spring容器为action进行属性注入
    private CustomerService customerService;
    public void setCustomerService(CustomerService customerService) {
        this.customerService = customerService;
    }
缘由:为何直接导入struts2-spring-plugin.jar包就能够直接注入?
在default.properties配置有中struts.objectFactory.spring.autoWire = name
Spring负责为Struts中的Action注入匹配的属性,根据属性的名称注入(默认,能够修改)

方式二:将action交给spring容器来管理,
    action是多例的,因此须要配置scope="prototype"属性
    修改applicationContext.xml和struts.xml文件
<!-- 将action交给spring容器来管理 -->
    <bean id="customerAction" class="com.itheima.web.action.CustomerAction" scope="prototype">
        <property name="customerService" ref="customerService"></property>
    </bean>
    修改struts文件:
    <package name="ssh" extends="struts-default" namespace="/">
        <action name="customer_*" class="customerAction" method="{1}">
            <result name="success">/success.jsp</result>
        </action>
    </package>

问题三

在dao层,使用Hibernate操做数据库,须要加载Hibernate.cfg.xml配置文件
获取SessionFactory(相似于DataSource数据源)而后获取到session对象
(相似于Connection链接对象,SessionFactory:是重量级而且线程安全的,
因此在项目中只存在一份即

解决方案
    将SessionFactory交给spring容器管理:单例
sessionFactory是一个接口,在这里咱们使用它的实现类LocalSessionFactoryBean
选择Hibernate5的,由于咱们使用的Hibernate是5.0.7版本的



在applicationContext.xml中配置
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <!-- 须要指定hibernate的核心配置文件,由于这里面有须要的数据库配置参数 -->
        <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
</bean>
使用spring提供的HibernateTemplate在dao层操做数据库,将HibernateTemplate交给
spring容器来管理,并注入到dao层

在applicationContext.xml中进行配置
配置Hibernate模板须要注入SessionFactory,由于模板是对Hibernate的包装,底层仍是
使用session来操做数据库,因此须要获取到session对象,经过SessionFactory对象
<!-- 配置HibernateTemplate模板  -->
        <bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
            <!-- hibernateTemplate模板底层操做数据库是经过session对象,因此须要注入sessionFactory对象获取到session -->
            <property name="sessionFactory" ref="sessionFactory"></property>
        </bean>
        
    <!-- 配置dao -->
    <bean id="customerDao" class="com.itheima.dao.impl.CustomerDaoImpl">
        <!-- 为 dao注入HibernateTemplate dao层使用HibernateTemplate来操做数据库-->
        <property name="hibernateTemplate" ref="hibernateTemplate"></property>
    </bean>
修改以后dao层的代码以下
public class CustomerDaoImpl implements CustomerDao {
    //注入HibernateTemplate来操做数据库
    private HibernateTemplate hibernateTemplate;
    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }
    @Override
    public void save(Customer customer) {
        hibernateTemplate.save(customer);
    }
}
这样直接运行程序,会抛出异常,异常信息为:
Write operations are not allowed in read-only mode (FlushMode.MANUAL):
    Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' 
    marker from transaction definition.
问题:只读模式下(FlushMode.NEVER/MANUAL)写操做不被容许:
把你的Session改为FlushMode.COMMIT/AUTO或者清除事务定义中的readOnly标记

spring会将获取到的session绑定到当前线程中,
为了确保在一个请求中service层和dao层使用的是一个session对象
只有受spring声明式事务的方法才具备写权限。不然在操做的会抛出上面异常

因此还须要在applicationContext.xml中配置spring的声明式事务
<!-- 无论是注解仍是xml文件的方式配置spring声明式事务,都须要指定平台事务管理器 -->
    <!-- Hibernate 的事务平台管理器  -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <!-- 
            须要session对象来开启事务session.beginTransaction()
            因此须要注入SessionFactory来获取到session对象
         -->
         <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <!-- 配置加强/通知spring提供好的,咱们来指定规则 -->
    <tx:advice transaction-manager="transactionManager" id="my_advice">
        <tx:attributes>
            <!-- 这些事务隔离级别,事务传播行为,超时信息都是默认值
            当进行查询操做的时候,能够修改propagation="SUPPORTS" read-only="true"(查询的时候只读)
         -->
            <!-- 在开发中的经常使用配置 -->
            <!-- <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
            <tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
            <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/> -->
            <tx:method name="save" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
        </tx:attributes>
    </tx:advice>
    <!-- aop配置 -->
    <aop:config>
        <!-- 配置切入点  事务控制在service层 -->
        <aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="myPt"/>
        <aop:advisor advice-ref="my_advice" pointcut-ref="myPt"/>
    </aop:config>
这样一个纯xml配置整合ssh框架就完成了!!!

在实际的开发中都是注解+xml来完成的。如今咱们来对代码进行优化。
在实际的开发中:
    咱们自定义bean的建立和注入经过注解来完成(CustomerService等)
    非自定义的bean交给xml文件配置(例如数据源dataSource和SessionFactory)
    
    
优化一:去除struts.xml文件(action的配置使用注解来完成)
使用注解的方式配置action必须导入jar包struts2-convention-plugin-2.3.24.jar
在CustomerAction上面添加注解:
@Namespace("/")
@ParentPackage("struts-default")
public class CustomerAction extends ActionSupport implements ModelDriven<Customer> {
    private static final long serialVersionUID = 1L;
    //使用ModelDriven模型驱动进行数据封装,必须手动来建立对象
    private Customer customer = new Customer();
    @Override
    public Customer getModel() {
        return customer;
    }
    //由spring容器为action进行属性注入
    private CustomerService customerService;
    public void setCustomerService(CustomerService customerService) {
        this.customerService = customerService;
    }
    //保存用户的方法
    @Action(value="customer_save",results={@Result(name="success",location="/success.jsp")})
    public String save(){
        customerService.save(customer);
        return SUCCESS;
    }
}

优化二:全部的自定义bean都使用注解的方式进行配置,
去除applicationContext中的自定义bean
必须在applicationContext中开启组件扫描

在applicationContext中开启组件扫描
   <!-- 开启组件扫描 -->
   <context:component-scan base-package="com.itheima"></context:component-scan>

   CustomerAction中的代码
   @Namespace("/")
   @ParentPackage("struts-default")
   @Controller("customerAction")
   public class CustomerAction extends ActionSupport implements ModelDriven<Customer>{
       @Autowired
       private CustomerService customerService;
   }

   CustomerServiceImpl中配置注解的代码
   @Service("customerService")
   public class CustomerServiceImpl implements CustomerService {
   @Autowired
   private CustomerDao customerDao;

   CustomerDaoImpl中注解的代码
   @Repository("customerDao")
   public class CustomerDaoImpl implements CustomerDao {
   //注入HibernateTemplate来操做数据库
   @Autowired
   private HibernateTemplate hibernateTemplate;

优化三:spring的声明式事务使用xml+注解的方式进行配置
减小applicationContext.xml文件中的配置代码

要在applicationContext中开启事务注解支持
<!-- 开启事务注解支持 -->
<tx:annotation-driven/>
事务是在service层进行控制的,在service层上加上事务注解
能够去除applicationContext中配置加强和aop配置的代码
@Service("customerService")
@Transactional
public class CustomerServiceImpl implements CustomerService

优化四:去除持久化类的映射配置文件,使用注解进行代替

其它字段和属性名相同,能够省略@Column
@Entity
@Table(name="cst_customer")
public class Customer implements Serializable{
    private static final long serialVersionUID = 1L;
    //oid
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
去除持久化类的映射配置文件以后,在Hibernate.cfg.xml文件中
引入持久化类映射文件的代码须要修改:
<!-- Hibernate加载映射 -->
<mapping resource="com/itheima/domain/Customer.hbm.xml"/>
Customer.hbm.xml文件已经去除,修改成
<mapping class="com.itheima.domain.Customer"/>

优化五:去除Hibernate.cfg.xml

在前面applicationContext.xml中将SessionFactory交给spring容器管理的时候
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
    <!-- 须要指定hibernate的核心配置文件,由于这里面有须要的数据库配合参数 -->
    <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
</bean>
指定了核心配置文件,如今须要手动的配置数据库参数以及Hibernate的一些基本配置
如是否显示sql语句,是否格式化sql语句,mysql方言配置等

最终:只留下了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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 开启事务注解支持 -->
    <tx:annotation-driven/>
    <!-- 开启组件扫描 -->
    <context:component-scan base-package="com.itheima"></context:component-scan>
    <!-- 将配置文件加载到容器中 -->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 进行属性的注入 -->
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!-- 将SessionFactory交给spring容器来管理 -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"></property>
        <!-- Hibernate的基本配置 -->
        <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>
        <!-- 指定扫描包  包下具备@Entity的这些类 -->
        <property name="packagesToScan">
            <list>
                <value>com.itheima.domain</value>
            </list>
        </property>
    </bean>
    <!-- 配置HibernateTemplate模板  -->
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
        <!-- hibernateTemplate模板底层操做数据库是经过session对象,因此须要注入sessionFactory对象获取到session -->
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <!-- 无论是注解仍是xml文件的方式配置spring声明式事务,都须要指定平台事务管理器 -->
    <!-- Hibernate 的事务平台管理器  -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <!-- 
            须要session对象来开启事务session.beginTransaction()
            因此须要注入SessionFactory来获取到session对象
         -->
         <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
</beans>

这样,以xml+注解结合方式整合ssh框架就完成了。

相关文章
相关标签/搜索