day04 spring 声明式事务

news2025/1/9 17:12:35

day04 spring 声明式事务

1.JDBCTemplate

1.1 简介

为了在特定领域帮助我们简化代码,Spring 封装了很多 『Template』形式的模板类。例如:RedisTemplate、RestTemplate 等等,包括我们今天要学习的 JDBCTemplate。

1.2 准备工作

1.2.1 加入依赖

<?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.atguigu</groupId>
    <artifactId>day50spring-JDBCTemplate</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- Spring 持久化层支持jar包 -->
        <!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包 -->
        <!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- Spring 测试相关 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>
        <!-- 数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.31</version>
        </dependency>
        <!--引入LomBok的依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

1.2.2 数据源的属性文件jdbc.properties

datasource.url=jdbc:mysql://localhost:3306/mybatis2?characterEncoding=utf8&serverTimezone=UTC
datasource.driver=com.mysql.cj.jdbc.Driver
datasource.username=root
datasource.password=123456

1.2.3 spring配置文件

<!--    2.对JdbcTemplate进行IOC-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

<!--    3.对DruidDataSources进行IOC-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${datasource.username}"></property>
        <property name="password" value="${datasource.password}"></property>
        <property name="url" value="${datasource.url}"></property>
        <property name="driverClassName" value="${datasource.driver}"></property>
    </bean>

1.2.4 创建Soldier实体类

package com.atguigu.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
// 全参构造器
@AllArgsConstructor
// 无参构造器
@NoArgsConstructor
public class Soldier {
    private Integer soldierId;
    private String soldierName;
    private String soldierWeapon;
}

1.2.5 创建SoldierDao接口

package com.atguigu.dao;

import com.atguigu.pojo.Soldier;

import java.sql.SQLException;
import java.util.List;

public interface SoldierDao {
    /**
     * 根据id删除
     * @param soldierId
     */
    void deleteById(Integer soldierId) throws SQLException;

    /**
     * 更新士兵对象
     * @param soldier
     */
    void update(Soldier soldier) throws SQLException;


    /**
     * 添加士兵对象
     * @param soldier
     */
    void add(Soldier soldier) throws SQLException;


    /**
     * 根据id查询士兵
     * @param soldierId
     * @return
     */
    Soldier getById(Integer soldierId) throws SQLException;

    /**
     * 获取所有士兵
     * @return
     */
    List<Soldier> findAll() throws SQLException;
}

1.2.6 创建SoldierDaoImpl实现类 JDBCTemplate的基本用法

package com.atguigu.dao.impl;

import com.atguigu.dao.SoldierDao;
import com.atguigu.pojo.Soldier;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.sql.SQLException;
import java.util.List;

// 持久层注解
@Repository
public class SoldierDaoImpl implements SoldierDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void deleteById(Integer soldierId) throws SQLException {
        String sql = "delete from t_soldier where soldier_id=?";
        jdbcTemplate.update(sql, soldierId);
    }

    @Override
    public void update(Soldier soldier) throws SQLException {
        String sql = "update t_soldier set soldier_name=?,t_soldier_weapon=? where soldier_id=?";
        jdbcTemplate.update(sql, soldier.getSoldierName(),soldier.getSoldierWeapon(),soldier.getSoldierId());

    }

    @Override
    public void add(Soldier soldier) throws SQLException {
        String sql = "insert into t_soldier(soldier_name,soldier_weapon) values(?,?)";
        jdbcTemplate.update(sql, soldier.getSoldierName(), soldier.getSoldierWeapon());

    }

    @Override
    public Soldier getById(Integer soldierId) throws SQLException {
        String sql = "select soldier_id soldierId,soldier_name soldierName,soldier_weapon soldierWeapon from t_soldier where soldier_id=?";
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Soldier.class), soldierId);
    }

    @Override
    public List<Soldier> findAll() throws SQLException {
        String sql = "select soldier_id soldierId,soldier_name soldierName,soldier_weapon soldierWeapon from t_soldier";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Soldier.class));
    }
}

1.2.7 spring配置文件中添加包扫描注解

<!--    1.包扫描-->
    <context:component-scan base-package="com.atguigu"></context:component-scan>

1.2.8 测试代码

package com.atguigu;


import com.atguigu.dao.SoldierDao;
import com.atguigu.dao.impl.SoldierDaoImpl;
import com.atguigu.pojo.Soldier;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.sql.SQLException;
import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class JDBCTest {

    @Autowired
    private SoldierDao soldierDao;

    @Test
    public void testFindAll() throws SQLException {
        List<Soldier> all = soldierDao.findAll();
        for (Soldier soldier : all) {
            System.out.println(soldier);
        }

    }

    @Test
    public void testFindOne() throws SQLException {
        System.out.println(soldierDao.getById(1));

    }
}

在这里插入图片描述

2.声明式事务的概述

2.1 编程式事务

事务功能的相关操作全部通过自己编写代码来实现:

Connection conn = ...;
    
try {
    
    // 开启事务:关闭事务的自动提交
    conn.setAutoCommit(false);
    
    // 核心操作
    
    // 提交事务
    conn.commit();
    
}catch(Exception e){
    
    // 回滚事务
    conn.rollBack();
    
}finally{
    //将连接的autoCommit还原成true
    conn.setAutoCommit(true);
    // 释放数据库连接
    conn.close();
}

编程式的实现方式存在缺陷:

  • 细节没有被屏蔽:具体操作过程中,所有细节都需要程序员自己来完成,比较繁琐。
  • 代码复用性不高:如果没有有效抽取出来,每次实现功能都需要自己编写代码,代码就没有得到复用。

