2024.3.20-21@spring框架学习笔记

20 :Time:43min

P114 spring AOP基于注解之全注解开发 start :
其它文件与上集相同
Spring6Config类:

package com.powernode.spring6.service;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

@Configuration//代替spring.xml文件
@ComponentScan("com.powernode.spring6.service")//组件扫描
@EnableAspectJAutoProxy(proxyTargetClass = true)//启用aspecttj的自动代理机制
public class Spring6Config {
}

测试类内容:
@Test
public void testNoXml(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Config.class);
    OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
    orderService.generate();
}

运行结果:
前环绕
前置通知
目标方法的方法名generate
前置通知:安全。。。
系统正在生成订单。。。
后置通知
最终通知
后环绕

P115 Spring AOP基于XML方式实现  start :
TimerAspect类中的内容:
package com.powernode.spring6.service;

import org.aspectj.lang.ProceedingJoinPoint;

public class TimerAspect {
    //通知
    public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        //前环绕
        long begin = System.currentTimeMillis();
        //目标
        joinPoint.proceed();
        //后环绕
        long end = System.currentTimeMillis();
        System.out.println("耗时" + (end - begin) + "毫秒");

    }

}

UserService类中的内容:
package com.powernode.spring6.service;

public class UserService {//目标对象
    public void logout(){//目标方法
        System.out.println("系统正在安全退出...");
    }
}

spring配置文件中 的内容:
<?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">


        <!--纳入spring  ioc -->
        <bean id="userService" class="com.powernode.spring6.service.UserService"></bean>
        <bean id="timerAspect" class="com.powernode.spring6.service.TimerAspect"></bean>

        <!--aop的配置 -->
    <aop:config>
        <!--切点表达式 -->
        <aop:pointcut id="mypointcut" expression="execution(* com.powernode.spring6.service..*(..))"/>
        <!--切面: 通知 + 切点 -->
        <aop:aspect ref="timerAspect" >
            <aop:around method="aroundAdvice" pointcut-ref="mypointcut" />
        </aop:aspect>

    </aop:config>

</beans>
测试类中的内容:
@Test
public void testXml(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    UserService userService = applicationContext.getBean("userService", UserService.class);
    userService.logout();
}
运行结果:
系统正在安全退出...
耗时10毫秒

Time:43min
P116 Spring AOP编程式事务解决方案 start:
15.5 AOP的实际案例:事务处理
项目中的事务控制是在所难免的。在一个业务流程当中,可能需要多条DML语句共同完成,为了保证数据的安全,这多条DML语句要么同时成功,要么同时失败。
这个控制事务的代码就是和业务逻辑没有关系的“交叉业务”。以上伪代码当中可以看到这些交叉业务的代码没有得到复用,并且如果这些交叉业务代码需要修改,那必然需要修改多处,难维护,怎么解决?可以采用AOP思想解决。可以把以上控制事务的代码作为环绕通知,切入到目标类的方法当中。

AccountService类中的内容:
package com.powernode.spring6.service;

import org.springframework.stereotype.Service;

@Service
public class AccountService {//目标对象
    //目标方法
    //转账 的业务方法
    public void transfer(){
        System.out.println("银行账户正在完成转账操作...");
    }
    //目标方法
    //取款的业务方法
    public void withdraw(){
        System.out.println("正在取款,请稍后。。。");
    }

}

OrderService类中的内容:
package com.powernode.spring6.service;

import org.springframework.stereotype.Service;

@Service
public class OrderService {//目标对象
    //目标方法
    //生成订单的业务方法
    public void generate(){
        System.out.println("正在生成订单...");
    }
    //目标方法
    //取消订单的业务方法
    public void cancel(){
        System.out.println("订单已取消...");
        String s = null;
        s.toString();
    }

}

TransactionAspect类中的内容:
package com.powernode.spring6.service;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class TransactionAspect {

    //编程式事务解决方案。

    @Around("execution(* com.powernode.spring6.service..*(..))")
    public void aroundAdvice(ProceedingJoinPoint joinPoint){

        try {
            //前环绕
            System.out.println("开启事务");
            //执行目标
            joinPoint.proceed();
            //后环绕
            System.out.println("提交事务");
        } catch (Throwable e) {
            System.out.println("回滚事务");
        }

    }

}


