SSM框架学习——Spring事务管理

Spring事务管理

概念

事务(Transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么都不执行,是一个不可分割的工作单元

事务有如下特性:

  • 原子性
  • 隔离性
  • 一致性
  • 持久性

如果多个事务同时操作同一批数据,则会引发并发异常,设置不同的隔离级别可以解决这些问题。事务的隔离级别如下

隔离界别从小到大,安全性越高,但效率就越低。

事务的传播行为是指在同一个方法中,不同操作前后所使用的事务。传播行为可以控制是否需要创建事务以及如何创建事务,Spring默认传播行为是REQUIRED。

事务的管理方式主要分为两种:

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

为了解耦,我们一般用后者。

声明式事务管理

基于XML

Spring的声明式事务管理可以通过两种方式来实现,一种是基于XML的方式,另一种是基于注解的方式。

基于XML方式的声明式事务是在配置文件中通过<tx:advice>元素配置事务规则来实现的。当配置了事务的增强处理后,就可以通过编写的AOP配置,让Spring自动对目标生成代理。其属性如下。

基于注解

我们更关心基于注解的方式,下面用代码来了解下

我们用之前的top.cairbin.test3项目

app.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/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">

	<!-- 指定需要扫描的包,使注解生效 -->
	<context:component-scan
		base-package="top.cairbin.test3" />
	<!-- 配置dataSource -->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<!--数据库驱动 -->
		<property name="driverClassName"
			value="com.mysql.jdbc.Driver" />
		<!--连接数据库的url -->
		<property name="url"
			value="jdbc:mysql://localhost:3306/db_javaee" />
		<!--连接数据库的用户名 -->
		<property name="username" value="db_javaee" />
		<!--连接数据库的密码 -->
		<property name="password" value="dbjavaeepassword" />
	</bean>
	<!-- 2配置JDBC模板 -->
	<bean id="jdbcTemplate"
		class="org.springframework.jdbc.core.JdbcTemplate">
		<!-- 默认必须使用数据源 -->
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	
	<bean id="transactionManager" class=
     "org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>	
    <!-- 注册事务管理器驱动 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

IAccountDao中声明方法

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

AccountDao类中添加方法及注解

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
public void transfer(String outUser, String inUser, Double money) {
    // 收款时,收款用户的余额=现有余额+所汇金额
    this.jdbcTemplate.update("update account set balance = balance +? "
            + "where username = ?",money, inUser);
    // 模拟系统运行时的突发性问题
    int i = 1/0;
    // 汇款时,汇款用户的余额=现有余额-所汇金额
    this.jdbcTemplate.update("update account set balance = balance-? "
            + "where username = ?",money, outUser);
}

JdbcTemplateTest测试类中添加测试方法

@Test
public void transTest(){		
    accountDao.transfer("lisi", "zhangsan", 100.0);
    // 输出提示信息
    System.out.println("转账成功!");
}

运行程序前向数据库中插入两条记录,我们使用终端或者sqlyog连接数据库,我这里以Mac的终端为例

mysql -u db_javaee -p

注意这里,我们用的不再是root账户,而是之前创建的db_javaee

输入密码dbjavaeepassword,如果你之前设置的与我不一样这里换成你的密码

输入密码的时候是不显示密码内容的,敲击回车,如果界面跟我下面差不多就说明成功了

后面来写SQL语句,首先把数据库切换到db_javaee下面

USE db_javaee;

如果出现Database changed提示则表示成功。

我们先来查看下之前创建的Account

SELECT * FROM account;

这是我的表里面的内容,你的应该与我不太一样,不过不影响接下来的操作。

我们使用一下语句来插入两条数据

INSERT INTO account(id,username,balance) VALUES(17,'zhangsan',100);
INSERT INTO account(id,username,balance) VALUES(18,'lisi',200);

成功的话会有提示

来看下目前表里的数据

SELECT * FROM account;

接下来我们回到Eclipse IDE,运行我们的代码

运行结果如下

不出意外的话该出意外了,注意看左边的transTest()方法竟然报错,那是正常的,因为我们public void transfer(String outUser, String inUser, Double money);代码写了int i = 1/0;这种语句来模拟报错。

我们到数据库中查看数据

SELECT * FROM account;

很显然数据没有啥变化,我们将transfer方法中的int i = 1/0;删掉试试。注意方法名称,别搞错了。

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
public void transfer(String outUser, String inUser, Double money) {
    // 收款时,收款用户的余额=现有余额+所汇金额
    this.jdbcTemplate.update("update account set balance = balance +? "
            + "where username = ?",money, inUser);
    // 模拟系统运行时的突发性问题
    // int i = 1/0;
    // 汇款时,汇款用户的余额=现有余额-所汇金额
    this.jdbcTemplate.update("update account set balance = balance-? "
            + "where username = ?",money, outUser);
}

运行一下,发现控制台输出转账成功!

回到数据库查看下

SELECT * FROM account;

下图是操作前后两次的对比,上面是转账成功之前的,后面是转账成功之后的。

数据显然是发生变化了!

问题

到了这里,你可能会有疑问,敲了这么多的代码,我们还不断地进行测试、查看数据库,到底是想干什么。

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
public void transfer(String outUser, String inUser, Double money) {
    // 收款时,收款用户的余额=现有余额+所汇金额
    this.jdbcTemplate.update("update account set balance = balance +? "
            + "where username = ?",money, inUser);
    // 模拟系统运行时的突发性问题
    int i = 1/0;
    // 汇款时,汇款用户的余额=现有余额-所汇金额
    this.jdbcTemplate.update("update account set balance = balance-? "
            + "where username = ?",money, outUser);
}

我们看看上方的代码。**在前面提到过事务的特性是原子的,也就是不可分割的。**倘若我们没有事务,当this.jdbcTemplate.update(...);这条语句执行的时候,数据库就已经更新了,而int i = 1/0;却出现了一个突发性问题,按理来说我们转账transfer这一方法不应该成立才对,而数据库中的记录的确发生了变动。

如果你是一个恶意用户,然后你发现了这一漏洞,向银行存款,每次存款失败代码异常银行都退回你钱,然后你不断重复这个操作就可以使你的账户余额不断增加,然而你实际上并没有存一分钱进去,这显然是不合理的!

然而我们有了事务,就不会发生这种情况了,因为对于transfer这一操作是不可分割成小操作的,也就是说对于方法里面的东西要么全成功,要么全失败回滚,不可能出现部分成功部分失败这种情况。

结束

Spring初步这一部分就到此为止了,你会发现对于数据库操作在代码里嵌入SQL语句是很麻烦而且不可靠的,如果不做好过滤,用户提交一段包含SQL语句的字符串就有可能利用你的权限操作数据库,还会有安全问题,所以接下来我们将学习MyBatis这个框架。

  • 13
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring声明式事务管理包括以下几个步骤:1. 在 Spring 配置文件中配置事务管理器;2. 使用 @Transactional 注解将需要事务管理的方法标注;3. 调用该方法,Spring 将自动开启事务并在事务结束时自动提交或回滚事务。 ### 回答2: Spring声明式事务管理的使用步骤如下: 1. 配置事务管理器:在Spring的配置文件中,需要配置一个事务管理器,例如使用Spring提供的JtaTransactionManager、DataSourceTransactionManager等。事务管理器负责管理事务的开始、提交、回滚等操作。 2. 配置事务通知:在需要进行事务管理的方法上添加事务通知的注解,通常使用@Transactional注解。该注解可以添加在类级别或方法级别上,表示该类或方法需要进行事务管理。 3. 配置事务属性:通过@Transactional注解的属性来配置事务的特性,包括事务的隔离级别、传播行为、只读属性等。事务的隔离级别可以通过设置Isolation枚举类的值来指定,默认为DEFAULT,传播行为可以通过Propagation枚举类的值来指定,默认为REQUIRED。 4. 启用事务管理:在Spring的配置文件中,需要启用事务管理,可以通过配置tx命名空间或使用@EnableTransactionManagement注解来开启事务功能。 5. 在需要进行事务管理的方法中进行操作:在需要进行事务管理的方法中,可以通过调用其他方法或访问数据库等进行操作,当方法执行结束时,如果发生异常,则事务会自动回滚,否则会提交事务。 总结: Spring声明式事务管理使用步骤包括配置事务管理器、配置事务通知、配置事务属性、启用事务管理和在需要进行事务管理的方法中进行操作。通过这些步骤,我们可以简化事务管理的代码,并且使得事务管理更加便捷和高效。 ### 回答3: Spring声明式事务管理是通过使用@Transactional注解来实现的。下面是使用Spring声明式事务管理的步骤: 1. 配置数据源:在Spring的配置文件中,配置数据源,可以使用JDBC或者连接池来配置数据源。 2. 配置事务管理器:在Spring的配置文件中,配置事务管理器,可以使用Spring自带的事务管理器,如DataSourceTransactionManager或者HibernateTransactionManager。 3. 配置事务的属性:通过在需要进行事务管理的方法上添加@Transactional注解来指定事务的传播行为、隔离级别、超时时间等属性。 4. 启用事务支持:在Spring的配置文件中,添加<tx:annotation-driven/>配置,开启对@Transactional注解的支持。 5. 使用事务管理的方法:在需要进行事务管理的方法上添加@Transactional注解,表示这个方法需要在事务控制下执行。 6. 异常处理:在事务管理的方法中,如果发生了异常,Spring会根据事务的属性进行相应的处理,如回滚事务或者提交事务。 通过以上步骤,我们可以使用Spring声明式事务管理来实现对数据库事务管理。使用声明式事务管理可以减少编码量,提高代码的可维护性,同时也使得事务管理的配置更加简洁明了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值