2.2 声明式事务

既然事务控制的代码有规律可循,代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出来,进行相关的封装。

封装起来后,我们只需要在配置文件中进行简单的配置即可完成操作。

  • 好处1:提高开发效率
  • 好处2:消除了冗余的代码
  • 好处3:框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题,进行了健壮性、性能等各个方面的优化

所以,我们可以总结下面两个概念:

  • 编程式自己写代码实现事务控制
  • 声明式:通过配置spring框架实现事务控制

2.3 事务管理器

Spring中的声明式事务是通过事务管理器来进行事务管理的,所以在Spring中定义了事务管理器的顶级接口,针对各种不同的持久层框架,又定义了不同的事务管理器类来进行事务管理

2.3.1 顶级接口

2.3.1.1 spring5.2以前
public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(TransactionStatus status) throws TransactionException;
}
2.3.1.2 spring5.2以后

PlatformTransactionManager 接口本身没有变化,它继承了 TransactionManager

public interface TransactionManager {
    
}

TransactionManager接口中什么都没有,但是它还是有存在的意义——定义一个技术体系。

2.3.2 事务管理器的体系结构

在这里插入图片描述

我们现在要使用的事务管理器是org.springframework.jdbc.datasource.DataSourceTransactionManager,将来整合 Mybatis 用的也是这个类。

DataSourceTransactionManager类中的主要方法:

  • doBegin():开启事务
  • doSuspend():挂起事务(暂停事务)
  • doResume():恢复挂起的事务
  • doCommit():提交事务
  • doRollback():回滚事务

如果持久层使用Hibernate框架的话,则需要使用HibernateTransactionManager

3.基于注解的声明式事务

3.1 准备工作

3.1.1 引入依赖

<?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.atguigu</groupId>
    <artifactId>day50spring-transcation</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>

        <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.1</version>
        </dependency>

        <!-- Spring 持久化层支持jar包 -->
        <!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包 -->
        <!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.3.1</version>
        </dependency>

        <!-- Spring 测试相关 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.1</version>
        </dependency>

        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>

        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
        <!-- 数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.31</version>
        </dependency>
        <!--引入LomBok的依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
            <scope>provided</scope>
        </dependency>

        <!-- 加入日志 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>
</project>

3.1.2 数据源的属性文件 jdbc.properties

datasource.url=jdbc:mysql://localhost:3306/mybatis2?characterEncoding=utf8&serverTimezone=UTC
datasource.driver=com.mysql.cj.jdbc.Driver
datasource.username=root
datasource.password=123456

3.1.3 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<!--    1.包扫描-->
    <context:component-scan base-package="com.atguigu"></context:component-scan>

<!--    2.JdbcTemplate进行IOC-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

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

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

    </bean>
</beans>

3.1.4 数据建模

3.1.4.1 物理建模
CREATE TABLE t_account(
	account_id INT PRIMARY KEY AUTO_INCREMENT,
	account_name VARCHAR(20),
	money DOUBLE
);

INSERT INTO t_account VALUES (NULL,'zs',1000);

INSERT INTO t_account VALUES (NULL,'ls',1000);
3.1.4.2 逻辑建模
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
    private Integer accountId;
    private String accountName;
    private Double money;
}

3.1.5 创建持久层组件

3.1.5.1 AccountDao接口
package com.atguigu.dao;

public interface AccountDao {
    /**
     * 修改用户的金额
     * @param id
     * @param money
     */
    void updateAccountMoney(Integer id, Integer money);
}
3.1.5.2 AccountDaoImpl实现类
package com.atguigu.dao.impl;

import com.atguigu.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplatel;

    @Override
    public void updateAccountMoney(Integer id, Integer money) {
        String sql = "update t_account set money=money+? where account_id=?";
        jdbcTemplatel.update(sql, money, id);
    }
}

3.1.6 创建业务层组件

3.1.6.1 AccountService接口
package com.atguigu.serivce;

public interface AccountService {

    /**
     * 转账方法
     * @param fromId 转出账户的id
     * @param toId 转入账户的id
     * @param money 转账金额
     */
    void transfer(Integer fromId,Integer toId,Integer money);
}

3.1.6.2 AccountServiceImpl实现类
package com.atguigu.serivce.impl;

import com.atguigu.dao.AccountDao;
import com.atguigu.serivce.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;


    @Override
    public void transfer(Integer fromId, Integer toId, Integer money) {
        // 1.转出账户扣款
        accountDao.updateAccountMoney(fromId, -money);

        // 测试报错是否会回滚
        int num = 10 / 0;

        // 2.转入账户收款
        accountDao.updateAccountMoney(toId, money);
    }
}

3.1.7 创建AccounrController类

package com.atguigu.controller;

import com.atguigu.serivce.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class AccountController {

    @Autowired
    private AccountService accountService;

    public void transfer(Integer fromId,Integer toId,Integer money){
        accountService.transfer(fromId, toId, money);
        // 转账成功
        System.out.println("转账成功!!!");
    }
}

3.1.8 创建测试类进行测试

package com.atguigu;

import com.atguigu.controller.AccountController;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class TestTransaction {

    @Autowired
    private AccountController accountController;

    @Test
    public void testTransfer(){
        accountController.transfer(1, 2, 500);
    }
}

因为没有添加事务,所以报错后并没有进行回滚

在这里插入图片描述

在这里插入图片描述

3.2 进行基于注解的声明式事务的配置

3.2.1 配置事务管理器