spring配置文件spring.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: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.powernode.spring6.service"/>
    <!--启动自动代理 -->
    <aop:aspectj-autoproxy />

</beans>

测试类中的 内容:
@Test
public void testTransaction(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
    OrderService orderService = applicationContext.getBean("orderService", OrderService.class);

    accountService.transfer();
    accountService.withdraw();
    orderService.generate();
    orderService.cancel();

运行结果:
开启事务
银行账户正在完成转账操作...
提交事务
开启事务
正在取款,请稍后。。。
提交事务
开启事务
正在生成订单...
提交事务
开启事务
订单已取消...
回滚事务

P117 spring AOP安全日志解决方案 start:
15.6 AOP的实际案例:安全日志:

SecurityLogAspect类中的内容:
package com.powernode.spring6.biz;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
@Component
@Aspect
public class SecurityLogAspect {

    @Pointcut("execution(* com.powernode.spring6.biz..save*(..))")
    public void savePointcut(){}

    @Pointcut("execution(* com.powernode.spring6.biz..delete*(..))")
    public void deletePointcut(){}

    @Pointcut("execution(* com.powernode.spring6.biz..modify*(..))")
    public void modifyPointcut(){}

    @Before("savePointcut() || deletePointcut() || modifyPointcut()")
    public void beforeAdvice(JoinPoint joinPoint){
        //系统时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        String nowTime = sdf.format(new Date());
        //输出日志信息
        System.out.println(nowTime + " zhangsan : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
    }

}

UserService类中的内容:
package com.powernode.spring6.biz;

import org.springframework.stereotype.Service;

@Service
public class UserService {

    public void saveUser(){
        System.out.println("新增用户信息");
    }

    public void deleteUser(){
        System.out.println("删除用户信息");
    }

    public void modifyUser(){
        System.out.println("修改用户信息");
    }

    public void getUser(){
        System.out.println("获取用户信息");
    }
}

VipService类中的内容:
package com.powernode.spring6.biz;

import org.springframework.stereotype.Service;

@Service
public class VipService {

    public void saveVip(){
        System.out.println("新增会员信息");
    }

    public void deleteVip(){
        System.out.println("删除会员信息");
    }

    public void modifyVip(){
        System.out.println("修改会员信息");
    }

    public void getVip(){
        System.out.println("获取会员信息");
    }
}

spring配置文件spring.mxl文件中的内容:
<?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.powernode.spring6"/>
    <!--启动自动代理 -->
    <aop:aspectj-autoproxy />

</beans>
测试类中的内容:
@Test
public void testSecuritylog(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    UserService userService = applicationContext.getBean("userService", UserService.class);
    VipService vipService = applicationContext.getBean("vipService", VipService.class);

    userService.saveUser();
    userService.deleteUser();
    userService.modifyUser();
    userService.getUser();

    vipService.saveVip();
    vipService.deleteVip();
    vipService.modifyVip();
    vipService.getVip();

}
运行结果:
2024-03-21 14:16:06 851 zhangsan : com.powernode.spring6.biz.UserService.saveUser
新增用户信息
2024-03-21 14:16:06 866 zhangsan : com.powernode.spring6.biz.UserService.deleteUser
删除用户信息
2024-03-21 14:16:06 867 zhangsan : com.powernode.spring6.biz.UserService.modifyUser
修改用户信息
获取用户信息
2024-03-21 14:16:06 867 zhangsan : com.powernode.spring6.biz.VipService.saveVip
新增会员信息
2024-03-21 14:16:06 872 zhangsan : com.powernode.spring6.biz.VipService.deleteVip
删除会员信息
2024-03-21 14:16:06 873 zhangsan : com.powernode.spring6.biz.VipService.modifyVip
修改会员信息
获取会员信息

P118 Spring事务之事务概述
十六、Spring对事务的支持
16.1 事务概述
● 什么是事务
○ 在一个业务流程当中,通常需要多条DML(insert delete update)语句共同联合才能完成,这多条DML语句必须同时成功,或者同时失败,这样才能保证数据的安全。
○ 多条DML要么同时成功,要么同时失败,这叫做事务。
○ 事务:Transaction(tx)
● 事务的四个处理过程:
○ 第一步:开启事务 (start transaction)
○ 第二步:执行核心业务代码
○ 第三步:提交事务(如果核心业务处理过程中没有出现异常)(commit transaction)
○ 第四步:回滚事务(如果核心业务处理过程中出现异常)(rollback transaction)
● 事务的四个特性:
○ A 原子性:事务是最小的工作单元,不可再分。
○ C 一致性:事务要求要么同时成功,要么同时失败。事务前和事务后的总量不变。
○ I 隔离性:事务和事务之间因为有隔离性,才可以保证互不干扰。
○ D 持久性:持久性是事务结束的标志。


16.2 引入事务场景
以银行账户转账为例学习事务。两个账户act-001和act-002。act-001账户向act-002账户转账10000,必须同时成功,或者同时失败。(一个减成功,一个加成功, 这两条update语句必须同时成功,或同时失败。)
连接数据库的技术采用Spring框架的JdbcTemplate。
采用三层架构搭建:
P119  spring事务之引入事务场景  :
一张表会对应一个实体类(一般)
AccountDaoImpl类中的内容:
package com.powernode.bank.dao.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import jakarta.annotation.Resource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Resource(name = "jdbcTemplate")
    private JdbcTemplate jdbcTemplate;

    @Override
    public Account selectByActno(String actno) {
        String sql = "select actno, balance from t_act where actno = ?";
        Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), actno);

        return account;
    }

    @Override
    public int update(Account act) {
        String sql = "update t_act set balance = ? where actno = ?";
        int count = jdbcTemplate.update(sql, act.getBalance(), act.getActno());
        return count;
    }
}

