Spring---事务管理

事务操作

事务是什么?

事务是对数据库操作时最基本的单元,在逻辑上一组操作,要么都成功要么都失败。只要有一个失误就都失败

事务的特性(ACID):

例子:你要给我转账一百块

原子性:在给我转账的过程中,如果你少了一百,但是由于网络原因或别的原因我并没有多出一百。事务失败。你的一百会被退回。整个过程不能分割开。要么你少一百,我多一百,成功。要么失败。

一致性:在转帐前你有一百,我没有钱,转账后,我一百你没有钱。总量不变。就是一致性。

隔离性:当同时有你和别人都给我转一百,这是好几个事务,你转账给我一百块,这个事务不会受到其他事务的影响。

持久性:当这一组事务【你给我转钱】,真正提交成功。数据库表中的数据会发生变化。假如现在你转账工具炸了,数据库中的数据也不会发生变化。

 

使用

首先在xml文件中引入命名空间、开启组件扫描、引入外部文件、给DataSource注入属性、创建JDBCTemplate建立bean对象并将datasource对象注入jdbctemplate。

 

创建一个数据库表、插入数据

create table o_tx(
userid int primary key auto_increment,
username varchar(30) not null,
usermoney Decimal(30,3)
​
)
insert into o_tx values(null,"狼",1000.02),(null,"羊",1000.02)

 

 

创建Dao接口和实现类、创建Service

创建bean对象

@Component
public interface TxDao {
    public void addMoney();
    public void reduceMoney();
}
 

 

创建bean对象、注入JDBCTemplate属性

@Repository
public class TxDaoImpl implements TxDao {
​
    @Autowired
    private JdbcTemplate jdbcTemplate;
​
    @Override
    public void addMoney() {
        String sql = "update o_tx set usermoney=usermoney+100.001 where username=?";
        int update = jdbcTemplate.update(sql, "狼");
        System.out.println(update);
    }
​
    @Override
    public void reduceMoney() {
        String sql = "update o_tx set usermoney=usermoney-100.001 where username=?";
        int update = jdbcTemplate.update(sql, "羊");
        System.out.println(update);
    }
}

 

 

创建bean对象、注入Dao属性

@Service
public class TxService {
    @Autowired
    private TxDao txDao;
    public void changeMoney(){
        txDao.reduceMoney();
        txDao.addMoney();
    }
}
 

 

事务管理场景引入

当上面的转账操作中出现了错误,就会导致程序执行不完全,突然停下来。已经执行的操作会把结果提交给数据库。这样就会出现你的钱少了,我的钱没多。钱消失了。为了避免这种情况,实现只有你的钱少了并且我的钱多了才能够将结果一起提交给数据库,所以需要引入事务管理操作

 

Spring事务管理介绍

1、一般将事务添加到service层中

2、Spring进行事务管理操作有两种方式:编程式事务管理和声明式事务管理【一般都用这个】

3、声明式事务管理两种方式:【xml文件】【注解方式】

4、声明式事务管理底层使用AOP原理实现

5、Spring中提供了一个接口干的是事务管理器的事【PlatformTransactionManager】,他针对不同框架有不同的实现类

 

声明式事务管理的使用

1、创建DataSourceTransactionManager的bean对象【事务管理器】并向里面以set方法的方式注入dataSource属性【注入数据源】

<!--创建TransactionManager事务管理器的bean对象,并注入数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="druid"></property>
</bean>

 

 

部分DataTranscationManager源码

public DataSourceTransactionManager(DataSource dataSource) {
    this();
    this.setDataSource(dataSource);
    this.afterPropertiesSet();
}
​
public void setDataSource(@Nullable DataSource dataSource) {
    if (dataSource instanceof TransactionAwareDataSourceProxy) {
        this.dataSource = ((TransactionAwareDataSourceProxy)dataSource).getTargetDataSource();
    } else {
        this.dataSource = dataSource;
    }
​
}

 

 

2、添加命名空间tx,以标签【tx:annotation-driven】开启事务管理注解,以属性【transaction-manager】指定事务管理器

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       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">
    
    
    <!--开启事务注解并指定使用的事务管理器开启注解管理-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

 

 

 

3、在service层中添加注解,并实验效果

注解@Transactional可以添加到类上面这样类里面的方法都能进行事务管理操作 @Transactional可以添加到方法上,方法内部就能使用事务管理

@Service
//注解@Transactional可以添加到类上面这样类里面的方法都能进行事务管理操作
//@Transactional可以添加到方法上,方法内部就能使用事务管理
@Transactional
public class TxService {
    @Autowired
    private TxDao txDao;
    public void changeMoney(){
        txDao.reduceMoney();
        int i = 1/0;
        txDao.addMoney();
    }
}

 

 

TransactionManager参数

 

 

 

1、propagation【事务的传播行为】:

