Spring事务管理,xml和注解实现

目标:熟悉事务管理的核心接口,能够说出它的3个核心接口及内容

spring-tx-5.2.8.RELEAS依赖包的3个接口

  • PlatformTransactionManager接口:可以根据属性管理事务。
  • TransactionDefinition接口:用于定义事务的属性。
  • TransactionStatus接口:用于界定事务的状态。

1.PlatformTransactionManager接口

PlatformTransactionManager接口主要用于管理事务,该接口中提供了三个管理事物的方法。

 

在实际应用中,Spring事务管理实际是由具体的持久化技术完成的,而
PlatformTransactionManager接口只提供统一的抽象方法。为了应对不同持久化技术的差异性,Spring为他们提供了具体的实现类,例如,Spring为Spring JDBC和MyBatis等依赖于DataSource的持久化技术提供了实现类DataSourceTransactionManager,如此以来,Spring JDBC或MyBatis等持久化技术的事务管理可以由DataSourceTransactionManager类实现,而且Spring 可以通过PlatformTransactionManager接口对这些实现类进行统一管理。

2.TransactionDefinition接口

TransactionDefinition接口中定义了事务描述相关的常量,其中包括了事务的隔离级别、事务的传播行为、事务的超时时间和是否为只读事务。

事务的隔离级别

 

事务的传播行为

事务的传播行为是指处于不同事务中的方法在相互调用时,方法执行期间,事务的维护情况。例如,当一个事务的方法B调用另一个事务的方法A时,可以规定A方法继续在B方法所属的现有事务中运行,也可以规定A方法开启一个新事务,在新事务中运行,B方法所属的现有事务先挂起,等A方法的新事务执行完毕后再恢复。

TransactionDefinition接口中定义的7种事务传播行为

事务的超时时间

事务的超时时间是指事务执行的时间界限,超过这个时间界限,事务将会回滚。TransactionDefinition接口提供了TIMEOUT_DEFAULT常量定义事务的超时时间。 

是否为只读事务

当事务为只读时,该事务不修改任何数据,只读事务有助于提升性能,如果在只读事务中修改数据,会引发异常。TransactionDefinition接口中除了提供事务的隔离级别、事务的传播行为、事务的超时时间和是否为只读事务的常量外,还提供了一系列方法来获取事务的属性。

TransactionDefinition接口常用方法

3.TransactionStatus接口 

TransactionStatus接口主要用于界定事务的状态,通常情况下,编程式事务中使用该接口较多。TransactionStatus接口提供了一系列返回事务状态信息的方法,具体如下。

熟悉事务管理的方式,能够说出Spring事务管理的两种方式分别是什么 

Spring中的事务管理分为两种方式,一种是传统的编程式事务管理,另一种是声明式事务管理。

  • 编程式事务管理:通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚。
  • 声明式事务管理:通过AOP技术实现的事务管理,其主要思想是将事务管理作为一个“切面”代码单独编写,然后通过AOP技术将事务管理的“切面”代码植入到业务目标类中。

编写式事务管理有点落后,一般使用第二种,声明式事务管理

实现XML方式的声明式事务

没有配置事务具体实现代码:

这是一开始表中的数据:

 现在模拟zhangsan给lisi转账100元

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>_20230319</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.6</version>
        </dependency>
        <!--        事务相关-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.3.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
    </dependencies>

</project>

 AccountDaoImpl:

package cn.hdc.dao.impl;

import cn.hdc.dao.AccountDao;
import cn.hdc.model.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.util.List;

public class AccountDaoImpl implements AccountDao {
    private JdbcTemplate jdbcTemplate;

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public Integer addAccount(Account account) {
        String sql = "insert into account(username,balance) values(?,?)";
        Object[] params = new Object[]{account.getUsername(), account.getBalance()};
        jdbcTemplate.update(sql, params);
        return jdbcTemplate.update(sql, params);
    }

    @Override
    public Integer deleteAccount(Integer id) {
        String sql = "delete from account where id = ?";
        return jdbcTemplate.update(sql, id);
    }

    @Override
    public Integer updateAccount(Account account) {
        String sql = "update account set username=?,balance=? where id=?";
        Object[] params = new Object[]{account.getUsername(), account.getBalance(), account.getId()};
        return jdbcTemplate.update(sql, params);
    }

    @Override
    public Account findAccountById(Integer id) {
        String sql = "select * from account where id = ?";
        RowMapper<Account> accountRowMapper = new BeanPropertyRowMapper<>(Account.class);
        Account account = jdbcTemplate.queryForObject(sql, accountRowMapper, id);
        return jdbcTemplate.queryForObject(sql, accountRowMapper, id);
    }

