Spring(3)

Spring(3)


Spring编程式事务管理

从程序上实现细粒度的控制事务的边界,但是与程序代码耦合度高,jdbd、hibernate、spring中不推荐使用。

Spring声明式事务管理

声明式事务管理器类:
JDBC:DataSourceTransactionManager【管理Jdbc中的事务控制】
Hibernate:HibernateTransactionManager【管理Hibernate中事务控制】

XML配置方式实现:事务控制在Service层
1. 引入jar文件
Spring core
Spring Aop
Spring-jdbc
Spring-tx
驱动包、连接池
2. dao/service
3. 配置
* 数据源
* JdbcTemplate
* Dao/Service
* spring声明式事务管理配置
(拦截service方法的执行,动态植入事务控制代码!)
4. 测试
Save();
Int i = 1/0;
Save();

XML配置方式实现事务管理:
<!-- 1. 数据源配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.DataSources">
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql:///hib_demo"></property>
    <property name="user" value="root"></property>
    <property name="password" value="root"></property>
    <property name="initialPoolSize" value="3"></property>
    <property name="maxPoolSize" value="6"></property>
</bean>

<!-- 2. JdbcTemplate配置 ,  注入数据源-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 3. dao实例,注入jdbcTemplate -->
<bean id="deptDao" class="cn.ustb.a_tx_jdbc.DeptDao">
    <property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>

<!-- 4. Service实例,注入dao实例 -->
<bean id="deptService" class="cn.ustb.a_tx_jdbc.DeptService">
    <property name="deptDao" ref="deptDao"></property>
</bean>

<!-- 5. Spring声明式事务管理配置 -->

<!-- 5.1 配置事务管理器类 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 5.2 事务通知配置, 拦截到指定的方法后如何管理事务 -->
<!-- find*  find开头的方法,是只读的事务 -->
<!--   *    上面所有的方法都不满足时候,采用的事务控制规则 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="find*" read-only="true"/>
        <tx:method name="get*" read-only="true"/>
        <tx:method name="*" read-only="false"/>
    </tx:attributes>
</tx:advice>

<!-- 5.3 事务Aop配置 = 切入点表达式  + 应用上面的事务通知 -->
<aop:config>
    <aop:pointcut expression="execution(* cn.ustb.a_tx_jdbc.*Service.*(..))" id="pt"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>

Hibernate中事务管理器类的配置区别:【类不同,传入的是sessionFactory】

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"></property>
</bean>
注解方式实现事务管理
  1. 导包同上
  2. 在XML中开启注解扫描、在Spring声明式事务管理,注解开启
<!-- 事务管理器类 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 开启注解扫描 -->
<context:component-scan base-package="cn.ustb.b_tx_jdbc_anno"></context:component-scan>
<!-- spring声明式事务管理,注解开启 -->
<tx:annotation-driven transaction-manager="txManager"/>

注解方式实现:使用@Transactional注解
写到方法上,表示当前方法应用事务控制。
写到类上,表示当前类的所有方法都会应用事务。
写到父类上,当执行父类的这个方法时候才应用事务。

缺点:与代码的耦合度高,不提倡使用

// 当前方法应用事务
@Transactional(
        readOnly=false,      // 读写的事务,当修改数据时候用;如果查询就设置为true
        isolation=Isolation.DEFAULT,  // 事务隔离级别
        timeout=-1,         // 事务执行的超时时间, -1 表示不超时
        //noRollbackFor=ArithmeticException.class,   // 遇到指定的异常不回滚
        propagation=Propagation.REQUIRED      // 事务传播行为
)
public void save() {
    // 插入日志
    logService.insertLog(); 
    int i = 1/0;        
    // 插入部门
    deptDao.save();
}
事务传播行为详解:

REQUIRED_NEW:当前执行方法必须在事务下运行且当前方法始终开启一个新的事务。
REQUIRED:当前执行方法必须在事务环境下运行。
SUPPORTS:支持事务环境,如果当前方法没有事务,也可以运行,但不会开启新的事务。
NEVER:当前方法不能在事务环境下运行。