AccountDao接口中的内容:
package com.powernode.bank.dao;

import com.powernode.bank.pojo.Account;

/**
 * 专门负责账号信息的CRUD操作。
 * DAO中只执行SQL语句,没有任何业务逻辑。
 * 也就是说DAO不和业务挂钩。
 */
public interface AccountDao {
    /**
     * 根据账号查询账户信息
     * @param actno
     * @return
     */
    Account selectByActno(String actno);

    /**
     * 更新账户信息。
     * @param act
     * @return
     */
    int update(Account act);

}

Account类中的内容:
package com.powernode.bank.pojo;

/**
 * 银行账号类
 */
public class Account {
    private String actno;
    private Double 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;
    }

    @Override
    public String toString() {
        return "Account{" +
                "actno='" + actno + '\'' +
                ", balance=" + balance +
                '}';
    }
}

AccountServiceImpl类中的内容:
package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Resource(name = "accountDao")
    private AccountDao accountDao;


    //控制事务,因为在这个方法中要完成所有的转账业务。
    @Override
    public void transfer(String fromActno, String toActno, double money) {
        //查询转出账户的余额是否充足
        Account fromAct = accountDao.selectByActno(fromActno);
        if (fromAct.getBalance() < money) {
            throw new RuntimeException("余额不足!!!");
        }

        //余额充足
        Account toAct = accountDao.selectByActno(toActno);

        //将内存中两个对象的余额先修改。
        fromAct.setBalance(fromAct.getBalance() - money);
        toAct.setBalance(toAct.getBalance() + money);

        //数据库更新
        int count = accountDao.update(fromAct);
        count += accountDao.update(toAct);

        if(count != 2){
            throw new RuntimeException("转账失败,联系银行!");
        }

    }
}

AccountService接口中的内容:
package com.powernode.bank.service;

/**
 * 业务接口
 * 事务就是在这个接口下控制的。
 */
public interface AccountService {
    /**
     * 转账业务方法
     * @param fromActno  从这个账户转出
     * @param toActno   转入这个账号
     * @param money //转账金额
     */
    void transfer(String fromActno,String toActno, double money);

}