    @Override
    public List<Account> findAll() {
        String sql = "select * from account";
        RowMapper<Account> accountRowMapper = new BeanPropertyRowMapper<>(Account.class);
        List<Account> list = jdbcTemplate.query(sql, accountRowMapper);
        return list;
    }

    //模拟转账操作
    @Override
    public void transfer(String outUser, String inUser, Double money) {
        //收款
        jdbcTemplate.update("update account set balance = balance + ? where username = ?", money, inUser);
        //模拟异常
        int i = 10 / 0;
        //付款
        jdbcTemplate.update("update account set balance = balance - ? where username = ?", money, outUser);
    }
}

AccountDao接口:

package cn.hdc.dao;

import cn.hdc.model.Account;

import java.util.List;

public interface AccountDao {
    public Integer addAccount(Account account);

    public Integer deleteAccount(Integer id);

    public Integer updateAccount(Account account);

    public Account findAccountById(Integer id);

    public List<Account> findAll();

    public void transfer(String outUser, String inUser, Double money);
}

Account实体类:

package cn.hdc.model;

public class Account {
    private Integer id;
    private String username;
    private Double balance;

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", balance=" + balance +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Double getBalance() {
        return balance;
    }

    public void setBalance(Double balance) {
        this.balance = balance;
    }
}

测试类:

import cn.hdc.dao.AccountDao;
import cn.hdc.dao.impl.AccountDaoImpl;
import cn.hdc.model.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

public class APP {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
        AccountDao accountDao = (AccountDao) context.getBean("accountDao");
        accountDao.transfer("zhangsan", "lisi", 100.0);
        
//        List<Account> list = accountDao.findAll();
//        list.forEach(account -> {
//            System.out.println(account);
//        });

//        Account account = accountDao.findAccountById(1);
//        System.out.println(account);

//        Integer ret = accountDao.deleteAccount(2);
//        if (ret > 0) {
//            System.out.println("删除成功!");
//        } else {
//            System.out.println("删除失败!");
//        }
//        Account account1 = new Account();
//        account1.setId(2);
//        account1.setUsername("zhangsan");
//        account1.setBalance(5000.02153);
//        Integer ret = accountDao.updateAccount(account1);
//        if (ret > 0) {
//            System.out.println("修改成功!");
//        } else {
//            System.out.println("修改失败!");
//        }

//        Account account = new Account();
//        account.setUsername("tom");
//        account.setBalance(1000.011);
//        Integer ret = accountDao.addAccount(account);
//        if (ret > 0) {
//            System.out.println("插入成功!");
//        } else {
//            System.out.println("插入失败!");
//        }


//        jdbcTemplate.execute("create table account" +
//                "(" +
//                "    id int primary key auto_increment," +
//                "    username varchar(50)," +
//                "    balance double" +
//                ");");
//        System.out.println("account表创建成功!");


        //        UserDaoImpl userDao = (UserDaoImpl) context.getBean("userDao");
        //        System.out.println(userDao.getJdbcTemplate());
    }
}

运行结果:

看一下表数据:

 

 张三的钱没扣!

接下来我们使用事务

具体实现代码:

现在还原一下表

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>_20230319</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.6</version>
        </dependency>
        <!--        事务相关-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.3.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
    </dependencies>

</project>

 

AccountDao接口:

package cn.hdc.dao;

import cn.hdc.model.Account;

import java.util.List;

public interface AccountDao {
    public Integer addAccount(Account account);

    public Integer deleteAccount(Integer id);

    public Integer updateAccount(Account account);

    public Account findAccountById(Integer id);

    public List<Account> findAll();

    public void transfer(String outUser, String inUser, Double money);
}

AccountDaoImpl

package cn.hdc.dao.impl;

import cn.hdc.dao.AccountDao;
import cn.hdc.model.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.util.List;

public class AccountDaoImpl implements AccountDao {
    private JdbcTemplate jdbcTemplate;

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public Integer addAccount(Account account) {
        String sql = "insert into account(username,balance) values(?,?)";
        Object[] params = new Object[]{account.getUsername(), account.getBalance()};
        jdbcTemplate.update(sql, params);
        return jdbcTemplate.update(sql, params);
    }

    @Override
    public Integer deleteAccount(Integer id) {
        String sql = "delete from account where id = ?";
        return jdbcTemplate.update(sql, id);
    }

