其他分享
首页 > 其他分享> > 第四十八天:Spring04 Spring整合事务及Spring模板模式

第四十八天:Spring04 Spring整合事务及Spring模板模式

作者:互联网

1. 事务相关概念回顾

1.1 相关概念回顾

概念:一组对数据库的操作,要么同时成功,要么同时失败。

作用:保证一组对数据库的操作是一个原子操作,同时保证多个同时对数据库的操作尽量不受影响。

四大特性:ACID;原子性、移植性,隔离性,持久性。

事务并发访问的问题及隔离级别

1.2 事务控制位置

2. Spring事务管理的核心对象(了解)

学顶层,用底层。

学习Spring事务管理的核心高层接口。

事务对业务层代码的一种增强,在事务方面的增强,其中获取事务(前置增强)、提交事务(后置增强)、回滚事务(异常增强)就是某个具体的增强,PlatformTransactionManager就是一个通知类/切面类。

4. 案例环境

4.1 业务逻辑

银行转账业务

4.2 技术选型

基于Spring+Mybatis

4.3 相关配置和代码

编程式事务:通过编码的方式来控制事务

声明式事务

5. 编程式事务(理解)

5.0 Spring控制事务的方式

5.1 基本的编程式事务

修改业务层转账代码(核心业务代码),系统辅助功能代码入侵了核心业务代码

public void transfer(String outName,String inName,Double money){
    //创建事务管理器
    DataSourceTransactionManager dstm = new DataSourceTransactionManager();
    //为事务管理器设置与数据层相同的数据源
    dstm.setDataSource(dataSource);
    //创建事务定义对象
    TransactionDefinition td = new DefaultTransactionDefinition();
    //创建事务状态对象,用于控制事务执行
    TransactionStatus ts = dstm.getTransaction(td);
    
    
    
    accountDao.inMoney(outName,money);
    // int i = 1/0; //模拟业务层事务过程中出现错误
    accountDao.outMoney(inName,money);
    
    
 
    //提交事务
    dstm.commit(ts);
}

注意:业务层要注入与dao层相同的dataSource对象(底层原理:保证使用的是同一个连接对象)

5.2 使用AOP改造编程式事务

实现了,系统辅助功能代码 和 核心业务代码 解耦

业务层核心转账代码

public void transfer(String outName, String inName, Double money) {
    accountDao.inMoney(outName,money);
    int i = 1/0;
    accountDao.outMoney(inName,money);
}

切面类中事务增强代码

public class TxAdvice {

    // 保证使用的是同一个DataSource,所以要在切面类中注入dataSource(dao层同一个对象)
    private DataSource dataSource;
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Object transactionManager(ProceedingJoinPoint pjp) throws Throwable {
        //开启事务
        PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
        //事务定义
        TransactionDefinition td = new DefaultTransactionDefinition();
        //事务状态
        TransactionStatus ts = ptm.getTransaction(td);

        Object ret = pjp.proceed(pjp.getArgs());

        //提交事务
        ptm.commit(ts);
        return ret;
    }
}

配置织入关系

<bean id="txAdvice" class="com.company.aop.TxAdvice">
    <property name="dataSource" ref="dataSource"/>
</bean>

<aop:config>
    <aop:pointcut id="pt" expression="execution(* *..transfer(..))"/>
    <aop:aspect ref="txAdvice">
        <aop:around method="transactionManager" pointcut-ref="pt"/>
    </aop:aspect>
</aop:config>

6. 声明式事务

声明式事务:以配置的方式管理事务

配置方式有两种:xml注解

6.1 XML(理解)

6.1.1 入门范例,beans.xml

<!--装配平台事务管理器对象-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--
    配置事务的切面类,不需要我们自己编写这个类了
    定义事务管理的通知类,需要指定一个平台事务管理器对象
-->
<tx:advice id="txAdvice" transaction-manager="txManager">
    <!--定义哪些业务方法会被事务控制,以及使用什么样的参数进行控制-->
    <tx:attributes>
        <!-- tx:method必须写,否则任何一个业务方法都不会被事务控制
 			name 要被事务控制的业务方法的名字 
			read-only等其他属性,配置当前这个方法的事务的属性:是否只读、超时时间……
		-->
        <tx:method name="transfer" read-only="false" />
    </tx:attributes>