P120 spring事务之演示转账失败:
P121 Spirng事务之事务管理器接口
16.3 Spring对事务的支持
Spring实现事务的两种方式
● 编程式事务
○ 通过编写代码的方式来实现事务的管理。
● 声明式事务
○ 基于注解方式
○ 基于XML配置方式

Spring事务管理API
Spring对事务的管理底层实现方式是基于AOP实现的。采用AOP的方式进行了封装。所以Spring专门针对事务开发了一套API,API的核心接口如下:

PlatformTransactionManager接口:spring事务管理器的核心接口。在Spring6中它有两个实现:
● DataSourceTransactionManager:支持JdbcTemplate、MyBatis、Hibernate等事务管理。
● JtaTransactionManager:支持分布式事务管理。
如果要在Spring6中使用JdbcTemplate,就要使用DataSourceTransactionManager来管理事务。(Spring内置写好了,可以直接用。)

P122  spring事务之注解方式:

@Transactional如果你写在类上,表示所有的方法都应用事务,如果用在方法上,说明只针对这个方法应用食物
AccountDaoImpl类中的内容:
package com.powernode.bank.dao.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import jakarta.annotation.Resource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Resource(name = "jdbcTemplate")
    private JdbcTemplate jdbcTemplate;

    @Override
    public Account selectByActno(String actno) {
        String sql = "select actno, balance from t_act where actno = ?";
        Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), actno);

        return account;
    }

    @Override
    public int update(Account act) {
        String sql = "update t_act set balance = ? where actno = ?";
        int count = jdbcTemplate.update(sql, act.getBalance(), act.getActno());
        return count;
    }
}

AccountDao类中的内容:
package com.powernode.bank.dao;

import com.powernode.bank.pojo.Account;

/**
 * 专门负责账号信息的CRUD操作。
 * DAO中只执行SQL语句,没有任何业务逻辑。
 * 也就是说DAO不和业务挂钩。
 */
public interface AccountDao {
    /**
     * 根据账号查询账户信息
     * @param actno
     * @return
     */
    Account selectByActno(String actno);

    /**
     * 更新账户信息。
     * @param act
     * @return
     */
    int update(Account act);

}

Account类中的内容:
package com.powernode.bank.pojo;

/**
 * 银行账号类
 */
public class Account {
    private String actno;
    private Double 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;
    }

    @Override
    public String toString() {
        return "Account{" +
                "actno='" + actno + '\'' +
                ", balance=" + balance +
                '}';
    }
}

AccountServiceImpl类中的内容:
package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("accountService")
@Transactional
public class AccountServiceImpl implements AccountService {
    @Resource(name = "accountDao")
    private AccountDao accountDao;


    //控制事务,因为在这个方法中要完成所有的转账业务。
    @Override
    //@Transactional
    public void transfer(String fromActno, String toActno, double money) {

        //第一步:开启事务

        //第二步:执行核心业务逻辑
        //查询转出账户的余额是否充足
        Account fromAct = accountDao.selectByActno(fromActno);
        if (fromAct.getBalance() < money) {
            throw new RuntimeException("余额不足!!!");
        }

        //余额充足
        Account toAct = accountDao.selectByActno(toActno);

        //将内存中两个对象的余额先修改。
        fromAct.setBalance(fromAct.getBalance() - money);
        toAct.setBalance(toAct.getBalance() + money);

        //数据库更新
        int count = accountDao.update(fromAct);

        //模拟异常
//        String s =null;
//        s.toString();


        count += accountDao.update(toAct);

        if(count != 2){
            throw new RuntimeException("转账失败,联系银行!");
        }

        //第三步:如果执行业务流程过程中,没有异常,提交事务
        //第四步:如果执行业务流程过程中,有异常,回滚事务

    }
}

AccountService接口中 的内容:

package com.powernode.bank.service;

/**
 * 业务接口
 * 事务就是在这个接口下控制的。
 */
public interface AccountService {
    /**
     * 转账业务方法
     * @param fromActno  从这个账户转出
     * @param toActno   转入这个账号
     * @param money //转账金额
     */
    void transfer(String fromActno,String toActno, double money);

}