在spring的配置文件中配置事务管理器对象

<!--    4. 对DataSourceTransactionManager进行IOC-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

3.2.2 开启基于注解的声明式事务功能

在spring的配置文件中开启基于注解的声明式事务功能

<!--
        要开启注解方式声明式事务的支持
        transaction-manager属性:表示使用哪个事务管理器对象来管理事务
                               如果你的事务管理器的id是transactionManager的话,可以省略该属性
-->
<tx:annotation-driven/>

注意:导入名称空间时有好几个重复的,我们需要的是 tx 结尾的那个。

在这里插入图片描述

3.2.3 在需要事务的方法上使用注解

package com.atguigu.serivce.impl;

import com.atguigu.dao.AccountDao;
import com.atguigu.serivce.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    
    @Transactional
    @Override
    public void transfer(Integer fromId, Integer toId, Integer money) {
        // 1.转出账户扣款
        accountDao.updateAccountMoney(fromId, -money);

        // 测试报错是否会回滚
        int num = 10 / 0;

        // 2.转入账户收款
        accountDao.updateAccountMoney(toId, money);
    }
}

3.2.4 执行测试

package com.atguigu;

import com.atguigu.controller.AccountController;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class TestTransaction {

    @Autowired
    private AccountController accountController;

    @Test
    public void testTransfer(){
        accountController.transfer(1, 2, 500);
    }
}

在这里插入图片描述

3.3 从日志内容角度查看事务效果

3.3.1 加入依赖

        <!-- 加入日志 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

3.3.2 加入logback的配置文件

文件名:logbcak.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT"
              class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志输出的格式 -->
            <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
        </encoder>
    </appender>

    <!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="INFO">
        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
        <appender-ref ref="STDOUT"/>
    </root>

    <!-- 根据特殊需求指定局部日志级别 -->
    <logger name="org.springframework.web.servlet.DispatcherServlet" level="DEBUG"/>

</configuration>

3.3.3 观察日志打印

在这里插入图片描述

3.4 debug查看事务管理器中的关键方法

需要查看的类:org.springframework.jdbc.datasource.DataSourceTransactionManager

3.4.1 开启事务的方法

在这里插入图片描述

3.4.2 提交事务的方法

在这里插入图片描述

3.4.3 回滚事务的方法

在这里插入图片描述

4.事务属性

4.1 只读属#性

4.1.1 简介

对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。但是如果你的方法中执行写操作,那么就会报错

4.1.2 设置方式

    @Transactional(readOnly = true)
    @Override
    public void transfer(Integer fromId, Integer toId, Integer money) {
        // 1.转出账户扣款
        accountDao.updateAccountMoney(fromId, -money);

        // 测试报错是否会回滚
//        int num = 10 / 0;

        // 2.转入账户收款
        accountDao.updateAccountMoney(toId, money);
    }

4.1.3 如果在设置了只读的事务中进行写操作

会抛出下面异常

Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed

4.1.4 如果将@Transactinal注解放在类上

4.1.4.1 生效原则

如果一个类中每一个方法上都使用了@Transactional注解,那么就可以将@Transactional注解提取到类上。反过来说:@Transactional注解在类级别标记,会影响到类中的每一个方法。同时,类级别标记的@Transactional注解中设置的事务属性也会延续影响到方法执行时的事务属性。除非在方法上又设置了@Transactional注解。

对一个方法来说,离它最近的@Transactional注解中的事务属性设置生效。

4.1.4.2 用法举例

在类级别@Transactional注解中设置只读,这样类中所有的查询方法都不需要设置@Transactional注解了。因为对查询操作来说,其他属性通常不需要设置,所以使用公共设置即可。

然后在这个基础上,对增删改方法设置@Transactional注解 readOnly 属性为 false。

package com.atguigu.serivce.impl;

import com.atguigu.dao.AccountDao;
import com.atguigu.serivce.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * read-only属性表示事务是否只读:默认值是false,如果设置为true 那么当前事务中只能做数据库的读操作,不能做写操作
 * 该属性的作用:可以对只读的数据库操作做一些优化
 */
@Service
// 类上使用此注解 表示类中每个方法都会生效 如果方法上也设置了 则使用方法上的注解
@Transactional(readOnly = true)
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    
    @Transactional(readOnly = false)
    @Override
    public void transfer(Integer fromId, Integer toId, Integer money) {
        // 1.转出账户扣款
        accountDao.updateAccountMoney(fromId, -money);

        // 测试报错是否会回滚
//        int num = 10 / 0;

        // 2.转入账户收款
        accountDao.updateAccountMoney(toId, money);
    }
}

PS:Spring 环境下很多场合都有类似设定,一个注解如果标记了类的每一个方法那么通常就可以提取到类级别。但是,如果不是类中的所有方法都需要用到事务,则绝不允许将@Transaction注解放在类上

4.2 超时属性

4.2.1 简介

事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源,大概率是因为程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)。

此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以执行。

概括来说就是一句话:超时回滚,释放资源。

4.2.2 设置方式

    // timeout 单位:秒  超过指定的时间没响应 则抛出异常
    @Transactional(readOnly = false,
                    timeout = 3)
    @Override
    public void transfer(Integer fromId, Integer toId, Integer money) {
        // 1.转出账户扣款
        accountDao.updateAccountMoney(fromId, -money);

        // 测试报错是否会回滚
//        int num = 10 / 0;

        // 2.转入账户收款
        accountDao.updateAccountMoney(toId, money);
    }