</tx:advice>



<!-- 配置织入关系 -->
<aop:config>
    <!-- 切面 = 切点 + 通知 -->
    <aop:pointcut id="pt" expression="execution(* com.company.service..*ServiceImpl.*(..))"/>
    <!--
        aop:advisor 和 aop:aspect 功能一样,都是用于配置切面的信息
        前者是Spring专门为事务管理添加的一个新标签
     -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>

6.1.2 tx:attributes&tx:method

Spring事务控制是方法级别的,也就是可以为每个方法配置不同的事务管理属性。

tx:attributes对不同的方法进行事务控制、事务控制参数配置的父标签

tx:method配置哪些方法被事务控制、以及使用什么样的参数进行控制

<tx:advice id="txAdvice" transaction-manager="txManager">
    <!--
        Spring进行事务控制的级别:
            方法级别
     -->

    <!--定义哪些业务方法会被事务控制,以及使用什么样的参数进行控制-->
    <!-- 对不同的方法进行事务控制、事务控制参数配置的父标签 -->
    <tx:attributes>
        <!-- 配置哪些方法被事务控制、以及使用什么样的参数进行控制
            name属性用于指定哪些方法会被控制
            read-only="false"   只读/读写事务,默认是读写事务
            isolation="DEFAULT"  默认数据库的隔离级别
            timeout="-1"         默认不会超时
            propagation=""       传播行为
            rollback-for=""      配置异常类型名,抛出指定异常后必须回滚
            no-rollback-for=""   配置异常类型名,抛出指定异常后不回滚
         -->
        <tx:method name="transfer"/>
    </tx:attributes>
</tx:advice>

6.1.3 切点表达式和tx:methodname值关系

aop:config中切点表达式和 tx:advice中 tx:method是否重复,不重复的话,作用分别是什么?

前者仅仅明确了哪些方法会被事务控制,但是没有事务控制是具体细节 — 相当于海选

后者指定被拦截成功的所有方法中,每个方法的事务到底是什么属性参数进行控制 — 按照特质进行培养包装宣传

6.1.4 工作中用法

<tx:advice transaction-manager="transactionManager" id="txAdvice">
    <tx:attributes>
        <!-- 
			工作中如何配置
            匹配原则:从上往下挨个匹配,只有匹配成功
         -->
        <tx:method name="find*" read-only="true" timeout="10"/>
        <!-- log代表的是系统辅助功能,不应该影响核心业务的运行 -->
        <tx:method name="*log" propagation="REQUIRES_NEW"/>
        <!-- 其他所有方法都走默认的事务管理 -->
        <tx:method name="*" read-only="false"/>
    </tx:attributes>
</tx:advice>
/*
成对出现
组件扫描 :<context:component-scan>|@ComponentScan 和 @Component;
aop自动代理:<aop:aspectj-autoproxy> | @EnableAspectjAutoProxy 和 @Aspect

事务管理:<tx:annotation-driven/> | @EnableTransactionManagement 和 @Transactional
*/

6.2 注解

6.2.1 注解 + XML配置

一个注解 + 一个标签

Spring配置文件beans.xml开启注解驱动

<tx:annotation-driven transaction-manager="txManager"/>

在要被事务控制的接口或者接口方法上添加@Transactional注解

@Transactional
public interface AccountService {}

通过注解的属性,调整事务的参数(工作中用法)

@Transactional  //保证当前类中所有的方法都被事务控制
public interface AccountService {
    
    @Transactional(readOnly= true,timeout=10) // 为当前方法的事务添加个性需求
    List<Account> findAll();
    
}

该注解添加在接口上还是实现类上,取决于你的需求:

  1. 如果加载接口上,该接口的所有实现类都会被事务控制
  2. 如果加载某个具体的实现类上,事务只会在当前类有效

