04_Spring-SSH框架整合

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的日志包)
img54.png
1.2 Sturts2的配置文件
1. 修改web.xml配置Struts2的前端控制器
    <!-- Struts2前端控制器 -->
    <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. 配置Sturts2的核心配置文件struts2.xml到src(从模板中复制,删除到最简单的配置)
    如果要添加配置提示,根据DTD文件添加到Xml Catalog中
        
    1. 关闭开发者模式(常量设置从/org/apache/struts2/default.properties中查找)
        缺点:开发者模式不能处理ajax的异常
        但我们需要自动加载配置文件的功能,因此要开启自动加载配置文件的开关

    2. 修改Sturts2的前端样式为simple
    
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
    <struts>
    
        <!-- 关闭开发者模式 -->
        <!-- <constant name="struts.devMode" value="true" /> -->
        <constant name="struts.configuration.xml.reload" value="true" />
    
        <!-- 使用简单样式 -->
        <constant name="struts.ui.theme" value="simple" />
    
        <!-- 自定义包,配置Action -->
        <package name="default" namespace="/" extends="struts-default">
        </package>
    </struts>


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的创建起到监听作用。

<!-- Spring的核心监听器 -->
<listener>
    <!-- Spring提供的ContextLoaderListener监听器 -->
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 全局参数 -->
<context-param>
    <!-- 默认加载的配置文件在web-inf里的applicationContext.xml,因此需要修改 -->
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
2.3 Spring的核心配置文件applicationContext.xml

复制核心配置文件到src目录下:

头约束包括:beans,context,tx,aop
-----------------------------------------------------------------------------

<?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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
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目录下

<?xml version="1.0" encoding="UTF-8"?>
<!-- 导入Hibernate核心配置的DTD约束 -->
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <!--1. 基本配置 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_day02</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>

        <!--2. 与本地线程绑定 -->
        <property name="hibernate.current_session_context_class">thread</property>

        <!--3. Hibernate的属性 -->
        <!--Hibernate的方言配置,实现了跨数据库  -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

        <!-- 显示sql语句 -->
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>

        <!-- Hibernate表格的创建方式 -->
        <property name="hibernate.hbm2ddl.auto">update</property>

        <!-- 
            设置隔离级别:
                hibernate.connection.isolation = 4
                1-Read uncommitted isolation
                2-Read committed isolation
                4-Repeatable read isolation
                8-Serializable isolation 
        -->
        <property name="hibernate.connection.isolation">4</property>

        <!-- C3P0的供应商 -->
        <property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
        <!-- 最小连接 -->
        <property name="hibernate.c3p0.min_size">5</property>
        <!-- 最大连接数 -->
        <property name="hibernate.c3p0.max_size">20</property>
        <!-- 连接超时时长 -->
        <property name="hibernate.c3p0.timeout">120</property>
        <!-- 每120秒检查空闲连接 -->
        <property name="hibernate.c3p0.idle_test_period">120</property>
        <!-- 最大statments数量 -->
        <property name="hibernate.c3p0.max_statements">120</property>
        <!-- 连接用完后,每次增加的连接数 -->
        <property name="hibernate.c3p0.acquire_increment">2</property>
        <!-- 每次都验证连接是否可用 -->
        <property name="hibernate.c3p0.validate">false</property>

        <!--4. 加载映射文件 -->
        <mapping resource="com/itdream/domain/Customer.hbm.xml" />
    </session-factory>
</hibernate-configuration>

到这里,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:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入DTD约束 -->
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
 <hibernate-mapping>
    <!-- 建立持久化类与表的映射关系 -->
    <class name="com.itdream.ssh.domain.Product" table="product">
        <!-- 建立持久化类标OID与数据库主键的映射,name与column相同可省略column -->
        <id name="pid">
            <generator class="native"/>
        </id>
        
        <!-- 配置持久化类普通属性与数据库字段的映射 -->
        <property name="pname"/>
        <property name="price"/>
    </class>
 </hibernate-mapping>

-----------------------------------------------------------------------------

在Hibernate.cfg.xml中加载该映射文件:
    <mapping resource="com/itdream/ssh/domain/Product.hbm.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


    <!-- 引入外部配置文件,下面使用${key}取值 -->
    <context:property-placeholder location="classpath:db.properties"/>
    
    <!-- 注册数据源 -->
    <bean id="dataSource" class="${jdbc.dataSourceClass}">
        <property name="driverClass" value="${jdbc.driverClassName}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>


2. 将hibernate.cfg.xml的参数完全配置到applicationContext.xml中,由Spring管理SessionFactory.这样Spring容器创建的时候,会创建注入了数据源的SessionFacotory.