spring配置文件spring.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/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.powernode.bank" />


    <!--配置数据源 -->
    <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" />
    </bean>

    <!--配置JdbcTemplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>

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

    <!--开启事务注解驱动器,开启事务注解。告诉Spring框架,采用注解的方式去控制事务。 -->
    <tx:annotation-driven transaction-manager="txManager" />

</beans>
测试类中的内容:

@Test
 public void testSpringTx(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
    try{
        accountService.transfer("act-001","act-002",10000);
        System.out.println("转账成功");
    }catch(Exception e){
        e.printStackTrace();
    }

}


P123  Spring事务之传播行为 :
事务属性
事务属性包括哪些
事务中的重点属性:
● 事务传播行为
● 事务隔离级别
● 事务超时
● 只读事务
● 设置出现哪些异常回滚事务
● 设置出现哪些异常不回滚事务
事务传播行为
什么是事务的传播行为?
在service类中有a()方法和b()方法,a()方法上有事务,b()方法上也有事务,当a()方法执行过程中调用了b()方法,事务是如何传递的?合并到一个事务里?还是开启一个新的事务?这就是事务传播行为。
事务传播行为在spring框架中被定义为枚举类型:

一共有七种传播行为:
● REQUIRED:支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】
● SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行【有就加入,没有就不管了】
● MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常【有就加入,没有就抛异常】
● REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】
● NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务【不支持事务,存在就挂起】
● NEVER:以非事务方式运行,如果有事务存在,抛出异常【不支持事务,存在就抛异常】
● NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】

P124  Spring事务之搭建传播行为测试程序
一定要集成Log4j2日志框架,在日志信息中可以看到更加详细的信息。
事务隔离级别
事务隔离级别类似于教室A和教室B之间的那道墙,隔离级别越高表示墙体越厚。隔音效果越好。
数据库中读取数据存在的三大问题:(三大读问题)
● 脏读:读取到没有提交到数据库的数据,叫做脏读。
● 不可重复读:在同一个事务当中,第一次和第二次读取的数据不一样。
● 幻读:读到的数据是假的。
事务隔离级别包括四个级别:
● 读未提交:READ_UNCOMMITTED
○ 这种隔离级别,存在脏读问题,所谓的脏读(dirty read)表示能够读取到其它事务未提交的数据。
● 读提交:READ_COMMITTED
○ 解决了脏读问题,其它事务提交之后才能读到,但存在不可重复读问题。
● 可重复读:REPEATABLE_READ
○ 解决了不可重复读,可以达到可重复读效果,只要当前事务不结束,读取到的数据一直都是一样的。但存在幻读问题。
● 序列化:SERIALIZABLE
○ 解决了幻读问题,事务排队执行。不支持并发。
大家可以通过一个表格来记忆:

隔离级别    脏读    不可重复读    幻读
读未提交    有    有    有
读提交    无    有    有
可重复读    无    无    有
序列化    无    无    无

在Spring代码中如何设置隔离级别?
隔离级别在spring中以枚举类型存在:

P125 Spring事务之传播行为REQUIRED:

依赖文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.powernode</groupId>
    <artifactId>spring6-013-tx-bank</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <!--仓库-->
    <repositories>
        <!--spring里程碑版本的仓库-->
        <repository>
            <id>repository.spring.milestone</id>
            <name>Spring Milestone Repository</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>

    <!--依赖-->
    <dependencies>
        <!--spring context-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.0-M2</version>
        </dependency>
        <!--spring jdbc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.0.0-M2</version>
        </dependency>
        <!--mysql驱动-->
        <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>
        <!--@Resource注解-->
        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>2.1.1</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <!--log4j2的依赖 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.19.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j2-impl</artifactId>
            <version>2.19.0</version>
        </dependency>

    </dependencies>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

</project>
log4j2.xml文件中的内容:
<?xml version="1.0" encoding="UTF-8"?>

<configuration>

    <loggers>
        <!--
            level指定日志级别,从低到高的优先级:
            ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
         -->
        <root level="DEBUG">
            <appender-ref ref="spring6log"/>
        </root>
    </loggers>

    <appenders>
        <!--输出日志信息到控制台 -->
        <console name="spring6log" target="SYSTEM_OUT">
            <!--控制日志输出的格式 -->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-3level %logger{1024} - %msg%n"/>
        </console>
    </appenders>

