Spring05进阶-07篇

81 阅读5分钟

学习插图.jpg

前篇

事务操作(事务概念)

  1. 什么是事务?

    事务是数据库操作中最基本的单元,逻辑上的一组操作,要么都成功,如果有一个失败,所有操作都失败。

  2. 事务的四个特性(ACID)

    原子性:表示一个事务是一个整体,不可分割,要么都成功,要么都失败。

    一致性:表示事务操作前后数据总量一致

    隔离性:表示多个事务之间互不影响

    持久性:表示数据改变之后,这个变化是永久性的。

事务操作(搭建事务操作环境)

2023-01-31_205306.jpg

  1. 创建数据库表,添加记录

2023-01-31_205400.jpg

  1. 连接数据库,创建service、dao,并完成对象的注入

(1)添加相关jar包,看JdbcTemplate篇

(2)配置

<?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: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
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:component-scan base-package="com.atguigu"/>

<!--    外部文件引入-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

<!--    配置数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driverClass}"/>
        <property name="url" value="${prop.url}"/>
        <property name="username" value="${prop.username}"/>
        <property name="password" value="${prop.password}"/>
     </bean>

<!--    创建JdbcTemplate对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>
public interface UserDao {

//    多钱
    void addMoney();

//    少钱
    void reduceMoney();
}
@Repository
public class UserDaoImpl implements UserDao{

    @Autowired
    private JdbcTemplate jdbcTemplate;

    //    marry多100
    @Override
    public void addMoney() {
        String sql = "update t_account set money = money+? where username=?";
        jdbcTemplate.update(sql,100,"marry");
    }

    //    lucy少100
    @Override
    public void reduceMoney() {
        String sql = "update t_account set money = money-? where username=?";
        jdbcTemplate.update(sql,100,"lucy");
    }
}
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

//    转账操作(lucy给marry转账100)
    public void accountMoney(){
//        lucy少100
        userDao.reduceMoney();
//        marry多100
        userDao.addMoney();
    }
}

测试

@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.accountMoney();
}

上面的代码,如果正常执行就没有问题,但是如果代码在执行的过程中发生问题,业务操作就会有问题

2023-01-31_211228.jpg

那上面的问题如何解决呢?

使用事务解决!

事务的操作过程

2023-01-31_211431.jpg

事务操作(Spring事务管理介绍)

  1. 事务添加到JavaEE三层结构中的Service业务逻辑层

  2. 在Spring中进行事务管理操作

    (1)有两种方式:编程式事务管理和声明式事务管理(使用)

  3. 声明式事务管理

    (1)基于注解实现(使用)

    (2)基于XML配置文件方式实现

  4. 在Spring中进行声明式事务管理,底层使用AOP原理

  5. Spring事务管理API

    (1)Spring提供一个接口,代表事务管理器,这个接口针对不同的接口提供不同的实现类

2023-01-31_213431.jpg

事务操作(声明式事务管理-注解方式)

  1. 在Spring配置文件中配置事务管理器
<!--    配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--        注入数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

2.在Spring配置文件中,开启事务注解

(1)在Spring配置文件中,添加tx名称空间

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

(2)开启事务注解

<!--    配置开启注解事务管理-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

(3)在Service类(或者service类中的方法上面)添加事务注解