注意:

  1. 配置注解驱动的时候,注意选择tx的命名空间(url后缀为tx的,否则会报错)
  2. 当类(接口)上、方法上都有事务注解并且参数不一致的时候,按照就近原则,以最近的为准
  3. 成对出现:tx:annotation-driven@Transactional

6.2.1 全注解

Spring配置类SpringConfig.java开启注解驱动,并通过@Bean注入平台事务管理器

@ComponentScan("com.company")
@PropertySource("classpath:jdbc.properties")
@EnableTransactionManagement
public class SpringConfig(){
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource")
    public DataSource getDataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }

    @Bean
    public PlatformTransactionManager transactionManager(@Autowired DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

在要被事务控制的接口或者接口方法上添加@Transactional注解

@Transactional
public interface AccountService {}

通过注解的属性,调整事务的参数(工作中用法)

@Transactional  //保证当前类中所有的方法都被事务控制
public interface AccountService {
    
    @Transactional(readOnly= true,timeout=10) // 为当前方法的事务添加个性需求
    List<Account> findAll();
    
}

注意事项

  1. 成对出现:@EnableTransactionManagement@Transactional

7. 传播行为

7.1 概念

传播行为研究的是:业务层A方法调用其他方法B(C、D……)时,A、B(C、D……)这些方法执行期间如何控制事务的问题:被调用方法B如果看待处理调用方法A事务的问题。

结婚的时候彩礼类比说明

事务 – 彩礼房车

方法的调用者A – 男方 - 事务管理员

被调用的方法B – 女方 – 事务协调员

7.2 分类

总共七种

1603877022989

7.3 工作中用法

REQUIRED 这个是默认值

REQUIRES_NEW 应用场景,eg:取钱时候,打印小票方法可以配置该传播行为。

8. Spring模板

高频常用的代码封装成模板,通过AOP的方式,使用的时候直接在模板上填充个性数据即可。

8.1 Spring中常见的模板

8.2 JDBCTemplate(了解)

8.3 RedisTemplate(掌握)

8.3.1 环境准备(使用redis)

  1. 开启Redis服务

    注意

    • 要求启动时使用的配置文件中绑定可以从外部连通的本机网卡地址、禁用受保护模式。否则启动会报错
    bind 192.168.115.130
    prot 6379
    protected-mode no
    
  2. 导入Jedis依赖坐标

    <!-- Jedis  -->
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.9.0</version>
    </dependency>
    
  3. 创建Jedis对象,并使用

    public class App {
        public static void main(String[] args) {
            Jedis jedis = new Jedis("192.168.115.130",6379);
            jedis.set("name","company");
            jedis.close();
        }
    }
    
    

8.3.2 使用RedisTemplate

9. 相关设计模式

设计模式:在不断的编码摸索过程中,总结出来的解决某些需求的思路(解决方案)

4ClassRunner.class)
//设定加载的spring上下文对应的配置
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
@Autowired
private AccountService accountService;

  /*@Test
  public void test(){
      Jedis jedis = new Jedis("192.168.40.130",6378);
      jedis.set("name","company");
      jedis.close();
  }*/

  @Test
  public void save(){
      Account account = new Account();
      account.setName("Jock");
      account.setMoney(666.66);

  }

  @Test
  public void changeMoney() {
      accountService.changeMoney(1,200D);
  }

  @Test
  public void findMondyById() {
      Double money = accountService.findMondyById(1);
      System.out.println(money);
  }

}






## 9. 相关设计模式

**设计模式**:在不断的编码摸索过程中,总结出来的解决某些需求的思路(解决方案)

- 策略模式,根据不同的条件,提供了多种解决方案供使用者选择,并最终解决问题。

`JDBCTemplate`在封装结果集的时候,让使用者根据结果集字段名和实体属性名是否一致,选择手动封装或自动封装

- 装饰者模式

静态代理。

目标对象:JDBCTemplate

代理对象{	NamedParamterJDBCTemplate(从使用`?`作为占位符 增强成了  使用`:名称`作为占位符)

​	//持有目标对象,并且增强目标对象

}









标签:account,String,Spring,void,Spring04,事务,money,第四十八,public
来源: https://blog.csdn.net/qq_38250571/article/details/112559470