是用来处理多个事务之间调用产生的问题。场景:多个事务方法互相调用,这些方法中至少有一个方法有@Transactional注解,当有方法调用这个使用了注解的方法或别的方法调用了这个有注解的方法。如哈处理这两个方法之间调用产生的事务问题,就叫事物的传播行为。他有7个状态【值】。默认是REQUEIRED

2、isolation【事务隔离级别】:

是为了解决事务之间的隔离性产生的。如果不考虑隔离性的问题就会产生读的问题:

【脏读】:一个未提交的事务读到了另一个未提交的事务的数据

场景:有两个人想同时对数据库中的同一条数据进行操作,这两个人都有事务管理,当A更改数据内容后,B读到了这个更改后的数据,就根据更改后的数据进行B本该进行的操作,但是A没有提交事务,它回滚了事务,数据恢复了原样,B却提交了事务【或进行了其他操作】,这就叫脏读。不可以出现。

【虚(幻)读】:一个未提交事务读取到了另一个提交事务的添加数据

【不可重复读】:一个未提交事务读取到了另一个提交事务的修改数据

当需要对数据进行统计时,A读到了B没有提交的数据,同时A读到了B的提交结果的数据,就会使统计结果不准确。这是一种现象,需要去避免出现。

事务隔离级别

设置隔离级别避免这种现象Serializable就会杜绝这三个情况的发生,使用mysql默认级别是【REPEATABLE_READ】,不会发生脏读、不可重复读。会发生幻读

3、timeOut事务超时时间

设置事务提交时间,当超出了规定时间进行回滚操作。默认值是-1,在进行设置时,单位是秒

4、readOnly【是否只读】

读:查询操作 写:增删改操作

readOnly默认值是false,当设置true后只能查询

5、rollbackFor【回滚】

设置出现异常时进步进行回滚。【属性】=【异常.class】

6、noRollbackFor【不回滚】

设置出现哪些异常不进行回滚。

基于xml文件进行声明式事务管理

【开启组件扫描】、【添加aop命名空间】、【引入外部数据库配置文件】、【创建druidDatasource bean对象并属性注入】、【创建JDBCTemlate对象和注入数据源】、【创建事务管理器bean对象并注入数据源】、以标签tx:advice【配置通知】、以标签aop:config【设置切入点和进行切面】

通过xml文件方式就能很好的理解事务管理底层使用AOP这句话。

xml文件中

<!--配置通知-->
<tx:advice id="txTransaction">
    <!--配置事务参数,要将事务加在哪个方法上-->
    <tx:attributes>
        <!--配置事务规范,指定事务在哪个方法上-->
        <!--可以指定在哪一类方法上加事务-->
        <tx:method name="change*"/>
        <tx:method name="changeMoney"/>
    </tx:attributes>
</tx:advice><!--配置切入点和切面-->
<aop:config>
    <!--设置切入点-->
    <aop:pointcut id="op" expression="execution(* xlw.com.jdbcTemplate.service.TxService.*(..))"/>
    <!--在切入点增加事务-->
    <aop:advisor advice-ref="txTransaction" pointcut-ref="op"></aop:advisor>
</aop:config>

 

 

事务管理完全注解开发

配置类

在引入外部配置文件后使用@Value注解对参数进行调用,使用set方法给属性赋值

在创建连接池时已经创建了bean实例,在后面需要用到bean对象进行注入时,在方法中以参数的形式引入bean对象,只需要去IOC容器中根据类型找对应的bean对象。不需要重复调用方法,重复产生bean对象。

//声明配置类
@Configuration
//开启组件扫描
@ComponentScan(basePackages = "xlw.com.jdbcTemplate")
//开启事务
@EnableTransactionManagement
//引入外部配置文件
@PropertySource("jdbc.properties")
public class TxConfig {
​
    @Value("${druid.driverClassName}")
    private String driverClassname;
    @Value("${druid.url}")
    private String url;
    @Value("${druid.username}")
    private String userName;
    @Value("${druid.password}")
    private String passWord;
​
    @Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource druid = new DruidDataSource();
        druid.setDriverClassName(driverClassname);
        druid.setUrl(url);
        druid.setUsername(userName);
        druid.setPassword(passWord);
        return druid;
    }
​
//    由于是使用的SpringIOC容器,所以在后面需要用到数据库连接池的类的对象时,
//    不必重复调用第一个方法。使用参数方式去IOC容器中找到这一个已经生成的bean对象
​
    @Bean
    public JdbcTemplate getJDBCTemplate(DruidDataSource druid){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(druid);
        return jdbcTemplate;
    }
​
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DruidDataSource druid){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(druid);
        return dataSourceTransactionManager;
    }
​
}

 

 

 

 

posted @ 2021-11-15 12:12  优质水  阅读(50)  评论(0)    收藏  举报