**这里注意导入LocalSessionFactoryBean的时候,不要导错版本的全包名,选择Hibernate5**

    <!-- Spring管理SessionFactory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <!-- 1.数据源 -->
        <property name="dataSource" ref="dataSource"/>
        
        <!-- 2.Hibernate属性 -->
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
        
        <!-- 3.Hibernate映射文件 -->
        <property name="mappingResources">
            <list>
                <value>com/itdream/ssh/domain/Product.hbm.xml</value>
            </list>
        </property>
    </bean>

修改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可以直接获取使用。

img55.png
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<Product> findAll() {
        // 方式一:Spring提供了简单的方式
        return getHibernateTemplate().loadAll(Product.class);
        // 方式二:使用hql查询
        // return getHibernateTemplate().find("from Product");
    }

    // ------复杂查询-----------
    // 查询id大于x的商品(HQL语句查询)
    public List<Product> findGreaterthanID(Integer pid) {
        return (List<Product>) getHibernateTemplate().find("form Product where pid > ?", pid);
    }

    // 命名查询(sql语句与java代码解耦,写在Product.hbm.xml中)
    // 模糊查询
    public List<Product> findByNamedQuery(String name) {
        return (List<Product>) getHibernateTemplate().findByNamedQuery("Product.findByNameLike", "%" + name + "%");
    }

    // 模糊查询(Criteria离线查询)
    public List<Product> findByCriteria(DetachedCriteria criteria) {
        return (List<Product>) 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<Product> findAllProduct() {
        return productDAO.findAll();
    }

    public List<Product> findProductsGreaterthanID(Integer pid) {
        return productDAO.findGreaterthanID(pid);
    }

    // Criteria离线查询
    public List<Product> findProductsByCriteria(DetachedCriteria criteria) {
        return productDAO.findByCriteria(criteria);
    }

    // 命名查询
    public List<Product> findByNamedQuery(String name) {
        return productDAO.findByNamedQuery(name);
    }
}


Product.hbm.xml中配置命名查询的语句:
<!-- 全局hql语句 -->
<query name="Product.findByNameLike">
    from Product where pname like ?
</query>

-------------------------------------------------------------------------

在applicationContext.xml中注册ProductDAO和ProductService:

<!-- Spring容器中bean的装配 -->
<!-- 注册ProductDAO -->
<bean id="productDAO" class="com.itdream.ssh.dao.ProductDAO">
    <!-- 注入SessionFactory -->
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

<!-- 注册ProductService -->
<bean id="productService" class="com.itdream.ssh.dao.ProductService">
    <!-- 注入ProductDAO -->
    <property name="productDAO" ref="productDAO"/>
</bean>

---------------------------------------------------------------------------

为上面ProductService中的所有方法配置事务管理:(基于AOP的Spring写好的增强类)
<!-- Spring事务管理 -->
<!-- 注册对应的事务管理器,注入数据源提供连接,这里是session以管理事务 -->
<bean id="transactionManager"
    class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <!-- 注入数据源 -->
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<!-- 注册事务增强类,注入参数事务管理器,设置事务策略 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="save*" />
        <tx:method name="update*" />
        <tx:method name="delete*" />
        <tx:method name="find*" read-only="true" />
    </tx:attributes>
</tx:advice>

<!-- 配置切面 -->
<aop:config>
    <!-- 定义切入点 -->
    <aop:pointcut expression="execution(* com.itdream.ssh.dao.ProductService.*(..))" id="txPointcut"/>
    
    <!-- 配置切入点与通知的关联 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

----------------------------------------------------------------------------

测试:

@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<Product> allProducts = productService.findAllProduct();
        System.out.println(allProducts);
    }

    @Test
    public void findProductsByCriteriaTest() {
        DetachedCriteria criteria = DetachedCriteria.forClass(Product.class);
        criteria.add(Restrictions.like("pname", "%米%"));
        List<Product> productListByCriteria = productService.findProductsByCriteria(criteria);
        System.out.println(productListByCriteria);
    }

    // 命名查询测试
    @Test
    public void findProductsByNameQueryTest() {
        List<Product> listByNameQuery = productService.findByNamedQuery("%米%");
        System.out.println(listByNameQuery);
    }
}

测试成功

