Spring 事务管理有两种方式:声明式事务和编程式事务。其中,编程式事务是基于TransactionTemplate的手动管理,不太常用,我们常用的是声明式事务。声明式事务又有三种实现方式:

  • 基于TransactionProxyFactoryBean的方式
  • 基于AspectJ的XML方式
  • 基于注解的方式

一、基于注解的方式

(一)在Spring的xml文件中的配置

1. 配置事务管理器

2. 开启事务注解

java 编程式事务调用声明式事务 spring声明式事务实现_spring


在需要开启事务管理的方法上加上注解 @Transactional

java 编程式事务调用声明式事务 spring声明式事务实现_java 编程式事务调用声明式事务_02

@Transactional 注解中可以进行如下属性设置:

  • propagation:指定事务的传播行为(即当前的事务方法被另外一个事务方法调用时,如何使用事务, 默认取值为 REQUIRED,
    即使用调用方法的事务)。
  • isolation:指定事务的隔离级别,最常用的取值READ_COMMITTED
  • 默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚,也可以通过对应的属性进行设置。通常情况下取默认值即可。
  • readOnly:指定事务是否为只读,表示这个事务只读取数据但不更新数据。这样可以帮助数据库引擎优化事务.。若真的是一个只读取数据库值的方法,应设置 readOnly=true。
  • timeout:指定强制回滚之前事务可以占用的时间。若超过了timeout指定的时间还为执行,则会抛出异常。

(二) 案例

  银行转账业务。A用户向B用户转账,A用户减少一定的金额,B用户增加一定的金额,要么成功,要么失败。

1. 实体类Bean对象:

package com.zm.bean;