(1)@Transactional,这个注解添加到类上面,也可以添加方法上面
(2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务
(3)如果把这个注解添加方法上面,为这个方法添加事务
@Service
@Transactional
public class UserService {

事务操作(声明式事务管理参数配置)

1.在service类上面添加注解@Transactional,在这个注解里面可以配置事务的相关参数

2023-02-01_164020.jpg

2. propagation:事务传播行为

事务传播行为中关注的是:当一个事务方法被另一个事务方法调用的时候,这个事务方法如何进行

2023-02-01_164538.jpg

remark: Required_new 的方式就是:方法B会在自己的事务中进行,与事务A不产生任何联系,即使方法A在调用完了方法B执行后续代码产生异常后,事务B不会受到影响,依然会正常提交。(就是两个事务互不干扰)

下图中"当前的方法"指的是方法B

2023-02-01_164703.jpg remark:supports 的方式就是:方法B单独时如果运行在事务中,方法A调用方法B时,方法A依旧运行在事务A中,否则在被方法A调用时就不会运行在任何事务中,包括事务A中。

2023-02-01_165055.jpg

事务的传播行为可以由传播属性指定,Spring定义了7种类传播行为:

2023-02-01_165648.jpg

2023-02-01_165858.jpg

3. isolation:隔离级别

事务有个特性称为隔离性,多事务操作之间不会产生影响,如果不考虑隔离性会产生很多问题。

(1)如果不考虑事务的隔离性,会产生三个读的问题:脏读、不可重复读、幻(虚)读

脏读:一个未提交的事务读到另一个未提交事务的数据。

2023-02-01_172859.jpg

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

2023-02-01_173306.jpg

虚读:一个未提交事务读取到另一提交事务添加数据

解决上述三个读的问题:通过设置事务的隔离级别

2023-02-01_173705.jpg

2023-02-01_173751.jpg

4. timeout:超时时间

(1)事务在一定时间内会进行提交,如果不进行提交就会进行回滚 (2)默认值是-1,也就是不超时,设置的时间以秒单位进行计算

5.readOnly: 是否只读

(1)读:查询操作;写:增删改操作

(2)readOnly默认值是false,表示可以读,也可以增加删除修改

(3)设置readOnly的值是true后,表示只能查询。

6.rollbackFor:回滚

(1)设置出现哪些异常进行事务回滚

7.norollbackFor:不回滚

(1)设置出现哪些异常不进行事务回滚

事务操作(XML声明式事务管理)

  1. 在配置文件中进行配置

    第一步 配置事务管理器

    第二步 配置通知

    第三步 配置切入点和切面

<!--1 创建事务管理器-->

<bean id="transactionManager"

class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<!--注入数据源-->

<property name="dataSource" ref="dataSource"></property>

</bean>

<!--2 配置通知-->

<tx:advice id="txadvice">

<!--配置事务参数-->

<tx:attributes>

<!--指定哪种规则的方法上面添加事务-->

<tx:method name="accountMoney" propagation="REQUIRED"/>

<!--<tx:method name="account*"/>-->

</tx:attributes>

</tx:advice>

<!--3 配置切入点和切面-->

<aop:config>

<!--配置切入点-->

<aop:pointcut id="pt" expression="execution(*

com.atguigu.spring5.service.UserService.*(..))"/>

<!--配置切面-->

<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>

</aop:config>

事务操作(完全注解声明式事务管理)

  1. 创建配置类,使用配置类代替XML配置文件
@Configuration //配置类

@ComponentScan(basePackages = "com.atguigu") //组件扫描

@EnableTransactionManagement //开启事务

public class TxConfig {

//创建数据库连接池

@Bean public DruidDataSource getDruidDataSource() {

DruidDataSource dataSource = new DruidDataSource();

dataSource.setDriverClassName("com.mysql.jdbc.Driver");

dataSource.setUrl("jdbc:mysql:///user_db");

dataSource.setUsername("root");

dataSource.setPassword("root");

return dataSource;

}

//创建 JdbcTemplate 对象

@Bean

public JdbcTemplate getJdbcTemplate(DataSource dataSource) {

//到 ioc 容器中根据类型找到 dataSource

JdbcTemplate jdbcTemplate = new JdbcTemplate();

//注入 dataSource

jdbcTemplate.setDataSource(dataSource);

return jdbcTemplate;

}

//创建事务管理器

@Bean

public DataSourceTransactionManager

getDataSourceTransactionManager(DataSource dataSource) {

DataSourceTransactionManager transactionManager = new

DataSourceTransactionManager();

transactionManager.setDataSource(dataSource);

return transactionManager;

}

}

测试

@Test
public void test3(){
    ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
    UserService userService = context.getBean("userService", UserService.class);
    userService.accountMoney();
}