上面就完成了Hibernate与Spring整合的所有步骤:

  • 导包
  • Hibernate将SessionFactory交由Spring管理
  • Spring对Service层的方法进行事务管理

    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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 引入外部配置文件,下面使用${key}取值 -->
        <context:property-placeholder location="classpath:db.properties" />
    
        <!-- 注册数据源 -->
        <bean id="dataSource" class="${jdbc.dataSourceClass}">
            <property name="driverClass" value="${jdbc.driverClassName}" />
            <property name="jdbcUrl" value="${jdbc.url}" />
            <property name="user" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
    
        <!-- Spring管理SessionFactory -->
        <bean id="sessionFactory"
            class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
            <!-- 1.数据源 -->
            <property name="dataSource" ref="dataSource" />
    
            <!-- 2.Hibernate属性 -->
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                    <prop key="hibernate.show_sql">true</prop>
                    <prop key="hibernate.format_sql">true</prop>
                    <prop key="hibernate.hbm2ddl.auto">update</prop>
                </props>
            </property>
    
            <!-- 3.Hibernate映射文件 -->
            <property name="mappingResources">
                <list>
                    <value>com/itdream/ssh/domain/Product.hbm.xml</value>
                </list>
            </property>
        </bean>
    
        <!-- Spring事务管理 -->
        <!-- 注册对应的事务管理器,注入数据源提供连接,这里是session以管理事务 -->
        <bean id="transactionManager"
            class="org.springframework.orm.hibernate5.HibernateTransactionManager">
            <!-- 注入数据源 -->
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
    
        <!-- 注册事务增强类,注入参数事务管理器,设置事务策略 -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="save*" />
                <tx:method name="update*" />
                <tx:method name="delete*" />
                <tx:method name="find*" read-only="true" />
            </tx:attributes>
        </tx:advice>
    
        <!-- 配置切面 -->
        <aop:config>
            <!-- 定义切入点 -->
            <aop:pointcut expression="execution(* com.itdream.ssh.dao.ProductService.*(..))" id="txPointcut"/>
            
            <!-- 配置切入点与通知的关联 -->
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
        </aop:config>
    
        <!-- Spring容器中bean的装配 -->
        <!-- 注册ProductDAO -->
        <bean id="productDAO" class="com.itdream.ssh.dao.ProductDAO">
            <!-- 注入SessionFactory -->
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
    
        <!-- 注册ProductService -->
        <bean id="productService" class="com.itdream.ssh.dao.ProductService">
            <!-- 注入ProductDAO -->
            <property name="productDAO" ref="productDAO" />
        </bean>
    </beans>

注意:在导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.

============================================================================

表单页面提交参数:
<s:form action="product_saveProduct.action" method="post">
    商品名称:<s:textfield name="pname"/><br/>
    商品价格:<s:textfield name="price"/><br/>
    <s:submit value="提交"/>
</s:form>

struts.xml配置文件配置Action:
    (class内使用伪类名,即applicationContext.xml中Action动作类的注册bean的id)
<!-- 自定义包,配置Action -->
<package name="default" namespace="/" extends="struts-default">
    <action name="product_*" class="productAction" method="{1}"></action>
</package>


applicationContext.xml注册Action动作类 :

<!-- 注册ProductAction -->
<bean id="productAction" class="com.itdream.ssh.web.action.ProductAction" scope="prototype">
    <!-- 注入Service -->
    <property name="productService" ref="productService" />
</bean>

============================================================================

按照上面这么配置,Sturts2与Spring就完成了完全整合,Action动作类的创建交由给了Spring管理.并且可以使用Spring的AOP,属性注入等高级功能。

注意:Action是多实例的,而Spring管理的对象是单实例的,因此注册bean的时候,使用scope设置其为多实例prototype


小结:

img56.png

三、解决延迟加载问题

什么是延迟加载问题 ?

img57.png
多表关系时,Spring的查找机制默认是懒加载的,在查找了员工数据,返回到表现层,Session关闭以后,表现层使用list时,
会发送sql语句查询关联的信息部门,这时候就会出错,因为session已经关闭了。

如何解决延迟加载问题?

方案一: 配置为立即加载 lazy=false (不推荐 )
    我们查询的数据不一定都需要关联数据,太浪费资源。

方案二: Service方法返回前, 对延迟数据进行初始化  (缺点多写代码 )
List<Employee> list = dao.findAll (); 
for(Employee e : list ){
    Hibernate.initialize(e.getDepartment() );
}

【推荐】方案三: OpenSessionInView 机制 (将Session开启到表现层 最前面 Filter )
Spring框架提供了一个过滤器OpenSessionInViewFilter,让session对象在WEB层就创建,在WEB层销毁。
只需要配置该过滤器即可[需要配置在struts2 Filter前面]

web.xml:
<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>

注意:需要在struts2的核心过滤器之前进行配置

OpenSessionInViewFilter原理:  
    在request过程中维持session。延迟session的关闭,直到request结束,再自动关闭session。

缺点:
    如果没有被事务管理的方法, OpenSessionInViewFilter 会将这些方法的事务变为 readOnly 的。
    但是这个问题可以避免,在application的事务管理中:将事务拦截到的方法,都配置事务的属性。都设置好read-only的事务策略。

四、 各配置文件总结

SSH框架完全整合完毕后,各配置文件的最终形态:(以后使用直接修改)

此次整合用到的jar包37个。