</configuration>


AccountServiceImpl类中的内容:
package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service("accountService")
//@Transactional
public class AccountServiceImpl implements AccountService {
    @Resource(name = "accountDao")
    private AccountDao accountDao;


    //控制事务,因为在这个方法中要完成所有的转账业务。
    @Override
    @Transactional
    public void transfer(String fromActno, String toActno, double money) {

        //第一步:开启事务

        //第二步:执行核心业务逻辑
        //查询转出账户的余额是否充足
        Account fromAct = accountDao.selectByActno(fromActno);
        if (fromAct.getBalance() < money) {
            throw new RuntimeException("余额不足!!!");
        }

        //余额充足
        Account toAct = accountDao.selectByActno(toActno);

        //将内存中两个对象的余额先修改。
        fromAct.setBalance(fromAct.getBalance() - money);
        toAct.setBalance(toAct.getBalance() + money);

        //数据库更新
        int count = accountDao.update(fromAct);

        //模拟异常
//        String s =null;
//        s.toString();


        count += accountDao.update(toAct);

        if(count != 2){
            throw new RuntimeException("转账失败,联系银行!");
        }

        //第三步:如果执行业务流程过程中,没有异常,提交事务
        //第四步:如果执行业务流程过程中,有异常,回滚事务

        //withdraw();
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void withdraw(){

    }


    @Resource(name = "accountService2")
    private AccountService accountService;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void save(Account act) {
        //这里调用dao的insert方法。
        accountDao.insert(act);//保存act-003账户

         //创建账户对象
        Account act2 = new Account("act-004",1000.0);
        try{
            accountService.save(act2);//保存act-004账户
        }catch (Exception e){

        }


    }

}

AccountServiceImpl2类中的内容:
package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service("accountService2")
public class AccountServiceImpl2 implements AccountService {

    @Resource(name = "accountDao")
    private AccountDao accountDao;

    @Override
    public void transfer(String fromActno, String toActno, double money) {

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void save(Account act) {
        accountDao.insert(act);

        //模拟异常
        String s = null;
        s.toString();

        //事儿还没有处理完,这个大括号当中的后续也许还有其他的DML语句。

    }
}

测试类中的内容:
@Test
public void testPropagation(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
    Account act = new Account("act-003",1000.0);
    accountService.save(act);


}
其他文件未存储。
P126  Spring 事务之传播行为REQUIRES_NEW  :
内容:略
P127 Spring事务之事务隔离级别的三大读问题:
事务隔离级别
事务隔离级别类似于教室A和教室B之间的那道墙,隔离级别越高表示墙体越厚。隔音效果越好。
数据库中读取数据存在的三大问题:(三大读问题)
● 脏读:读取到没有提交到数据库的数据,叫做脏读。
● 不可重复读:在同一个事务当中,第一次和第二次读取的数据不一样。
● 幻读:读到的数据是假的。
事务隔离级别包括四个级别:
● 读未提交:READ_UNCOMMITTED
○ 这种隔离级别,存在脏读问题,所谓的脏读(dirty read)表示能够读取到其它事务未提交的数据。
● 读提交:READ_COMMITTED
○ 解决了脏读问题,其它事务提交之后才能读到,但存在不可重复读问题。
● 可重复读:REPEATABLE_READ
○ 解决了不可重复读,可以达到可重复读效果,只要当前事务不结束,读取到的数据一直都是一样的。但存在幻读问题。
● 序列化:SERIALIZABLE
○ 解决了幻读问题,事务排队执行。不支持并发。
大家可以通过一个表格来记忆:

隔离级别    脏读    不可重复读    幻读
读未提交    有    有    有
读提交    无    有    有
可重复读    无    无    有
序列化    无    无    无

在Spring代码中如何设置隔离级别?
隔离级别在spring中以枚举类型存在:

P127  3:24  
Time:4.30h+40min=5h
视频网址:
https://www.bilibili.com/video/BV1Ft4y1g7Fb?p=127&vd_source=ff898217e3612fcbdefecfdc49657ab1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值