4.2.3 模拟超时

    // timeout 单位:秒  超过指定的时间没响应 则抛出异常
    @Transactional(readOnly = false,
                    timeout = 3)
    @Override
    public void transfer(Integer fromId, Integer toId, Integer money) {
        // 1.转出账户扣款
        accountDao.updateAccountMoney(fromId, -money);

        // 测试报错是否会回滚
//        int num = 10 / 0;
        // 模拟超时 
        try {
            Thread.sleep(3500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 2.转入账户收款
        accountDao.updateAccountMoney(toId, money);
    }

4.2.4 执行效果:

org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Thu Nov 17 09:55:44 CST 2022

4.3 回滚和不回滚的异常属性

4.3.1 默认情况

默认只针对运行时异常回滚,编译时异常不回滚。情景模拟代码如下:

    // timeout 单位:秒  超过指定的时间没响应 则抛出异常
    @Transactional(readOnly = false,
                    timeout = 3)
    @Override
    public void transfer(Integer fromId, Integer toId, Integer money) throws ClassNotFoundException {
        // 1.转出账户扣款
        accountDao.updateAccountMoney(fromId, -money);

        // 测试报错是否会回滚
//        int num = 10 / 0;

        // 什么是运行时异常:不需要在编译时处理的异常
        // 什么是编译时异常:需要在编译时就进行处理的异常
        // 默认情况时遇到运行时异常才回滚
        Class.forName("1eu1j1");



        // 2.转入账户收款
        accountDao.updateAccountMoney(toId, money);
    }

运行后抛出异常并没有事务回滚

在这里插入图片描述

在这里插入图片描述

4.3.2 设置回滚的异常

  • rollbackFor属性:需要设置一个Class类型的对象
  • rollbackForClassName属性:需要设置一个字符串类型的全类名
@Transactional(rollbackFor = Exception.class)

4.3.3 设置不回滚的异常

在默认设置和已有设置的基础上,再指定一个异常类型,碰到它不回滚。

@Transactional(
    noRollbackFor = FileNotFoundException.class
)

4.3.4 如果回滚和不回滚异常同时设置

4.3.4.1 当两者范围不同

不管是哪个设置范围大,都是在大范围内在排除小范围的设定。例如:

  • rollbackFor = Exception.class
  • noRollbackFor = FileNotFoundException.class

意思是除了 FileNotFoundException 之外,其他所有 Exception 范围的异常都回滚;但是碰到 FileNotFoundException 不回滚。

4.3.4.2 当两者范围相同(傻子才会这样去设置)
  • noRollbackFor = FileNotFoundException.class
  • rollbackFor = FileNotFoundException.class

此时 Spring 采纳了 rollbackFor 属性的设定:遇到 FileNotFoundException 异常会回滚。

4.4 事务隔离级别属性

4.4.1 回顾事务的隔离级别

级别名字隔离级别脏读不可重复读幻读数据库默认隔离级别
1读未提交read uncommitted
2读已提交read committedOracle
3可重复读repeatable readMySQL
4串行化serializable最高的隔离级别

4.4.2 设置方式

在 @Transactional 注解中使用 isolation 属性设置事务的隔离级别。 取值使用 org.springframework.transaction.annotation.Isolation 枚举类提供的数值。

@Transactional(isolation = Isolation.READ_UNCOMMITTED)

isolation :
(1). READ_UNCOMMITTED : 读未提交,在这种隔离级别下脏读、不可重复读、幻读都可能发生
(2). READ_COMMITTED : 读已提交,Oracle数据库的默认隔离级别,在这种隔离级别下不可能发生脏读
(3). REPEATABLE_READ : 可重复读,MySQL数据库的默认隔离级别,在这种隔离级别下不可能发生脏读、不可重复读; 这种隔离级别其实是使用了行锁
(4). SERIALIZABLE : 串行化,在这种隔离级别下不可能发生脏读、不可重复读、幻读; 这种隔离级别使用表锁

事务并行性问题:

  1. 脏读: 一个事务读取到了另一个事务未提交的数据,并且另一个事务最终没有提交
  2. 不可重复读: 一个事务中多次读取到的数据的内容不一致(原因是当前事务执行的时候,其它的事务使用UPDATE操作修改了数据)
  3. 幻读: 一个事务中多次读取到的数据的行数不一致(原因是当前事务执行的时候,其它事务使用了INSERT、DELETE操作新增、删除了记录)

4.5 事务传播行为属性

4.5.1 事务传播行为要研究的问题

事务的传播行为要研究的是是当两个方法嵌套执行的时候,外层方法的事务能否传播到内层方法以及怎么传播到内层方法

4.5.2 propagation属性

4.5.2.1 默认值

@Transactional 注解通过 propagation 属性设置事务的传播行为。它的默认值是:

Propagation propagation() default Propagation.REQUIRED;
4.5.2.2 可选值说明

propagation 属性的可选值由 org.springframework.transaction.annotation.Propagation 枚举类提供:

名称含义
REQUIRES_NEW如果当前存在事务(外层方法开启了事务),那么我就先将当前事务暂停,然后我自己新建事务执行,我的事务执行完之后再恢复当前事务,如果当前不存在事务(外层方法没有开启事务),那么我就新建事务执行
REQUIRED 默认值如果当前存在事务(外层方法开启了事务),那么我就加入外层方法的事务一起执行,如果当前不存在事务(外层方法没有开启事务),那么我就自己新建事务执行
SUPPORTS如果当前存在事务(外层方法开启了事务),那么我就加入到外层方法的事务中一起执行,如果当前不存在事务(外层方法没有开启事务),那么我自己就以非事务方式运行
MANDATORY如果当前存在事务(外层方法开启了事务),那么我就加入到外层方法的事务中一起执行,如果当前不存在事务(外层方法没有开启事务),那么我就报错(也可以理解为强制外层方法必须有事务)
NOT_SUPPORTED如果当前存在事务(外层方法开启了事务),那么我就先将当前事务暂停,我自己以非事务方式执行,我执行完之后再恢复当前事务, 如果外层方法不存在事务(外层方法没有开启事务),那么我就以非事务方式执行
NEVER如果当前存在事务(外层方法开启了事务),那么我就直接报错, 如果当前不存在事务(外层方法没有开启事务),那么我就以非事务方式执行

4.5.3 测试事务的传播行为

4.5.3.1 在业务层新建一个接口OuterService
package com.atguigu.service;

public interface OuterService {
    void outerMethod();
}

4.5.3.2 新建一个实现类OuterServiceImpl
package com.atguigu.service.impl;

import com.atguigu.dao.AccountDao;
import com.atguigu.pojo.Account;
import com.atguigu.service.AccountService;
import com.atguigu.service.OuterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OuterServiceImpl implements OuterService {
    @Autowired
    private AccountService accountService;

    @Autowired
    private AccountDao accountDao;

    @Transactional
    @Override
    public void outerMethod() {
        // 1.新增一条数据
        accountDao.add(new Account(3, "ww", 1000));

        // 2.调用AccountService的转账方法
        accountService.transfer(1, 2, 500);
//        int i = 10/ 0;
        // 3.修改id为3的用户的金额,给其加600
        accountDao.updateAccountMoney(3, 600);

    }
}

4.5.3.3 内层方法代码
package com.atguigu.serivce.impl;

import com.atguigu.dao.AccountDao;
import com.atguigu.serivce.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * read-only属性表示事务是否只读:默认值是false,如果设置为true 那么当前事务中只能做数据库的读操作,不能做写操作
 * 该属性的作用:可以对只读的数据库操作做一些优化
 */
@Service
// 类上使用此注解 表示类中每个方法都会生效 如果方法上也设置了 则使用方法上的注解
@Transactional(readOnly = true)
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;


    // timeout 单位:秒  超过指定的时间没响应 则抛出异常
    @Transactional(readOnly = false,
                    timeout = 3,
                    propagation = Propagation.REQUIRED)
    @Override
    public void transfer(Integer fromId, Integer toId, Integer money) throws ClassNotFoundException {
        // 1.转出账户扣款
        accountDao.updateAccountMoney(fromId, -money);

        // 测试报错是否会回滚
        int num = 10 / 0;

        // 什么是运行时异常:不需要在编译时处理的异常
        // 什么是编译时异常:需要在编译时就进行处理的异常
        // 默认情况时遇到运行时异常才回滚
        Class.forName("1eu1j1");



        // 2.转入账户收款
        accountDao.updateAccountMoney(toId, money);
    }
}

4.5.3.4 测试方法
package com.atguigu;

import com.atguigu.controller.AccountController;
import com.atguigu.serivce.OuterService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class TestTransaction {

    @Autowired
    private AccountController accountController;

    @Autowired
    private OuterService outerService;

    @Test
    public void testTransfer() throws ClassNotFoundException {
        accountController.transfer(1, 2, 500);
    }

    @Test
    public void testPropagation(){
        outerService.outerMethod();
    }
}

4.5.3.5 测试REQUIRED模式

在这里插入图片描述

效果:内层方法A、内层方法B所作的修改没有生效,总事务回滚了

内部方法报错 总事物回滚

新增的数据也进行回滚了,可以通过日志进行查看

在这里插入图片描述

4.5.3.6 测试REQUIRES_NEW模式

修改AccountServiceImpl内层方法

    // timeout 单位:秒  超过指定的时间没响应 则抛出异常
    @Transactional(readOnly = false,
                    timeout = 3,
                    propagation = Propagation.REQUIRES_NEW)
    @Override
    public void transfer(Integer fromId, Integer toId, Integer money){
        // 1.转出账户扣款
        accountDao.updateAccountMoney(fromId, -money);

        // 测试报错是否会回滚
        int num = 10 / 0;


        // 2.转入账户收款
        accountDao.updateAccountMoney(toId, money);
    }

效果:内层方法报错了,内层和外层的事务都回滚了

在这里插入图片描述
在这里插入图片描述

如果内层方法没报错,外层方法报错了那么内层事物会提交 外层事务回滚

内层方法代码

@Service
// 类上使用此注解 表示类中每个方法都会生效 如果方法上也设置了 则使用方法上的注解
@Transactional(readOnly = true)
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;


    // timeout 单位:秒  超过指定的时间没响应 则抛出异常
    @Transactional(readOnly = false,
                    timeout = 3,
                    propagation = Propagation.REQUIRES_NEW)
    @Override
    public void transfer(Integer fromId, Integer toId, Integer money){
        // 1.转出账户扣款
        accountDao.updateAccountMoney(fromId, -money);

        // 测试报错是否会回滚
//        int num = 10 / 0;


        // 2.转入账户收款
        accountDao.updateAccountMoney(toId, money);
    }
}