Spring与Hibernate的整合

在Spring与Struts的整合之中,关键点在于将Action的创建交给IOC容器。于是需要导入Spring-web包和Struts2-spring的包。

在Spring和hibernate的整合中,关键点在于将单例的SessionFactory对象的创建交给spring的ioc容器。并且将事务管理交给Spring声明式事务管理器

之前只进行过SSH的整合,单独对Spring和Hibernate的整合还是头一回,竟然问题重重。现在来回顾一下历程。

整合步骤:
1. 导包【引入Hibernate/Spring相关包】
Hibernate.jar【对hibernate的支持包】
spring-core【spring核心包】
spring-aop【切面编程包,spring事务管理必备】
spring-orm【对ORM的支持,即对hibernate的支持】
spring-tx【对事务管理的支持】
2. 编写配置文件
由于已经将sessionFactory的创建交给了IOC容器,因此不再需要hibernate.cfg.xml的配置文件了(里面配置了sessionFactory创建所需要的一些参数),从另一个角度来看,在spring的applicationContext.xml中就需要配置sessionFactory的创建信息了。

由于只是对Spring和Hibernate的整合,没有涉及到web的内容,因此web.xml配置中就不需要进行配置啦。当SSH整合时是需要的。

下面将具体配置文件贴出来:

aoolicationContext.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:p="http://www.springframework.org/schema/p"
    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">

    <!-- 加载db.properties配置文件 -->
    <context:property-placeholder location="classpath:/config/db.properties" />

    <!-- 实例化database -->
    <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>
        <property name="initialPoolSize" value="${initialPoolSize}"></property>
        <property name="maxPoolSize" value="${maxPoolSize}"></property>
        <property name="acquireIncrement" value="${acquireIncrement}"></property>
    </bean>

    <!-- 2. Spring 创建SessionFactoru对象 -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <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.hbm2ddl.auto">update</prop>
                <!-- 当spring和hibernate单独配置时,需要加上这条,排除验证 -->
                <prop key="javax.persistence.validation.mode">none</prop> 
            </props>
        </property>
        <property name="mappingLocations">
            <list>
                <value>classpath:cn/ustb/*/*.hbm.xml</value>
            </list>
        </property>
    </bean>

    <bean id="userDao" class="cn.ustb.a_springandhibernate.UserDao">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <bean id="userService" class="cn.ustb.a_springandhibernate.UserService">
        <property name="userDao" ref="userDao"></property>
    </bean>
    <bean id="userAction" class="cn.ustb.a_springandhibernate.UserAction" scope="prototype">
        <property name="userService" ref="userService"></property>
    </bean>


    <!-- spring的声明式事务管理器 --><!-- 和Jdbc的不一样了,Spring对jdbc的支持的类不一样,再一个Jdbc传入的是datasource -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <!-- 事务通知配置,拦截到方法之后如何管理事务 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="get*" read-only="true"/>   
            <tx:method name="*" read-only="false"/> 
        </tx:attributes>
    </tx:advice>

    <!-- 面向切面编程,切入点配置,定义如何拦截方法通过executions表达式 -->
    <aop:config>
        <aop:pointcut expression="execution(* cn.ustb..*Service.*(..))" id="pc"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
    </aop:config>

</beans>  

在SSH整合时没有遇到过的问题出现在applicationContext.xml的编写中,在测试时出现了Unable to get the default Bean Validation factory的报错,解决办法是加上:具体原因我也不清楚,
http://blog.163.com/guomaolin_gavin/blog/static/199618307201111197542905/

<property name="hibernateProperties">
<props>
      <!-- 当spring和hibernate单独配置时,需要加上这条,排除验证 -->
      <prop key="javax.persistence.validation.mode">none</prop> 
  </props>
</property>

别的地方没什么好说的,关键之处就是将sessionFacotry的创建交给IOC容器以及对事务的管理。

Spring创建SessionFactory的方式

本例中直接将SessionFactory的创建交给了IOC容器,其实还有其他的几种方式:

方式1: 直接加载hibernate.cfg.xml的方式,创建sessionFactory对象

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
</bean>

方式2: 连接池交给spring管理,其他配置还是写到hibernate.cfg.xml中

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
    <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
</bean>

方式3:(推荐) 所有的配置都在spring中完成

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <!-- a. 注入连接池 -->
    <property name="dataSource" ref="dataSource"></property>

    <!-- b. hibernate常用配置: 方言、自动建表、显示sql -->
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
        </props>
    </property>

    <!-- c. 加载所有的映射(根据路径加载) 
    <property name="mappingLocations">
        <list>
            <value>classpath:cn/ustb/entity/*.hbm.xml</value>
        </list>
    </property>
    -->
    <!-- c. 根据目录加载所有的映射 -->
    <property name="mappingDirectoryLocations">
        <list>
            <value>classpath:cn/ustb/entity</value>
        </list>
    </property>
</bean>

Spring对DAO操作的支持

JDBC中

Spring提供了JdbcTemplate的模板类,相当于statement可以直接对数据库进行CRUD操作。

Hibernate

在纯Hibernate中,使用的sessionFactory对象创建session操作数据库。Spring中同样提供了对sessionFactory创建的支持。
1. 直接在Dao中使用sessionFactory对象操作数据库。【利于解耦
2. 使用Spring提供的HibernateTemplate工具类操作数据库。优点:对session的常用操作进行了封装,比较方便。
3. HibernateDaoSupport工具类,Dao类直接继承HibernateDaoSupport工具类即可。HibernateDaoSupport对HibernateTemplate类进行了封装。【推荐】
getHibernanteTemplate()是final方法。不允许覆盖。此方法需要注入sessionFactory

//1. 直接使用sessionFacotory
public class UserDao2 {
    private SessionFactory sessionFactory;
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public void save(Object obj){
        Session session = sessionFactory.openSession();
        session.save(obj);
    }
}

//2.使用Hibernatetemplate
public class UserDao3 {
    HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory);
    public void save(Object obj){
        hibernateTemplate.save(obj);
    }
}

//3.继承HibernateDaoSupport,使用getHibernateTemplate()操作
public class UserDao1 extends HibernateDaoSupport{
    public void save(Object obj){
        HibernateTemplate template = getHibernateTemplate();
        template.save(obj);
    }
}

SSH的整合

之前已经学习了Struts2+hibernate、Spring+Struts2、Spring+Hibernate。已经知道
Spring+struts2的关键:将action的创建交给IOC容器
Spring+Hibernate的关键:将SessinFacotory的创建交给IOC容器并且配置好Spring的事务管理

现在来尝试对Spring+Struts2+Hibernate进行整合。

步骤
  1. 导包
    struts2核心
    hibernate核心
    spring-core【spring核心】
    spring-aop【切面编程】
    spring-orm【对象关系映射,对hibernate的支持】
    spring-web【对struts的支持】
    spring-tx【对事务的支持】
    驱动【connector】+连接池【c3p0】

  2. 配置
    Web.xml 配置struts核心过滤器 + Spring容器初始化
    Struts.xml 配置访问路径与action类的映射关系
    applicationContext-public.xml Spring容器配置 【公用配置】
    applicationContext-dao.xml Spring容器配置 【dao配置】
    applicationContext-service.xml Spring容器配置 【service配置】
    applicationContext-action.xml Spring容器配置 【action配置】

  3. 编写代码

web.xml中的配置

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


  <!-- 配置sturts2核心过滤器 -->
    <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>*.action</url-pattern>
    </filter-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:bean*.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>
需要注意的几个细节:
  1. 实现了接口的目标对象的代理对象用接口去接。
    例如:如果把Service的方法作为切入点,则IOC容器会对Service进行代理,若Service实现了接口,会用JDK代理,若其没实现接口,则会用Cglib代理。因此在Action中维护Service,要用Service的接口去接。否则会报错。

  2. Action需配置scope="prototype"struts.xml中class引用IOC容器中的对象。

整合过程中遇到过的问题:
1. QUE:There is no Action mapped for namespace [/] and action name [UserAction_save] associated with context path [/MySpringAndHibernate]. - [unknown location]
ANS:Action中的方法不能带有自定义的参数,导致方法签名不一样,因此会找不到该方法。

  1. Spring中配置的事务不起作用啦,为什么?
    ANS:在SSH的整合中,UserDao中对数据库的操作依然使用的SessionFactory的方式,而Spring事务的配置要求使用HibernateTemplate或者继承HibrenateDaoSupport,修改UserDao的持久层实现之后,问题得到解决。

事务不起作用很可能是代理失败导致的,可以通过下列方式检测对象是否是正确的被代理了:

System.out.println("aopproxy?"+AopUtils.isAopProxy(userService));
System.out.println("jdk dynamic proxy?"+AopUtils.isJdkDynamicProxy(userService));
System.out.println("cglib proxy?"+AopUtils.isCglibProxy(userService));

更多的事务失效的原因可以参考:
http://blog.csdn.net/szwangdf/article/details/41516239

  1. 在部署的过程中,经常会出现java.lang.OutOfMemoryError: PermGen space的问题,如何解决?
    ANShttp://peak.iteye.com/blog/52606

方便起见,将所有的配置贴出来:

User.hbm.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.ustb.a_springandhibernate">

    <class name="User" table="t_user">
        <id name="id" column="userId">
            <generator class="native"></generator>
        </id>
        <property name="name" column="username"></property>
        <property name="pwd" column="pwd"></property>
    </class>

</hibernate-mapping>

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:p="http://www.springframework.org/schema/p"
    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">

    <!-- 加载db.properties配置文件 -->
    <context:property-placeholder location="classpath:/config/db.properties" />

    <!-- 实例化database -->
    <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>
        <property name="initialPoolSize" value="${initialPoolSize}"></property>
        <property name="maxPoolSize" value="${maxPoolSize}"></property>
        <property name="acquireIncrement" value="${acquireIncrement}"></property>
    </bean>

    <!-- 2. Spring 创建SessionFactoru对象 -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <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.hbm2ddl.auto">update</prop>
                <!-- 当spring和hibernate单独配置时,需要加上这条,排除验证 -->
<!--                <prop key="javax.persistence.validation.mode">none</prop> 
 -->            </props>
        </property>
        <property name="mappingLocations">
            <list>
                <value>classpath:cn/ustb/*/*.hbm.xml</value>
            </list>
        </property>
    </bean>

    <!-- spring的声明式事务管理 --><!-- 和Jdbc的不一样了,Spring对jdbc的支持的类不一样,在一个  传入的是datasource -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <!-- 事务通知配置,拦截到方法之后如何管理事务 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" read-only="false" rollback-for="Throwable"/>    
        </tx:attributes>
    </tx:advice>

    <!-- 面向切面编程,切入点配置,定义如何拦截方法executions表达式 -->
    <aop:config>
        <aop:pointcut expression="execution(* cn..*Service.*(..))" id="pc"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
    </aop:config>

    <import resource="classpath:config/bean-service.xml"/>
    <import resource="classpath:config/bean-dao.xml"/>
    <import resource="classpath:config/bean-action.xml"/>

</beans>     

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>
    <package name="action1" extends="struts-default">
        <action name="UserAction_*" class="userAction" method="{1}">
            <result name="success">/success.jsp</result>
            <result name="error">/error.jsp</result>
        </action>
    </package>
</struts>

web.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>
    <package name="action1" extends="struts-default">
        <action name="UserAction_*" class="userAction" method="{1}">
            <result name="success">/success.jsp</result>
            <result name="error">/error.jsp</result>
        </action>
    </package>
</struts>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值