4.1 web.xml配置文件

  • Spring的核心监听器,将Spring容器与ServletContext绑定,保证Spring容器只有一个.
    • 前提:导入Springweb的jar包
  • OpenSessionView过滤器,避免Spring与Hibernate整合后的延迟加载问题,将Session对象的关闭延迟到表现出。
  • Struts2的前端控制器,拦截所有请求,进行统一处理(初始化,拦截器...)

web.xml :

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
    <display-name>day42_ssh2</display-name>

    <!-- Spring的核心监听器 -->
    <listener>
        <!-- Spring提供的ContextLoaderListener监听器 -->
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 全局参数 -->
    <context-param>
        <!-- 默认加载的配置文件在web-inf里的applicationContext.xml,因此需要修改 -->
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!-- Struts2前端控制器 -->
    <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>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
</web-app>

4.2 struts.xml配置文件

  • 开启配置文件自动加载
  • 配置Struts2简单样式
  • 根据页面请求,配置对应的Action
    • 这里的class使用伪类名,在applicationContext.xml中配置对应的Action动作类的bean,完成整合

struts.xml :

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>

    <!-- 关闭开发者模式 -->
    <!-- <constant name="struts.devMode" value="true" /> -->
    <constant name="struts.configuration.xml.reload" value="true" />

    <!-- 使用简单样式 -->
    <constant name="struts.ui.theme" value="simple" />

    <!-- 自定义包,配置Action -->
    <package name="default" namespace="/" extends="struts-default">
        <action name="product_*" class="productAction" method="{1}"></action>
    </package>
</struts>

4.3 applicationContext.xml配置文件

  • 配置数据源
  • 这里是与Hibernate整合,因此配置SessionFactory,将hibernate.cfg.xml的所有参数配置都配置到applicationContext.xml配置文件的sessionFactory
    • 注入数据源
    • 配置Hibernate属性
    • 加载Hibernate的映射文件
  • 事务管理的配置
    • 确定增强类Service(注册)
    • 注册Spring提供的事务增强类<tx:advice>相当于注册
      • 注入事务管理器,属性transaction-manager="transactionManager"
      • 配置事务的属性策略read-only等
    • 注册事务管理器
      • <bean id="transactionManager" class="HibernateTransactionManager....">
      • 注入数据源提供连接Session管理事务
    • 配置切面aspect:<aop:config>
      • 定义切入点<aop:pointcut>
      • 配置切入点与通知的关联<aop:advisor>
  • Spring容器bean的装配注册
    • 其中注册DAO层,需要注入数据源,使HibernateDaoSupport能够生成HibernateTemplate.

appliactionContext.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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 引入外部配置文件,下面使用${key}取值 -->
    <context:property-placeholder location="classpath:db.properties" />

    <!-- 注册数据源 -->
    <bean id="dataSource" class="${jdbc.dataSourceClass}">
        <property name="driverClass" value="${jdbc.driverClassName}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <!-- Spring管理SessionFactory -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <!-- 1.数据源 -->
        <property name="dataSource" ref="dataSource" />

        <!-- 2.Hibernate属性 -->
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>

        <!-- 3.Hibernate映射文件 -->
        <property name="mappingResources">
            <list>
                <value>com/itdream/ssh/domain/Product.hbm.xml</value>
            </list>
        </property>
    </bean>

    <!-- Spring事务管理 -->
    <!-- 注册对应的事务管理器,注入数据源提供连接,这里是session以管理事务 -->
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <!-- 注入数据源 -->
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <!-- 注册事务增强类,注入参数事务管理器,设置事务策略 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" />
            <tx:method name="update*" />
            <tx:method name="delete*" />
            <tx:method name="find*" read-only="true" />
        </tx:attributes>
    </tx:advice>

    <!-- 配置切面 -->
    <aop:config>
        <!-- 定义切入点 -->
        <aop:pointcut
            expression="execution(* com.itdream.ssh.service.ProductService.*(..))"
            id="txPointcut" />

        <!-- 配置切入点与通知的关联 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
    </aop:config>

    <!-- Spring容器中bean的装配 -->
    <!-- 注册ProductDAO -->
    <bean id="productDAO" class="com.itdream.ssh.dao.ProductDAO">
        <!-- 注入SessionFactory -->
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <!-- 注册ProductService -->
    <bean id="productService" class="com.itdream.ssh.service.ProductService">
        <!-- 注入ProductDAO -->
        <property name="productDAO" ref="productDAO" />
    </bean>

    <!-- 注册ProductAction -->
    <bean id="productAction" class="com.itdream.ssh.web.action.ProductAction" scope="prototype">
        <!-- 注入Service -->
        <property name="productService" ref="productService" />
    </bean>
</beans>

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语句的定义:

<!-- 全局hql语句 -->
<query name="Product.findByNameLike">
    from Product where pname like ?
</query>

总结完毕。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容