import org.springframework.stereotype.Component;

    @Component("bank")
    public class Bank {
        private int id; //序号
        private String cardId; //账号
        private String userName; //用户名
        private double money; //金额

        public Bank() {

        }

        public Bank(int id,String cardId, String userName, double money) {
            this.id = id;
            this.cardId = cardId;
            this.userName = userName;
            this.money = money;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getCardId() {
            return cardId;
        }

        public void setCardId(String cardId) {
            this.cardId = cardId;
        }

        public String getUserName() {
            return userName;
        }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Bank{" +
                "id=" + id +
                ", cardId='" + cardId + '\'' +
                ", userName='" + userName + '\'' +
                ", money=" + money +
                '}';
    }
}

2. dao层数据操作:

接口:

package com.zm.dao;

import com.zm.bean.Bank;

public interface BankDao {
    //根据序号查询账户信息
    public Bank findBankById(int id);
    //更新bank表中金额信息 转走
    public void updateFrom(int fromId,double money);
    //转入
    public void updateTo(int toId,double money);
}

实现类:

package com.zm.dao.impl;

import com.zm.autoException.BankException;
import com.zm.bean.Bank;
import com.zm.dao.BankDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository("bankDao")
public class BankDaoImpl implements BankDao {
    @Autowired  //自动装配
    private JdbcTemplate template;

    @Override
    public Bank findBankById(int id) {
        String sql = "select * from bank where id=?";
        Bank bank = template.queryForObject(sql, new BeanPropertyRowMapper<Bank>(Bank.class), id);
        return bank;
    }

    @Override
    public void updateFrom(int fromId, double money) {
        Bank bank = findBankById(fromId);
        double yu = bank.getMoney();
        if(yu<=0){
            throw new BankException("已经没有余额了");
        }else if(yu<money){
            throw new BankException("余额不足,无法执行!");
        }
        String sql = "update bank set money = money - ? where id = ?";
        int update = template.update(sql, money, fromId);
        System.out.println(update);
    }

    @Override
    public void updateTo(int toId, double money) {
        String sql = "update bank set money = money + ? where id = ?";
        int update = template.update(sql, money, toId);
        System.out.println(update);
    }
}

3. 业务逻辑(service)层:

业务接口:

package com.zm.service;

public interface TransferDao {
    public void transferMoney(int fromId,int toId,double money);
}

实现类:

package com.zm.service.impl;

import com.zm.dao.BankDao;
import com.zm.service.TransferDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("transferDao")
public class TransferImpl implements TransferDao {
    @Autowired
    private BankDao bankDao;

    @Transactional  //添加事务注解,表示使用事务管理机制
    @Override  //银行转账
    public void transferMoney(int fromId,int toId,double money) {
        /*A账户转向B账户*/

        //从A账户中扣除相应的金额
        bankDao.updateFrom(fromId,money);
        //B账户中增加相应的金额
        bankDao.updateTo(toId,money);
    }
}

4. xml配置文件(applicationContext-tx.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: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">
    <!--开启注解扫描-->
    <context:component-scan base-package="com.zm"></context:component-scan>
    <!--导入资源文件-->
    <context:property-placeholder location="classpath:db.properties"/>
    <!--配置druid数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="initialSize" value="${jdbc.initialSize}"/>
        <property name="minIdle" value="${jdbc.minIdle}"/>
        <property name="maxActive" value="${jdbc.maxActive}"/>
    </bean>

    <!--配置spring的JdbcTemplate-->
    <bean id="template" 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"></tx:annotation-driven>

</beans>

2、基于XML文件的配置方式

(一)xml文件中的配置方式

java 编程式事务调用声明式事务 spring声明式事务实现_spring_03


也可以在 <tx:method name="" /> 标签中设置事务的传播方式,隔离级别。是否可读等属性。

java 编程式事务调用声明式事务 spring声明式事务实现_xml_04

(二)案例:

银行转账业务。

1. 实体类对象:

public class Bank {
    private int id; //序号
    private String cardId; //账号
    private String userName; //用户名
    private double money; //金额

    public Bank() {

    }

    public Bank(int id, String cardId, String userName, double money) {
        this.id = id;
        this.cardId = cardId;
        this.userName = userName;
        this.money = money;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public String getUserName() {
        return userName;
    }

public void setUserName(String userName) {
    this.userName = userName;
}

public double getMoney() {
    return money;
}

public void setMoney(double money) {
    this.money = money;
}

@Override
public String toString() {
    return "Bank{" +
            "id=" + id +
            ", cardId='" + cardId + '\'' +
            ", userName='" + userName + '\'' +
            ", money=" + money +
            '}';
   }
}

2. 数据操作的dao层:

接口:

package com.zm.xmlTx.dao;

import com.zm.xmlTx.bean.Bank;

public interface BankDao {
    //根据序号查询账户信息
    public Bank findBankById(int id);
    //更新bank表中金额信息 转走
    public void updateFrom(int fromId, double money);
    //转入
    public void updateTo(int toId, double money);
}

实现类:

package com.zm.xmlTx.dao.impl;

import com.zm.xmlTx.autoException.BankException;
import com.zm.xmlTx.bean.Bank;
import com.zm.xmlTx.dao.BankDao;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

public class BankDaoImpl implements BankDao {

    private JdbcTemplate template;

    public void setTemplate(JdbcTemplate template) {
        this.template = template;
    }

    @Override
    public Bank findBankById(int id) {
        String sql = "select * from bank where id=?";
        Bank bank = template.queryForObject(sql, new BeanPropertyRowMapper<Bank>(Bank.class), id);
        return bank;
    }

    @Override
    public void updateFrom(int fromId, double money) {
        Bank bank = findBankById(fromId);
        double yu = bank.getMoney();
        if(yu<=0){
            throw new BankException("已经没有余额了");
        }else if(yu<money){
            throw new BankException("余额不足,无法执行!");
        }
        String sql = "update bank set money = money - ? where id = ?";
        int update = template.update(sql, money, fromId);
        System.out.println(update);
    }

    @Override
    public void updateTo(int toId, double money) {
        String sql = "update bank set money = money + ? where id = ?";
        int update = template.update(sql, money, toId);
        System.out.println(update);
    }
}

3. 业务逻辑层(service):

接口:

package com.zm.xmlTx.service;

public interface TransferDao {
    public void transferMoney(int fromId, int toId, double money);
}

实现类:

package com.zm.xmlTx.service.impl;

import com.zm.xmlTx.dao.BankDao;
import com.zm.xmlTx.service.TransferDao;

public class TransferImpl implements TransferDao {

    private BankDao bankDao;

    public void setBankDao(BankDao bankDao) {
        this.bankDao = bankDao;
    }

    @Override  //银行转账
    public void transferMoney(int fromId,int toId,double money) {
        /*A账户转向B账户*/

        //从A账户中扣除相应的金额
        bankDao.updateFrom(fromId,money);
        //B账户中增加相应的金额
        bankDao.updateTo(toId,money);
    }
}

4.xml文件(applicationContext-xml.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.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">

    <!--导入资源文件-->
    <context:property-placeholder location="classpath:db.properties"/>
    <!--配置druid数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="initialSize" value="${jdbc.initialSize}"/>
        <property name="minIdle" value="${jdbc.minIdle}"/>
        <property name="maxActive" value="${jdbc.maxActive}"/>
    </bean>

    <!--配置spring的JdbcTemplate-->
    <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--引入数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

<!--==================================================================================================-->
    <!--将需要实例化的Bean对象注入到Spring Ioc的容器中-->
    <bean id="bankDao" class="com.zm.xmlTx.dao.impl.BankDaoImpl">
        <property name="template" ref="template"/>
    </bean>

    <bean id="transfer" class="com.zm.xmlTx.service.impl.TransferImpl">
        <property name="bankDao" ref="bankDao"/>
    </bean>
<!--==============================================================================================-->
    <!--1. 配置事务管理器-->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <!--使用哪个数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--2. 配置事务属性-->
   <tx:advice transaction-manager="transactionManager" id="txAdvice">
        <tx:attributes>
        <!--
        根据方法名指定事务的属性(传播方式,是否只可读,隔离级别)
        propagation:设置事务的传播行为。有7中传播行为,默认值是REQUIRED
        isolation:设置事务的隔离级别。
        DEFAULT:可重复读(mysql)    READ_UNCOMITTED:读未提交
        READ_COMMITED:读已提交  REPEATABLE_READ   SERLALIZABLE
        read-only:是否只读。true只读   false不只读
        timeout:设置等待的响应时间。若超过了这个时间还未执行,则抛出异常回滚。

        * 所有方法,是一个通配符-->
            <!-- <tx:method name="*"/>-->
            <tx:method name="transferMoney" propagation="REQUIRES_NEW"  />
        </tx:attributes>
    </tx:advice>
    <!--3. 配置事务切入点,以及将事务切入点和事务属性关联起来-->
    <aop:config>
        <aop:pointcut expression="execution(* com.zm.xmlTx.service.impl.TransferImpl.*(..))"
                      id="txPointCut"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
</beans>