一、事务
事务防止进行操作时,数据库里面的数据丢失。
二、Spring事务注解方式
注解方式:在配置文件中配置事务管理器,利用注解,管理事务。
实例
银行转账:一个账户向另一个账户转账。
1)先在数据库mysql中建立,账户和余额表,并对其赋值:
2)在程序中创建一个完整的javabean程序(Account类),其属性和mysql表中属性一致:
package com.bank.pajo;
public class Account {
private String actno;
private Double balance;
@Override
public String toString() {
return "Account{" +
"actno='" + actno + '\'' +
", balance=" + balance +
'}';
}
public Account() {
}
public Account(String actno, Double balance) {
this.actno = actno;
this.balance = balance;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
}
2)引入依赖(pom.xml):
<packaging>jar</packaging>
<!--仓库-->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<!--依赖-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0-M2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!-- mysql jar包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!--这是一个数据库连接池,在此可以配置数据源信息-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
3)创建一个接口(Acount),用于存放对数据库进行操作的语句方法,查看和更新:
package com.bank.dao;
import com.bank.pajo.Account;
//只执行sql语句
public interface Acount {
//查询账户
Account selectBy(String actno);
//更新
int update(Account acc);
}
4)这个接口的实现类(AcountImp),将查看和修改的功能实现利用sql语句和JdbcTemplate:
package com.bank.dao;
import com.bank.pajo.Account;
import jakarta.annotation.Resource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class AcountImp implements Acount{
@Resource
private JdbcTemplate j;
@Override
public Account selectBy(String actno) {//查看账户
String sql="select actno,balance from t_act where actno=?";
Account a=j.queryForObject(sql,new BeanPropertyRowMapper<>(Account.class),actno);
return a;
}
@Override
public int update(Account acc) {//更新余额
String sql="update t_act set balance =? where actno=?";
int count= j.update(sql,acc.getBalance(),acc.getActno());
return count;
}
}
5)定义一个转账接口(AcountSer):
package com.bank.service;
public interface AcountSer {
void transfer(String fromActno,String toAcrno,double money);
}
6)这个接口的实现类,进行转账功能:
package com.bank.service;
import com.bank.dao.AcountImp;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.bank.pajo.Account;
@Service
public class AccountService implements AcountSer{
@Resource
private AcountImp acountImp;
//从这个账户转到另个账户
@Transactional//事务管理器
@Override
public void transfer(String fromActno, String toAcrno, double money) {
Account from= acountImp.selectBy(fromActno);//这个账户信息
if(from.getBalance()<money){
throw new RuntimeException("余额不足");
}
Account to=acountImp.selectBy(toAcrno);//另一个账户信息
from.setBalance(from.getBalance()-money);//一个钱数加
to.setBalance(to.getBalance()+money);//一个钱数减
//数据库更新
int count= acountImp.update(from);
//模拟异常
String s=null;
s.toString();
count+=acountImp.update(to);
if(count<2){
throw new RuntimeException("转账失败,请联系银行");
}
}
}
spring.xml:
<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.bank"></context:component-scan>
<!-- 配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring6"/>
<property name="username" value="root"/>
<property name="password" value="123456"></property>
</bean>
<!-- 配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器-->
<bean id="txMange" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 事务注解驱动器开启,开启事务-->
<tx:annotation-driven transaction-manager="txMange"></tx:annotation-driven>
</beans>
测试类:
public class Test {
@org.junit.Test
public void test(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
AcountSer a= applicationContext.getBean("accountService", AcountSer.class);
try{
a.transfer("act-001","act-002",10000);
}catch (Exception e){
System.out.println("转账失败");
e.printStackTrace();
}
}
}
成功案例(无模拟异常):
失败案例(有模拟异常) :刷新数据库的两个余额都未改变。
三、事务传播行为
重点为:REQUIRED和REQUIRES_NEW。
REQUIRES_NEW:事务A和事务B隔离开,若事务B发生异常,应对事务A进行捕捉异常,事务A就不会回滚,若不捕捉,则事务A发生回滚,数据无法保存。