外层方法代码

package com.atguigu.serivce.impl;

import com.atguigu.dao.AccountDao;
import com.atguigu.pojo.Account;
import com.atguigu.serivce.AccountService;
import com.atguigu.serivce.OuterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OuterServiceImpl implements OuterService {

    @Autowired
    private AccountService accountService;

    @Autowired
    private AccountDao accountDao;

    @Transactional
    @Override
    public void outerMethod() {
        // 1.新增一条数据
        accountDao.add(new Account(3, "ww", 1000));

        // 2.调用AccountService的转账方法
        accountService.transfer(1, 2, 500);

        int num = 10 / 0;

        // 3.修改id为3的用户的金额,给其加600
        accountDao.updateAccountMoney(3, 600);
    }
}

测试效果 内部方法执行的转账成功了

在这里插入图片描述
在这里插入图片描述

4.5.3.7 测试SUPPORTS模式

修改内层方法为SUPPORTS模式

   @Transactional(readOnly = false,
                    timeout = 3,
                    propagation = Propagation.SUPPORTS)
    @Override
    public void transfer(Integer fromId, Integer toId, Integer money){
        // 1.转出账户扣款
        accountDao.updateAccountMoney(fromId, -money);

        // 测试报错是否会回滚
        int num = 10 / 0;


        // 2.转入账户收款
        accountDao.updateAccountMoney(toId, money);
    }
}