    @Override
    public Integer updateAccount(Account account) {
        String sql = "update account set username=?,balance=? where id=?";
        Object[] params = new Object[]{account.getUsername(), account.getBalance(), account.getId()};
        return jdbcTemplate.update(sql, params);
    }

    @Override
    public Account findAccountById(Integer id) {
        String sql = "select * from account where id = ?";
        RowMapper<Account> accountRowMapper = new BeanPropertyRowMapper<>(Account.class);
        Account account = jdbcTemplate.queryForObject(sql, accountRowMapper, id);
        return jdbcTemplate.queryForObject(sql, accountRowMapper, id);
    }

    @Override
    public List<Account> findAll() {
        String sql = "select * from account";
        RowMapper<Account> accountRowMapper = new BeanPropertyRowMapper<>(Account.class);
        List<Account> list = jdbcTemplate.query(sql, accountRowMapper);
        return list;
    }

    //模拟转账操作
    @Override
    public void transfer(String outUser, String inUser, Double money) {
        //收款
        jdbcTemplate.update("update account set balance = balance + ? where username = ?", money, inUser);
        //模拟异常
        int i = 10 / 0;
        //付款
        jdbcTemplate.update("update account set balance = balance - ? where username = ?", money, outUser);
    }
}

Account实体类:

package cn.hdc.model;

public class Account {
    private Integer id;
    private String username;
    private Double balance;

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", balance=" + balance +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Double getBalance() {
        return balance;
    }

    public void setBalance(Double balance) {
        this.balance = balance;
    }
}

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

       xmlns:tx="http://www.springframework.org/schema/tx" 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/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/cache
       http://www.springframework.org/schema/cache/spring-cache.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 配置一下:bean注解扫描的根路径(方面后面更简单存储对象到spring容器)-->
    <!--    <content:component-scan base-package="cn.hdc"></content:component-scan>-->

    <!--    定义数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="666666"></property>
    </bean>

    <!--    定义jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--    普通类-->
    <bean id="userDao" class="cn.hdc.dao.impl.UserDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <!--    accountDao类-->
    <bean id="accountDao" class="cn.hdc.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <!--    事务管理对象,注意:id必须叫做 transactionManager-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--    xml事务配置-->
    <!--    事务通知相关的属性配置-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" read-only="false" isolation="DEFAULT" timeout="-1"/>
        </tx:attributes>
    </tx:advice>

    <!--    事务的aop-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* cn.hdc.dao.impl.AccountDaoImpl.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"></aop:advisor>
    </aop:config>
</beans>

 

测试类:

import cn.hdc.dao.AccountDao;
import cn.hdc.dao.impl.AccountDaoImpl;
import cn.hdc.model.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

public class APP {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
        AccountDao accountDao = (AccountDao) context.getBean("accountDao");
        accountDao.transfer("zhangsan", "lisi", 100.0);

//        List<Account> list = accountDao.findAll();
//        list.forEach(account -> {
//            System.out.println(account);
//        });

//        Account account = accountDao.findAccountById(1);
//        System.out.println(account);

//        Integer ret = accountDao.deleteAccount(2);
//        if (ret > 0) {
//            System.out.println("删除成功!");
//        } else {
//            System.out.println("删除失败!");
//        }
//        Account account1 = new Account();
//        account1.setId(2);
//        account1.setUsername("zhangsan");
//        account1.setBalance(5000.02153);
//        Integer ret = accountDao.updateAccount(account1);
//        if (ret > 0) {
//            System.out.println("修改成功!");
//        } else {
//            System.out.println("修改失败!");
//        }

//        Account account = new Account();
//        account.setUsername("tom");
//        account.setBalance(1000.011);
//        Integer ret = accountDao.addAccount(account);
//        if (ret > 0) {
//            System.out.println("插入成功!");
//        } else {
//            System.out.println("插入失败!");
//        }


//        jdbcTemplate.execute("create table account" +
//                "(" +
//                "    id int primary key auto_increment," +
//                "    username varchar(50)," +
//                "    balance double" +
//                ");");
//        System.out.println("account表创建成功!");


        //        UserDaoImpl userDao = (UserDaoImpl) context.getBean("userDao");
        //        System.out.println(userDao.getJdbcTemplate());
    }
}

运行结果:

 

表中数据没有变化,事务发生了回滚。 

基于注解方式的声明式事务

@Transactional的属性

 

AccountDaoImpl在原来的基础上加上注解

 

applicationContext.xml

 运行结果:

 

表中数据没有发生改变,事务生效

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿瞒有我良计15

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值