Spring框架(八)

版权声明:@Wrial https://blog.csdn.net/qq_42605968/article/details/86732261

上一篇写的是Spring中的JDBC操作,今天说一说Spring事务编程.


Spring事务

事务有关知识请参考事务介绍的博客,附上链接:

Spring管理事务的流程:平台事务管理器根据事务定义进行事务管理,事务管理产生的状态记录在事务状态信息里

事务有关的API

  • Platform Transaction Manager
    平台事务管理器
  • TransactionDefinition
    事务定义信息:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读
  • TransactionStatus
    事务状态:用于记录在事务管理过程中,事务的状态的对象。

事务的传播行为

在我们的分层模式中事务应该处于那一层呢,应该在service层。如果遇到很复杂的问题不仅仅是service层调用dao层,也需要service层之间的互相调用,那么事务的传播行为就是用来解决复杂的业务层之间的相互调用

Spring中的七种事务的传播行为:
  • 保证多个操作在同一个事务中

    • PROPAGATION_REQUIRED :默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来
    • PROPAGATION_SUPPORTS :支持事务,如果A中有事务,使用A中的事务。如果A没有事务,不使用事务。
    • PROPAGATION_MANDATORY :如果A中有事务,使用A中的事务。如果A没有事务,抛出异常。
  • 保证多个操作不在同一个事务中

    • PROPAGATION_REQUIRES_NEW :如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没有事务,创建一个新事务,包含自身操作。
    • PROPAGATION_NOT_SUPPORTED :如果A中有事务,将A的事务挂起。不使用事务管理。
    • PROPAGATION_NEVER :如果A中有事务,报异常。
  • 嵌套式事务

    • PROPAGATION_NESTED :嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点。

了解了事务的这写概念,那就上手来演练一下子(光说不练假把式)
在日常生活中事务的用途很多,下边就以转账为例来深入了解事务
建立转账的Dao和Service层,这个很简单就直接附上代码
Dao

package Transactiom;

public interface Dao {
    public void outMoney(String from ,Double money);
    public void inMoney(String to ,Double money);
}

DaoImpl,注意在Dao的实现中继承了JdbcDaoSupport,里边封装着Jdbc模板的set方法,只需要传入datasource就可以自动获得Jdbc模板(this.getJdbcTemplate),所以就可以在xml中注入datasource属性就ok或者在xml配置好模板注入到dao中使用,但是后者的使用还需要再dao中注解声明,不如直接传入datasource简单方便。

package Transactiom;

import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class DaoImpl extends JdbcDaoSupport implements Dao {
    /**
     * 继承这个类只需要datasourse就可以自动创建JDBC模板,也可以自己配置模板 '
     *
     */
    @Override
    public void outMoney(String from, Double money) {
        this.getJdbcTemplate().update("update spring1 set money = money - ? where name = ?", money,from);
    }

    @Override
    public void inMoney(String to, Double money) {
        this.getJdbcTemplate().update("update spring1 set money = money + ? where name = ?", money ,to);
    }
}

转账前:
在这里插入图片描述
转账后:
在这里插入图片描述
但是,如果中途由于一些原因导致不能完整的执行转账这一操作,就会是下边这个场景
转账前:
在这里插入图片描述
转账后:
在这里插入图片描述
会明显的发现前者转账过去而后者未接收到转账。这就是需要事务管理的理由。
但是通过事务模板给转账加上事务测试后和事务的性质完全吻合!
下边是实现事务模板的execute方法,利用接口回调实现事务

 public void transfer(final String from, final String to, final Double money) {

            trsactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                    dao.outMoney(from, money);
                    int d = 999/ 0;
                    dao.inMoney(to, money);
                }
            });

也要在service注入transactionTemplate模板

 <!--配置事务管理平台-->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--配置事务管理模板简化事务操作-->
    <bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
    </bean>

执行成功时会显示正常数据,失败时就会回滚到初始状态!这种是编程式事务,用的不是很多。
下边就说一说非编程式事务

用xml配置

用tx标签配置事务,底层还是我上次写的AOP(环绕通知),因为事务的通知写法是固定的,所以Spring把它导到固定标签<tx…/>中。所以在开始就得引入 <t x … />标签。
我是这么理解的:就是tx里放的就是一个事务的初始化(看作一个加强版的bean),然后再aop的配置中配置切点和切面。

 <tx:advice transaction-manager="transactionManager" id="interceptor">
        <tx:attributes>
            <!--propagation设置传播行为-->
            <!--<tx:method name="*"/>  代表所有方法,用法和sql中的模糊查询相似-->
            <tx:method name="transfer" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    
    <aop:config>
        <aop:pointcut id="transfer" expression="execution(* Transactiom.ServiceImpl.transfer())"/>
        <!--配置单个用advisor,多个用aspect-->
        <aop:advisor advice-ref="interceptor" pointcut-ref="transfer"/>
    </aop:config>

恢复到最初的转账异常测试,发现配置成功。

 public void transfer(final String from, final String to, final Double money) {
        dao.outMoney(from, money);
        int i = 1/0;
        dao.inMoney(to, money);

    }

因为数据库前后对比相同就不放图了。

可以在isolation中设置隔离级别在这里插入图片描述
可以设置只读,回滚,和超时等初始化操作。
在这里插入图片描述
这些配置都是很简单的,如果有人不明白那些隔离级别,我也会在后边写一些隔离级别实现的博客。

用注解配置

必须要在tx中开启注解事务,然后再service类上加一个事务标签就ok

    <bean class="Transactiom.DaoImpl" id="dao">
        <!--<property name="jdbcTemplate" ref="jdbcTemplate"/>-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean class="Transactiom.ServiceImpl" id="service">
        <property name="dao" ref="dao"/>
        <!--<property name="trsactionTemplate" ref="transactionTemplate"/>-->
    </bean>
    <!--配置jdbc-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--jdbc模板-->
    <!--<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">-->
    <!--<property name="dataSource" ref="dataSource"/>-->
    <!--</bean>-->

    <!--配置事务管理平台-->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--开启事务注释模式-->
    <tx:annotation-driven transaction-manager="transactionManager" />

可以再Transaction()里边加上初始化属性和xml配置完全一样

@Transactional()
public class ServiceImpl implements Service

这就是Spring中的事务处理,可以说是很简洁了。后续我还是会继续深入Spring的分析,下一篇就回顾回顾钱前面学的知识并进入到MyBatis的学习。




加油!




猜你喜欢

转载自blog.csdn.net/qq_42605968/article/details/86732261
今日推荐