执行测试方法 外层方法有事务 会加入到外层事务中 报错后事务会回滚

在这里插入图片描述
在这里插入图片描述

外层方法去掉事务后 再进行测试 内部方法报错后 并没有进行回滚(以非事务方式执行)

在这里插入图片描述

在这里插入图片描述

4.5.3.8 事务传播性总结

propagation : 事务的传播性,它是研究方法嵌套执行的时候,内层方法是否会共用外层方法的事务
事务传播性的取值:
(1). REQUIRED : 如果当前存在事务,则加入到当前事务中一起执行,如果当前不存在事务则新建事务执行
(2). SUPPORTS : 如果当前存在事务,则加入当前事务中一起执行,如果当前不存在事务则以非事务方式执行,一般用于只读事务
(3). MANDATORY : 如果当前存在事务,则加入当前事务中一起执行,如果当前不存在事务则报错
(4). REQUIRES_NEW : 新创建事务执行,如果外层方法有事务先将外层方法的事务暂停,等内层方法事务执行完毕之后再恢复外层方法的事务
(5). NOT_SUPPORTED : 以非事务方式执行,如果外层方法有事务则先将外层方法事务暂停,等我执行完之后再恢复外层方法的事务
(6). NEVER : 以非事务方式执行,如果外层方法有事务则报错

事务传播性总结:
(1). 如果一个方法里面只有读操作,一般情况下会设置其事务传播性为SUPPORTS,并且可以设置readOnly为true
(2). 如果一个方法里面有写操作,一般情况下会设置其事务的传播性为REQUIRED
(3). 如果一个方法里面有写操作,并且当前方法执行没有异常就一定要提交事务(不受外层方法影响),此时使用REQUIRES_NEW

5.基于XML配置声明式事务

5.1 加入依赖

相比于基于注解的声明式事务,基于 XML 的声明式事务需要一个额外的依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.1</version>
</dependency>

5.2 迁移代码

将上一个基于注解的 module 中的代码转移到新module。去掉 @Transactional 注解。

5.3 修改spring配置文件

去掉 tx:annotation-driven 标签,然后加入下面的配置:

<!--
        使用配置文件方式配置声明式事务:
        配置文件方式进行声明式事务的配置是写在<tx:advice>标签中,
        该标签的transaction-manager表示使用哪个事务管理器对象来进行事务管理,默认的事务管理器的id是transactionManager
    -->
<tx:advice id="adv1">
    <!--
            tx:attributes表示声明各个方法上使用的事务的属性
        -->
    <tx:attributes>
        <!--
                tx:method表示要使用事务的方法,name属性表示方法名,其它属性表示事务的属性
            -->
        <tx:method name="transfer" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
    </tx:attributes>
</tx:advice>

<!--真正配置事务作用在哪些方法上,要通过aop配置-->
<aop:config>
    <!--配置切入点:表示描述哪些方法要使用事务-->
    <aop:pointcut id="pt1" expression="execution(public void com.atguigu.service.impl.AccountServiceImpl.transfer(Integer,Integer,Double))"/>

    <!--指定事务作用于切入点-->
    <aop:advisor advice-ref="adv1" pointcut-ref="pt1"/>
</aop:config>

5.4 注意

即使需要事务功能的目标方法已经被切入点表达式涵盖到了,但是如果没有给它配置事务属性,那么这个方法就还是没有事务。所以事务属性必须配置。

6.spring5的新特性

6.1 JSP的概述

6.1.1 JCP

JCP(Java Community Process) 是一个由SUN公司发起的,开放的国际组织。主要由Java开发者以及被授权者组成,负责Java技术规范维护,Java技术发展和更新。

JCP官网地址:https://jcp.org/en/home/index

6.1.2 JSR

JSR 的全称是:Java Specification Request,意思是 Java 规范提案。谁向谁提案呢?任何人都可以向 JCP (Java Community Process) 提出新增一个标准化技术规范的正式请求。JSR已成为Java界的一个重要标准。登录 JCP 官网可以查看所有 JSR 标准。

6.2 JSR305的规范

JSR 305: Annotations for Software Defect Detection(提供了一系列的软件缺陷检测(数据校验)的注解)

This JSR will work to develop standard annotations (such as @NonNull) that can be applied to Java programs to assist tools that detect software defects.

主要功能:使用注解(例如@NonNull等等)协助开发者侦测软件缺陷。

Spring 从 5.0 版本开始支持了 JSR 305 规范中涉及到的相关注解。

package org.springframework.lang;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierNickname;
/**
 * A common Spring annotation to declare that annotated elements cannot be {@code null}.
 *
 * <p>Leverages JSR-305 meta-annotations to indicate nullability in Java to common
 * tools with JSR-305 support and used by Kotlin to infer nullability of Spring API.
 *
 * <p>Should be used at parameter, return value, and field level. Method overrides should
 * repeat parent {@code @NonNull} annotations unless they behave differently.
 *
 * <p>Use {@code @NonNullApi} (scope = parameters + return values) and/or {@code @NonNullFields}
 * (scope = fields) to set the default behavior to non-nullable in order to avoid annotating
 * your whole codebase with {@code @NonNull}.
 *
 * @author Sebastien Deleuze
 * @author Juergen Hoeller
 * @since 5.0
 * @see NonNullApi
 * @see NonNullFields
 * @see Nullable
 */
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Nonnull
@TypeQualifierNickname
public @interface NonNull {
}

6.3 相关注解

注解名称含义可标记位置
@Nullable可以为空@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@NonNull不应为空@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@NonNullFields在特定包下的字段不应为空@Target(ElementType.PACKAGE) @TypeQualifierDefault(ElementType.FIELD)
@NonNullApi参数和方法返回值不应为空@Target(ElementType.PACKAGE) @TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})

6.4 整合junit5

6.4.1 导入依赖

在原有环境基础上增加依赖 替换junit的依赖

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.7.0</version>
    <scope>test</scope>
</dependency>

6.4.2 创建测试类

  • @ExtendWith(SpringExtension.class) 表示使用 Spring 提供的扩展功能。
  • @ContextConfiguration(value = {“classpath:spring-context.xml”}) 还是用来指定 Spring 配置文件位置,和整合 junit4 一样。
package com.atguigu;

import com.atguigu.controller.AccountController;
import com.atguigu.serivce.OuterService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(value = "classpath:spring.xml")
public class TestTransaction {

    @Autowired
    private AccountController accountController;

    @Autowired
    private OuterService outerService;

    @Test
    public void testTransfer() throws ClassNotFoundException {
        accountController.transfer(1, 2, 500);
    }

    @Test
    public void testPropagation(){
        outerService.outerMethod();
    }
}

6.4.3 使用复合注解

@SpringJUnitConfig 注解综合了前面两个注解的功能,此时指定 Spring 配置文件位置即可。但是注意此时需要使用 locations 属性,不是 value 属性了。

package com.atguigu;

import com.atguigu.controller.AccountController;
import com.atguigu.serivce.OuterService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

@SpringJUnitConfig(locations = "classpath:spring.xml")
public class TestTransaction {

    @Autowired
    private AccountController accountController;

    @Autowired
    private OuterService outerService;

    @Test
    public void testTransfer() throws ClassNotFoundException {
        accountController.transfer(1, 2, 500);
    }

    @Test
    public void testPropagation(){
        outerService.outerMethod();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/21721.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python之TCP网络编程

目录 1. python3编码转换 2. TCP网络应用程序开发 2.1 概述 2.2 开发流程 2.3 TCP客户端程序开发 2.4 TCP服务端程序开发 2.5 注意点 3. socket之send和recv原理 4. 案例 1. python3编码转换 1.网络传输是以二进制数据进行传输的。 2.数据转化用到了encode和decode函数…

ES6 入门教程 15 Proxy 15.2 Proxy 实例的方法 15.2.1 get()

ES6 入门教程 ECMAScript 6 入门 作者&#xff1a;阮一峰 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录ES6 入门教程15 Proxy15.2 Proxy 实例的方法15.2.1 get()15 Proxy 15.2 Proxy 实例的方法 拦截方法的详细介绍。 15.2.1 get() get方…

应急响应-进程排查

进程排查 进程是计算机中的程序关于某数据集合上的一次运行活动&#xff0c;是系统进行资源分配和调度的基本单位&#xff0c;是操作系统结构的基础。无论在Windows还是Linux中&#xff0c;主机在感染恶意程序后&#xff0c;恶意程序都会启动相应进程来完成恶意操作。 Window…

Android 深入理解View.post() 、Window加载View原理

文章目录背景&#xff1a;如何在onCreate()中获取View的宽高&#xff1f;View.post()原理Window加载View流程setContentView()ActivityThread#handleResumeActivity()总结扩展Window、Activity及View三者之间的关系是否可以在子线程中更新UI资料背景&#xff1a;如何在onCreate…

m认知无线电信号检测算法matlab仿真,能量检测,循环平稳检测以及自相关检测

目录 1.算法概述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法概述 频谱感测是认知无线电的一项关键技术。我们将频谱感知作为一个分类问题&#xff0c;提出一种基于深度学习分类的感知方法。我们归一化接收信号功率以克服噪声功率不确定性的影响。我们使…

postgresql源码学习(49)—— MVCC⑤-cmin与cmax 同事务内的可见性判断

一、 难以理解的场景 postgresql源码学习&#xff08;十九&#xff09;—— MVCC④-可见性判断 HeapTupleSatisfiesMVCC函数_Hehuyi_In的博客-CSDN博客 在前篇的可见性判断中有个一直没想明白的问题 —— 本事务插入的数据&#xff0c;什么场景可能会出现去查询获取快照后插入…

路面坑洼检测中的视觉算法

3D道路成像和路面坑洼检测的经典工作综述。论文链接&#xff1a;https://arxiv.org/pdf/2204.13590.pdf 计算机视觉算法在3D道路成像和路面坑洼检测中的应用已有二十多年的历史。这里先介绍了用于2D和3D道路数据采集的传感系统&#xff0c;包括摄像机、激光扫描仪和微软Kinect…

汉兰达汽车发动机怠速抖动故障诊断方案设计

目录 一、课题简介 1 1.1课题基本内容 1 1.2课题解决的主要问题 1 1.3课题设计思路 1 二、毕业设计成果 2 2.1汉兰达汽车发动机怠速抖动故障现象描述 2 2.2 汉兰达汽车发动机怠速抖动故障原因分析 2 2.3汉兰达汽车发动机怠速抖动故障诊断与排除 6 2.4维修结论与建议 12 三、毕业…

java sleep yield join区别

1、sleep&#xff1a;让出CPU调度&#xff0c;Thread类的方法&#xff0c;必须带一个时间参数。会让当前线程休眠进入阻塞状态并释放CPU&#xff08;阿里面试题 Sleep释放CPU&#xff0c;wait 也会释放cpu&#xff0c;因为cpu资源太宝贵了&#xff0c;只有在线程running的时候&…

高效正则匹配工具

很多人都用过正则&#xff0c;但文章或许会给你一种全新的认识(思考) 以下内容适合高效率正则匹配&#xff08;比较适合正则匹配场景较多的情况&#xff09; 效率提升精华&#xff1a;本地缓存减少编译次数&#xff08;对effective java的思考&#xff0c;以及对数据库连接中…

Java中的装包(装箱)和拆包(装包)

装箱和拆箱 在Java的学习中&#xff0c;我们有的时候会设计装箱和拆箱的概念&#xff08;也就是常说的装包和拆包&#xff09;&#xff0c;这篇博客将详细讲解一下装箱和拆箱的概念及其用途。 装箱&#xff08;装包&#xff09;&#xff1a;将基本数据类型转换成包装类类型 拆…

websocket给指定客户端推送消息

业务场景 最近有一个业务场景是要做实时语音转义&#xff0c;考虑到实时性&#xff0c;所以决定采用websocket实现。 业务场景是A客户端(手机)进行语音转义的结果实时同步到B客户端(pc)&#xff0c;这就需要用到websocket将A转义的结果发送给服务端&#xff0c;服务端接收到A…

软件工程经济学复习题答案

1、利润 收入-成本费用 2、资产 流动资产非流动资产 3、显性成本可以用货币计量&#xff0c;是可以在会计的帐目上反映出来的 4、领取什么保险应缴纳个人所得税 商业保险 某企业一项固定资产的原价为8000 000元&#xff0c;预计使用年限为6年&#xff0c;预计净残值为5 0…

[LeetCode周赛复盘] 第 320 场周赛20221120

[LeetCode周赛复盘] 第 320 场周赛20221120 一、本周周赛总结二、 [Easy] 6241. 数组中不等三元组的数目1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6242. 二叉搜索树最近节点查询1. 题目描述2. 思路分析3. 代码实现四、[Hard] 6243. 到达首都的最少油耗1. 题目描述2. 思路…

10_libpcap以及libnet

知识点1【飞秋欺骗】 1、windwos安装飞秋 双击运行 2、ubuntu安装飞秋 sudo apt-get install iptux ubuntu运行飞秋&#xff1a;iptux& 3、飞秋的格式&#xff1a; 版本:包编号:用户名:主机名:命令字:附加消息 飞秋的端口是2425固定的 1表示上线 32表示普通消息 1_i…

(经典dp) hdu 递推求解专题练习

文章目录前言题单hdu2044 一只小蜜蜂...hdu2045 不容易系列之(3)—— LELE的RPG难题hdu2046 骨牌铺方格hdu2047 阿牛的EOF牛肉串hdu2048 神、上帝以及老天爷hdu2049 不容易系列之(4)——考新郎hdu2050 折线分割平面END前言 题单&#xff1a;递推求解专题练习&#xff08;For Be…

华为机试 - 找出经过特定点的路径长度

目录 题目描述 输入描述 输出描述 用例 题目解析 算法源码 题目描述 无 输入描述 输入一个字符串&#xff0c;都是以大写字母组成&#xff0c;每个相邻的距离是 1&#xff0c; 第二行输入一个字符串&#xff0c;表示必过的点。 说明每个点可过多次。 输出描述 经过这…

精益(Lean)与ERP实施

周四、五看完了24小时不停的Lean Global Connection&#xff0c;总觉得要说些什么。 印象最深的有三个地方&#xff1a; 一是John Shook的话&#xff0c;他说Lean是一种Mindset。 这种Mindset是&#xff1a; 一种积极的态度&#xff0c;Problems solving, 把问题和挑战当成是…

Web 性能优化:TLS

个人博客 Web 性能优化&#xff1a;TCP&#x1f3af; Web 性能优化&#xff1a;TLSWeb 性能优化&#xff1a;HTTP “do it, do it work, do it better … and secure ☠️” 随着追逐利益而来的恶意参与者越来越多&#xff0c;当前的 Web 应用&#xff0c;已经从野蛮生长转而…

【通关MySQL】Java的JDBC编程

✨哈喽&#xff0c;进来的小伙伴们&#xff0c;你们好耶&#xff01;✨ &#x1f6f0;️&#x1f6f0;️系列专栏:【通关MySQL】 ✈️✈️本篇内容:Java的JDBC编程。 &#x1f680;&#x1f680;代码存放仓库gitee&#xff1a;MySQL码云存放&#xff01; ⛵⛵作者简介&#xff…