spring揭秘19-spring事务01-事务抽象

文章目录

  • 【README】
  • 【1】事务基本元素
    • 【1.1】事务分类
  • 【2】java事务管理
    • 【2.1】基于java的局部事务管理
    • 【2.2】基于java的分布式事务管理
      • 【2.2.1】基于JTA的分布式事务管理
      • 【2.2.2】基于JCA的分布式事务管理
    • 【2.3】java事务管理的问题
  • 【3】spring事务抽象概述
    • 【3.1】spring事务抽象的核心接口
  • 【4】spring事务管理的3个接口
    • 【4.1】TransactionDefinition事务定义
      • 【4.1.1】隔离级别
      • 【4.1.2】事务传播行为
      • 【4.1.3】其他属性
      • 【4.1.4】TransactionDefinition实现类
    • 【4.2】TransactionStatus事务状态
      • 【4.2.1】SavepointManager保存点抽象
      • 【4.2.2】TransactionStatus接口实现类
    • 【4.3】PlatformTransactionManager(平台事务管理器)
      • 【4.3.1】PlatformTransactionManager实现类概览
      • 【4.3.2】PlatformTransactionManager内部结构
      • 【4.3.3】AbstractPlatformTransactionManager
  • 【5】spring事务管理代码实践
    • 【5.1】spring集成mybatis及spring事务管理

【README】

本文总结自《spring揭秘》,作者王福强,非常棒的一本书,墙裂推荐;



【1】事务基本元素

1)事务定义:一组sql操作集合; 是操作数据库的最小单元;

2)事务基本元素:

  • ResourceManager:资源管理器; 简称RM; 是对数据库的抽象;
  • TransactionProcessingMonitor:事务处理监控; 简称TPM 或 TP Monitor; 在分布式事务场景中协调包含多个RM(数据库)的事务处理;
  • TransactionManager:事务管理器; 简称TM; 是TP Monitor中的核心模块,负责多RM(数据库)之间事务处理的协调工作,并提供事务界定,事务上下文传播等功能接口;
  • Application:以独立形式存在的或者运行于容器中的应用程序,可以认为是事务边界的触发点;

3)实际上,并不是每个事务的场景中都会使用全部元素;


【1.1】事务分类

1)根据整个事务涉及的RM(数据库)数量来区分:

  • 局部事务: 当前事务只涉及1个RM;我们称当前事务为局部事务(什么叫当前? 个人理解当前指的是当前线程);
  • 全局事务(分布式事务): 整个事务处理过程涉及多个RM;
    • 需要引入TP Monitor来协调多个RM之间的事务处理;
    • TP Monitor采用两阶段提交协议来保证整个事务的ACID属性;

2)全局事务中各参与者之间的关系:

在这里插入图片描述

注意: 局部事务与全局事务(分布式事务)的主要区别在于事务中涉及多少RM,而不是系统中实际有多少RM;



【2】java事务管理

1)事务管理定义: 划分事务边界,规定事务起点与终点

【2.1】基于java的局部事务管理

1)基于java的局部事务管理:要对数据库的访问过程中进行事务管理,每种数据访问技术都提供自身的事务管理api ,如jdbc,mybatis等;

  • 数据库资源的局部事务管理:
    • 基于jdbc的局部事务管理;
    • 基于orm框架(如hibernate,mybaits)的局部事务管理;
  • 消息服务资源的局部事务管理;
    • 如JMS

【2.2】基于java的分布式事务管理

1)java分布式事务管理,主要通过JTA(java事务api, Java Transaction API) 或者 JCA(java连接器架构 ,Java Connector Architecture);


【2.2.1】基于JTA的分布式事务管理

1)JTA:由sun公司提出的标准化分布式事务访问的 java 接口规范。JTA规范定义的只是一套java接口定义,具体实现由相应提供商实现;

2)JTA提供商产品包括:

  • JOTM;
  • Atomikos;
  • JBoss Transaction;

3)使用JTA的分布式事务管理有2种方式:

  • JTA编程事务管理;
  • JTA声明性事务管理;

4)JTA编程事务管理: 通常使用 UserTransaction 接口进行,各应用服务器都提供了针对他的JNDI查找服务;

详情可以参见: https://blog.csdn.net/zzuhkp/article/details/124672647



【2.2.2】基于JCA的分布式事务管理

1)JCA规范主要面向EIS(Enterprise Information System,企业信息系统)的集成; 通过为遗留的EIS系统和java ee 应用服务器指定统一的通信标准;

(具体本文不展开)


【2.3】java事务管理的问题

1)问题1: 事务管理逻辑绑定到了具体的数据访问技术上面,耦合性高;

  • 使用JDBC数据访问技术,通过java.sql.Connection管理事务;使用Hibernate技术访问,则通过Session管理事务;这样使得事务管理代码与数据访问代码甚至业务代码耦合在一起,事务管理本身属于系统功能,不应该散落在各个业务代码逻辑上;(一个合理的设计应该是: 事务管理代码, 与数据访问代码或业务代码独立开
  • 如果升级数据访问技术,则与事务管理相关的代码都需要重构,运维成本高;

2)问题2: 事务异常处理:(没有一个统一的事务相关异常体系,类似于原生SQL异常,每个数据库厂商实现不一样;如果数据库迁移或升级,可能需要修改业务代码,运维成本高)

3)问题3:事务处理API的多样性; 处理事务的API有很多,如JDBC,Mybaits,Hibernate等,但没有统一事务管理api ;



【3】spring事务抽象概述

1)spring对事务元素进行抽象, 客户端仅按照统一的编程模型管理事务,而不用关心数据访问技术以及具体要访问什么类型的事务资源;

  • 此外:spring事务管理与spring提供的数据访问技术(如JdbcTemplate)进行结合;

2)spring事务框架设计的基本原则: 让事务管理逻辑与数据访问逻辑解耦;

具体也可以参见:spring揭秘15-spring集成数据访问技术(orm框架)总结



【3.1】spring事务抽象的核心接口

1)spring事务抽象的核心接口:PlatformTransactionManager ,平台事务管理器; 主要作用是为应用程序提供事务管理的统一接口;

【PlatformTransactionManager】

public interface PlatformTransactionManager extends TransactionManager {
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;// 获取事务

    void commit(TransactionStatus status) throws TransactionException; // 提交事务 

    void rollback(TransactionStatus status) throws TransactionException; // 回滚事务
}

2)PlatformTransactionManager 子类:

  • AbstractPlatformTransactionManager:抽象平台事务管理器;
    • DataSourceTransactionManager:数据源事务管理器;
    • HibernateTransactionManager:Hibernate事务管理器;
    • JdbcTransactionManager:Jdbc事务管理器;
    • JpaTransactionManager:JPA事务管理器;
    • JtaTransactionManager:JTA事务管理器;(分布式事务管理器)
  • CallbackPreferringPlatformTransactionManager:回调偏好平台事务管理器;
  • ResourceTransactionManager:资源事务管理器;


【4】spring事务管理的3个接口

1)spring事务抽象包括3个主要接口: PlatformTransactionManager , TransactionDefinition, TransactionStatus

  • PlatformTransactionManager:平台事务管理器; 负责划定事务边界,即事务起点与终点;
  • TransactionDefinition:事务定义;定义事务相关属性,包括隔离级别, 传播行为等 ;
  • TransactionStatus:事务状态;

在这里插入图片描述



【4.1】TransactionDefinition事务定义

1)TransactionDefinition定义了事务属性:

  • 事务隔离级别;isolation;
  • 事务传播行为;propagation;
  • 事务超时时间;timeout;
  • 是否为只读事务;readOnly ;

【TransactionDefinition】

public interface TransactionDefinition {
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;
    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = 1;
    int ISOLATION_READ_COMMITTED = 2;
    int ISOLATION_REPEATABLE_READ = 4;
    int ISOLATION_SERIALIZABLE = 8;
    int TIMEOUT_DEFAULT = -1;

    default int getPropagationBehavior() {
        return 0;
    }

    default int getIsolationLevel() {
        return -1;
    }

    default int getTimeout() {
        return -1;
    }

    default boolean isReadOnly() {
        return false;
    }

    @Nullable
    default String getName() {
        return null;
    }

    static TransactionDefinition withDefaults() {
        return StaticTransactionDefinition.INSTANCE;
    }
}



【4.1.1】隔离级别

1)TransactionDefinition 定义了如下5个常量用于表示隔离级别(类型为int);

  • ISOLATION_DEFAULT: 如果指定隔离级别为ISOLATION_DEFAULT,则表示使用数据库默认隔离级别,通常情况下是 ReadCommitted;
  • ISOLATION_READ_UNCOMMITTED: 读未提交;
  • ISOLATION_READ_COMMITTED:读已提交;
  • ISOLATION_REPEATABLE_READ:可重复读;
  • ISOLATION_SERIALIZABLE:可串行化;


【4.1.2】事务传播行为

1)事务传播行为: 表示整个事务处理过程所跨越的业务对象,将以什么样的行为参与事务;

class TeachService { // 授课服务
    teach() {
		publicTeacherService.teach(); // 公办老师授课
 		trainingTeacherService.teach(); // 培训学校老师授课        
    }
}

// 公办授课服务 
class PublicTeacherService{
    teach(); // (传播行为设置为 Required )
}

// 培训学校授课服务
class TraningTeacherService {
    teach(); // (传播行为设置为 RequiredNew )
}

【场景解说】

  • PublicTeacherService.teach()方法传播行为为 Required:
  • 若TeachService.teach()本身开启事务1,则 PublicTeacherService.teach()方法复用事务1;
  • 若TeachService.teach()本身没有开启事务,则 PublicTeacherService.teach()方法新开启事务1;
  • TraningTeacherService.teach()方法传播行为为 RequiredNew:
    • 若TeachService.teach()本身开启事务1,则 TraningTeacherService.teach()方法新开启事务2;
    • 若TeachService.teach()本身没有开启事务,则 TraningTeacherService.teach()方法新开启事务2;

2)事务传播行为分类(类型为int):

  • PROPAGATION_REQUIRED: 如果当前存在事务,则当前方法复用该事务;否则当前方法创建新事务;(什么是当前? 当前线程就是当前)(默认的事务传播行为
  • PROPAGATION_SUPPORTS: 如果当前开启事务,则当前方法复用该事务;否则,当前方法直接执行(不开启事务,如事务自动提交);
  • PROPAGATION_MANDATORY:强行要求当前方法存在一个事务,否则抛出异常;
  • PROPAGATION_REQUIRES_NEW:不管当前是否存在事务,都会创建新事务;如果当前存在事务,则把当前事务挂起suspend;
  • PROPAGATION_NOT_SUPPORTED:不支持当前事务,而是在没有事务的情况下执行;若当前存在事务,则当前事务原则上被挂起suspend; 但这要看对应的 PlatformTransactionManager实现类是否支持事务挂起;
  • PROPAGATION_NEVER:永远不需要当前存在事务;若当前存在事务,则抛出异常;
  • PROPAGATION_NESTED:如果存在当前事务,则在当前事务的一个嵌套事务中执行(底层原理是保存点Savepoint,即在同一个事务新建多个保存点);否则与 PROPAGATION_REQUIRED 类似,即创建新事务,在新事务 中执行(注意:并非所有PlatformTransactionManager都支持嵌套事务传播行为 PROPAGATION_NESTED );
    • PROPAGATION_NESTED中的嵌套事务依赖当前事务(底层原理是保存点);它们父子关系;嵌套事务不能独立于当前事务而存在,当前事务与嵌套事务共有事务状态;(嵌套事务应用场景:可以把一个大事务划分为多个小事务来处理,并且外层事务可以根据各个内部嵌套事务的执行结果,选择不同执行流程)
    • PROPAGATION_REQUIRES_NEW新创建的事务与当前事务属于同一个级, 它们是兄弟,相互独立

在这里插入图片描述

【嵌套事务代码示例】

A.service() { // PROPAGATION_REQUIRED, 必须事务
    try {
        B.service() // PROPAGATION_NESTED, 嵌套事务
    } catch(Exception e) {
        C.service();  // PROPAGATION_NESTED, 嵌套事务
    }
}


【4.1.3】其他属性

1)事务超时时间: 通过TIMEOUT_DEFAULT 常量来定义,默认值为-1 ,单位秒; 可以通过 TransactionDefinition 实现类提供自定义的事务超时时间;

2)只读事务:通过 isReadOnly() 方法来定义;



【4.1.4】TransactionDefinition实现类

1)DefaultTransactionDefinition是 TransactionDefinition 的默认实现类,其提供了各种事务属性的默认值;

【DefaultTransactionDefinition】 默认事务定义实现类

public class DefaultTransactionDefinition implements TransactionDefinition, Serializable {
    public static final String PREFIX_PROPAGATION = "PROPAGATION_";
    public static final String PREFIX_ISOLATION = "ISOLATION_";
    public static final String PREFIX_TIMEOUT = "timeout_";
    public static final String READ_ONLY_MARKER = "readOnly";
    static final Map<String, Integer> propagationConstants = Map.of("PROPAGATION_REQUIRED", 0, "PROPAGATION_SUPPORTS", 1, "PROPAGATION_MANDATORY", 2, "PROPAGATION_REQUIRES_NEW", 3, "PROPAGATION_NOT_SUPPORTED", 4, "PROPAGATION_NEVER", 5, "PROPAGATION_NESTED", 6);
    static final Map<String, Integer> isolationConstants = Map.of("ISOLATION_DEFAULT", -1, "ISOLATION_READ_UNCOMMITTED", 1, "ISOLATION_READ_COMMITTED", 2, "ISOLATION_REPEATABLE_READ", 4, "ISOLATION_SERIALIZABLE", 8);
    private int propagationBehavior = 0; // PROPAGATION_REQUIRED
    private int isolationLevel = -1; // ISOLATION_DEFAULT 
    private int timeout = -1;
    private boolean readOnly = false;
    @Nullable
    private String name;

    public DefaultTransactionDefinition() {
    }

    public DefaultTransactionDefinition(TransactionDefinition other) {
        this.propagationBehavior = other.getPropagationBehavior();
        this.isolationLevel = other.getIsolationLevel();
        this.timeout = other.getTimeout();
        this.readOnly = other.isReadOnly();
        this.name = other.getName();
    }

    public DefaultTransactionDefinition(int propagationBehavior) {
        this.propagationBehavior = propagationBehavior;
    }
    // ......
}

2)TransactionDefinition实现类类图如下:
在这里插入图片描述

3)TransactionDefinition实现类按照编程式事务和声明式事务分为两类

  • 编程式事务:TransactionTemplate是进行编程式事务管理的模版方法类; TransactionTemplate继承了TransactionDefinition,即TransactionTemplate本身提供了事务属性设置功能;
  • 声明式事务:TransactionAttribute应用于使用spring aop进行声明式事务管理的场景;它在TransactionDefinition基础上添加了一个rollbackOn() 方法,使用该方法,业务代码通过声明的方式指定业务方法在抛出哪些异常情况下可以回滚事务;
    • 其直接实现类有DefaultTransactionAttribute;

4)DefaultTransactionAttribute下有2个子类,包括 RuledBasedTransactionAttribute 和 DelegatingTransactionAttribute。

  • RuledBasedTransactionAttribute:基于规则的事务属性类; 其允许指定多个回滚规则; RuledBasedTransactionAttribute#rollbackOn()方法将使用传入的异常类型与这些回滚规则进行匹配,然后在决定是否要回滚事务;
    • 回滚规则表示: RollbackRuleAttribute, 或者 NoRollbackRuleAttribute (RollbackRuleAttribute的子类);
  • DelegatingTransactionAttribute:委派事务属性类,是抽象类; 它存在的目的就是被子类化; DelegatingTransactionAttribute会将所有方法调用委派给另一个具体的TransactionAttribute实现类;如DefaultTransactionAttribute,RuledBasedTransactionAttribute;

【TransactionAttribute】事务属性接口定义

public interface TransactionAttribute extends TransactionDefinition {
    @Nullable
    String getQualifier();

    Collection<String> getLabels();

    boolean rollbackOn(Throwable ex);
}

【DefaultTransactionAttribute】默认事务属性接口实现类

public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute {
    @Nullable
    private String descriptor;
    @Nullable
    private String timeoutString;
    @Nullable
    private String qualifier;
    private Collection<String> labels = Collections.emptyList();

    public DefaultTransactionAttribute() {
    }

    public DefaultTransactionAttribute(TransactionAttribute other) {
        super(other);
    }

    public DefaultTransactionAttribute(int propagationBehavior) {
        super(propagationBehavior);
    }
    ...... 
}


【4.2】TransactionStatus事务状态

1)TransactionStatus:表示整个事务处理过程中的事务状态; 通常情况下,我们在编程式事务中使用该接口

【TransactionStatus】

public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
    default boolean hasSavepoint() {
        return false;
    }

    default void flush() {
    }
}

【TransactionExecution】

public interface TransactionExecution {
    default String getTransactionName() {
        return "";
    }

    default boolean hasTransaction() {
        return true;
    }

    default boolean isNewTransaction() {
        return true;
    }

    default boolean isNested() {
        return false;
    }

    default boolean isReadOnly() {
        return false;
    }

    default void setRollbackOnly() {
        throw new UnsupportedOperationException("setRollbackOnly not supported");
    }

    default boolean isRollbackOnly() {
        return false;
    }

    default boolean isCompleted() {
        return false;
    }
}

【SavepointManager】

public interface SavepointManager {
    Object createSavepoint() throws TransactionException;

    void rollbackToSavepoint(Object savepoint) throws TransactionException;

    void releaseSavepoint(Object savepoint) throws TransactionException;
}

【Flushable】

public interface Flushable {

    /**
     * Flushes this stream by writing any buffered output to the underlying
     * stream.
     *
     * @throws IOException If an I/O error occurs
     */
    void flush() throws IOException;
}

【TransactionStatus类图】
在这里插入图片描述

2)TransactionStatus工作方式:

  • 使用TransactionStatus查询事务状态;
  • 通过 setRollbackOnly()方法标记当前事务以便回滚;
  • 如果相应的PlatformTransactionManager支持Savepoint, 可以通过TransactionStatus在当前事务中创建内部嵌套事务;


【4.2.1】SavepointManager保存点抽象

1)保存点的抽象: SavepointManager对Savepoint进行了抽象;通过继承SavepointManager,TransactionStatus 获得可以管理Savapoint的能力,从而支持创建事务内部嵌套事务;

public interface SavepointManager {
    Object createSavepoint() throws TransactionException;

    void rollbackToSavepoint(Object savepoint) throws TransactionException;

    void releaseSavepoint(Object savepoint) throws TransactionException;
}
// TransactionStatus 继承 SavepointManager 
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
    default boolean hasSavepoint() {
        return false;
    }

    default void flush() {
    }
}


【4.2.2】TransactionStatus接口实现类

1)AbstractTransactionStatus:TransactionStatus接口的抽象类, 为子类提供一些基础设施;

  • DefaultTransactionStatus(默认事务状态类): 是TransactionStatus主要实现类; spring事务框架中各个 TransactionManager的实现类,大都使用 DefaultTransactionStatus 来记载事务状态信息;
  • SimpleTransactionStatus:仅供测试,不建议用于生产


【4.3】PlatformTransactionManager(平台事务管理器)

1)PlatformTransactionManager: 负责划定事务边界,即事务起点与终点;

public interface PlatformTransactionManager extends TransactionManager {
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;// 获取事务

    void commit(TransactionStatus status) throws TransactionException; // 提交事务 

    void rollback(TransactionStatus status) throws TransactionException; // 回滚事务
}


【4.3.1】PlatformTransactionManager实现类概览

1)分为面向局部事务与全局事务(分布式事务)共计2个分支;

2)面向局部事务的PlatformTransactionManager实现类

  • AbstractPlatformTransactionManager:抽象平台事务管理器;
    • DataSourceTransactionManager:数据源事务管理器;
    • HibernateTransactionManager:Hibernate事务管理器;
    • JdbcTransactionManager:Jdbc事务管理器;
    • JpaTransactionManager:JPA事务管理器;
  • CallbackPreferringPlatformTransactionManager:回调偏好平台事务管理器;
  • ResourceTransactionManager:资源事务管理器;

3)面向全局事务(分布式事务)的PlatformTransactionManager实现类

  • JtaTransactionManager:JTA事务管理器;(分布式事务管理器);JtaTransactionManager对各种JTA实现提供的分布式事务进行统一封装;但底层会把事务管理操作委派给 具体的JTA实现类;


【4.3.2】PlatformTransactionManager内部结构

1)内部结构如下:

  • TransactionObject:事务对象;承载了当前事务的必要信息,PlatformTransactionManager实现类可以根据事务对象来决定如何处理当前事务; 事务对象类似于 javax.transaction.Transaction 定义;
  • TransactionSynchronization:可以注册到事务处理过程的回调接口;类似于事务监听器,如事务完成后或者回调方法清理系统资源;
  • TransactionSynchronizationManager: 用于管理TransactionSynchronization, 当前事务状态以及具体的事务资源;类似于javax.transaction.TransactionSynchronizationRegistry定义;

2)spring事务抽象把PlatformTransactionManager作为核心接口及顶层接口,具体实现由不同实现类处理;整个事务管理框架的设计结合了策略模式与模版方法模式;PlatformTransactionManager类图如下:

在这里插入图片描述



【4.3.3】AbstractPlatformTransactionManager

1)AbstractPlatformTransactionManager:作为PlatformTransactionManager接口的抽象类,以模版方法模式封装了事务处理逻辑,而把与事务资源相关的操作交给子类实现;

【AbstractPlatformTransactionManager】抽象平台事务管理器主要模版方法

public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, ConfigurableTransactionManager, Serializable {
	// 获取事务
    public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
        // ......
    }   

	// 挂起事务
    @Nullable
    protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
        // ......
    }
	// 恢复事务
    protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder) throws TransactionException {
        // ......
    }  
	// 提交事务
    public final void commit(TransactionStatus status) throws TransactionException {
        // ......
    }
	// 回滚事务 
    public final void rollback(TransactionStatus status) throws TransactionException {
        // ......
    }
}

2) AbstractPlatformTransactionManager定义的事务内部处理逻辑(算法步骤)如下:

  • 根据当前线程是否存在事务,执行不同处理逻辑;
  • 根据 TransactionDefinition指定的传播行为执行后续逻辑;
  • 根据情况挂起或恢复事务;
  • 提交事务前检查 readOnly,若为true,则用回滚事务替代提交事务;
  • 在事务回滚的情况下,清理并恢复事务状态;
  • 若事务的Synchronization 处于active状态,在事务处理的规定时点触发Synchronization 回调接口;

3)事务处理的完成有2种情况,包括回滚事务与提交事务,分别对应rollback() 与commit() 2个模版方法 ;commit()方法在提交事务前会检查rollBackOnly状态,若为true则执行回滚;(补充:可以通过TransactionStatus.setRollbackOnly()方法标记事务是否回滚)

3.1)rollback(TransactionStatus)包含3种情况:

  • 回滚事务:
    • 若当前事务是嵌套事务,则通过TransactionStatus释放 Savepoint;
    • 若当前事务是新事务,则调用子类 doRollback(TransactionStatus)回滚事务;
    • 若当前存在事务,且 rollbackOnly被设置,则调用 子类实现的 doSetRollbackOnly(TransactionStatus)方法,各子类会将TransactionObject的状态设置为 rollbackOnly;
  • 触发Synchronization事件:
  • 清理事务资源:
    • 设置 TransactionStatus.completed为完成状态;
    • 清理与当前事务相关的 Synchronization;
    • 调用 doCleanupAfterCompletion() 方法释放事务资源,并解除 TransactionSynchronizationManager的资源绑定;
    • 若有挂起的事务,则恢复;

3.2)commit(TransactionStatus)处理逻辑如下:

  • 提交事务:
    • 提交事务前判断全局rollBackOnly标志是否为true;如果最外层事务已经被标记为rollBackOnly,并且failEarlyOnGlobalRollbackOnly为true,则抛出异常;
    • 若提交事务前发现TransactionStatus持有Savepoint,则释放它;这实际是在处理嵌套事务的提交;
    • 若 TransactionStatus 表示要提交一个新事务,则调用子类的commit()方法提交事务;
  • 触发Synchronization事件:
  • 若 AbstractPlatformTransactionManager 的 rollbackOnCommitFailure状态被设置为true,则表示若事务提交异常,则回滚;
  • 清理事务资源;


【5】spring事务管理代码实践

1)业务场景: 新增银行卡包括2个步骤,步骤1:先插入银行卡; 步骤2:然后再通过id查询银行卡;

  • 步骤1:插入逻辑在SpringAndMybatisTxMngServiceByRequired中, 事务传播类型为 REQUIRED;
  • 步骤2:查询逻辑在 SpringAndMybatisTxMngServiceByRequiredAndNew ,事务传播类型为 REQUIRES_NEW ;

两个service的事务传播类型不同,以此来比较不同传播模式的事务管理效果


【5.1】spring集成mybatis及spring事务管理

【pom.xml】maven 依赖

<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.tom.springnote</groupId>
  <artifactId>springDiscover</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>springDiscover</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>6.1.10</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>6.1.10</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>6.1.10</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>6.1.10</version>
    </dependency>

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

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>6.1.10</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>6.1.10</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>6.1.10</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>3.0.4</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.16</version>
    </dependency>


    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-dbcp2</artifactId>
      <version>2.12.0</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.32</version>
    </dependency>

    <dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>javax.annotation-api</artifactId>
      <version>1.3.2</version>
    </dependency>

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.3.0</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.22</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/resources</directory>
      </resource>
    </resources>
  </build>

</project>

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

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="locations">
            <value>jdbc-springdiscover.properties</value>
        </property>
    </bean>
    <!--注册数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="${jdbc.url}"/>
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

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

    <!-- 自动扫描注解 -->
    <context:component-scan base-package="com.tom.springnote.chapter20mybaits"/>

    <!-- 通过注解驱动的声明式事务管理 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!-- 注册sql会话工厂 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatisConf.xml"/>
        <!-- 自动扫描mapping.xml文件,**表示迭代查找,也可在sqlMapConfig.xml中单独指定xml文件-->
        <property name="mapperLocations" value="classpath:com/tom/springnote/**/*.mapping.xml"/>
    </bean>

    <!-- mybatis spring sqlSessionTemplate,使用时直接让spring注入即可 -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

</beans>

【jdbc-springdiscover.properties】

jdbc.url=jdbc:mysql://localhost:3306/springdiscover?serverTimezone=Asia/Shanghai
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=root

【mybatisConf.xml】mybaits配置 , 设置mybatis会话操作日志,如新建session,提交事务,回滚事务等日志;

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC
        "-//mybatis.org//DTD Config 3.0//EN"
        "http://www.mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
</configuration>

【SpringTxMngServiceByRequiredAndNewMain】

public class SpringTxMngServiceByRequiredAndNewMain {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext container = new ClassPathXmlApplicationContext("chapter20/mybatis/springAndMybatisIntegrate.xml");
        SpringAndMybatisTxMngServiceByRequired txMngService = container.getBean(SpringAndMybatisTxMngServiceByRequired.class);
        BankCardDto bankCardDto = txMngService.saveAndQryByPropagation(BankCardDto.newBankCardDto(BusiDatetimeUtils.getCurYyyyMmDdHhMmSs()));
        if (bankCardDto == null) {
            System.out.println("null");
        } else {
            System.out.println(bankCardDto);
        }
    }
}

【SpringAndMybatisTxMngServiceByRequired】

@Component("springAndMybatisTxMngServiceByRequired")
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
public class SpringAndMybatisTxMngServiceByRequired {

    @Autowired
    private BankCardDao bankCardDao;

    @Autowired
    private SpringAndMybatisTxMngServiceByRequiredAndNew springAndMybatisTxMngServiceByRequiredAndNew;

    public BankCardDto saveAndQryByPropagation(BankCardDto bankCardDto) {
        System.out.println("====== saveAndQryByPropagation begin");
        this.saveByPropagation(List.of(bankCardDto));
        BankCardDto result = springAndMybatisTxMngServiceByRequiredAndNew.queryById(bankCardDto.getId());
        System.out.println("====== saveAndQryByPropagation end");
        return result;
    }

    public void saveByPropagation(List<BankCardDto> bankCardDtoList) {
        System.out.println("====== saveByPropagation begin");
        bankCardDao.insertBankCard(bankCardDtoList.get(0));
        System.out.println("====== saveByPropagation end");
    }
}

【SpringAndMybatisTxMngServiceByRequiredAndNew】

@Component("springAndMybatisTxMngServiceByRequiredAndNew")
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
public class SpringAndMybatisTxMngServiceByRequiredAndNew {

    @Autowired
    private BankCardDao bankCardDao;

    public BankCardDto queryById(Long id) {
        System.out.println("====== queryById begin");
        BankCardDto result = bankCardDao.qryBankCardById(id);
        System.out.println("====== queryById end");
        return result;
    }

    public BankCardDto saveAndQryByPropagation(BankCardDto bankCardDto) {
        System.out.println("====== saveAndQryByPropagation begin");
        this.saveByPropagation(List.of(bankCardDto));
        BankCardDto result = this.queryById(bankCardDto.getId());
        System.out.println("====== saveAndQryByPropagation end");
        return result;
    }

    public void saveByPropagation(List<BankCardDto> bankCardDtoList) {
        System.out.println("====== saveByPropagation begin");
        if (CollectionUtils.isEmpty(bankCardDtoList)) {
            return;
        }
        bankCardDao.insertBankCard(bankCardDtoList.get(0));
        System.out.println("====== saveByPropagation end");
    }
}

【BankCardDao】

@Repository
public class BankCardDao {

    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;

    private static final String NAMESPACE = "com.tom.springnote.chapter20mybaits.dao.bankcard";

    public void insertBankCard(BankCardDto bankCardDto) {
        sqlSessionTemplate.insert(NAMESPACE + ".insertBankCard", bankCardDto);
    }

    public BankCardDto qryBankCardById(Long id) {
        List<BankCardDto> resultList = sqlSessionTemplate.selectList(NAMESPACE + ".qryBankCardById", id);
        if (CollectionUtils.isEmpty(resultList)) {
            return null;
        } else {
            return resultList.get(0);
        }
    }
}

【BankCard.mapping.xml】

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tom.springnote.chapter20mybaits.dao.bankcard">

    <select id="qryBankCardById" resultType="com.tom.springnote.common.model.BankCardDto">
        select id, card_no, balance, remark
          from bank_card_tbl where id = #{id}
    </select>

    <insert id="insertBankCard">
        insert into bank_card_tbl (id, card_no, balance, remark)
        values (#{id}, #{cardNo}, #{balance}, #{remark})
    </insert>
</mapper>

【打印日志】

信息: {dataSource-1} inited
====== saveAndQryByPropagation begin
	
	====== saveByPropagation begin
		Creating a new SqlSession
		Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@724bade8]
		JDBC Connection [com.alibaba.druid.pool.DruidStatementConnection@6815c5f2] will be managed by Spring
		==>  Preparing: insert into bank_card_tbl (id, card_no, balance, remark) values (?, ?, ?, ?)
		==> Parameters: 20240908163727(Long), 20240908163727(String), 20240908163727(BigDecimal), 备注20240908163727(String)
		<==    Updates: 1
		
		Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@724bade8]
	====== saveByPropagation end
	
	Transaction synchronization suspending SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@724bade8]
	
	====== queryById begin
		Creating a new SqlSession
		Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6ab72419]
		JDBC Connection [com.alibaba.druid.pool.DruidStatementConnection@5bbc9f97] will be managed by Spring
		==>  Preparing: select id, card_no, balance, remark from bank_card_tbl where id = ?
		==> Parameters: 20240908163727(Long)
		<==      Total: 0
		
		Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6ab72419]
	====== queryById end
	
	Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6ab72419]
	Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6ab72419]
	Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6ab72419]
	Transaction synchronization resuming SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@724bade8]

====== saveAndQryByPropagation end
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@724bade8]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@724bade8]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@724bade8]

null
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

【日志解说】

1)事务传播机制:

  • SpringAndMybatisTxMngServiceByRequired 事务传播机制是 Propagation.REQUIRED (当前线程存在事务则复用,否则新建事务);
  • SpringAndMybatisTxMngServiceByRequiredAndNew 事务传播机制是 Propagation.REQUIRES_NEW (无论当前线程是否存在事务,则当前方法新建事务;若当前线程存在事务1,则先挂起事务1,然后新建事务2;事务2执行完成后,恢复事务1

2)新建会话及事务(一个会话就是一个数据库连接,1个会话对应多个事务):

  • SpringAndMybatisTxMngServiceByRequired#saveAndQryByPropagation() 方法先调用 this.saveByPropagation() ,再调用 SpringAndMybatisTxMngServiceByRequiredAndNew.queryById() 方法;其中saveByPropagation的事务传播模式是REQUIRED ; queryById的事务传播模式是REQUIRES_NEW ;
    • this.saveByPropagation() 方法执行时: 创建 SqlSession[DefaultSqlSession@724bade8] ,开启事务,执行insert操作;
    • SpringAndMybatisTxMngServiceByRequiredAndNew.queryById() 执行前:实际先执行spring 事务切面,因为queryById是REQUIRES_NEW ,需要新建事务;所以会先挂起(suspending)当前会话(事务) SqlSession[DefaultSqlSession@724bade8] ,接着再新建会话[DefaultSqlSession@6ab72419]
      • queryById()执行完成后,再提交事务(DefaultSqlSession@6ab72419),关闭会话;
      • 最后一步是恢复(resuming)会话 [DefaultSqlSession@724bade8]
    • SpringAndMybatisTxMngServiceByRequired.saveAndQryByPropagation() 执行完成后: 提交事务,关闭会话 [DefaultSqlSession@724bade8];

3)查询结果为null:

  • SpringAndMybatisTxMngServiceByRequired#saveAndQryByPropagation是事务1,保存时没有提交事务;
  • SpringAndMybatisTxMngServiceByRequiredAndNew#queryById是事务2;因为事务1没有提交,所以事务2查询不到数据,所以是null;(事务隔离级别读已提交)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MSSQL数据库安全配置

预备知识 1、数据库安全的概念 对任何企业组织来说,数据的安全性最为重要。安全性主要是指允许那些具有相应的数据访问权限的用户能够登录到数据库,并访问数据以及对数据库对象实施各种权限范围内的操作,但是要拒绝所有的非授权用户的非法操作。因此安全性管理与用户管理是…

pptpd配置文件/etc/pptpd.conf详解

正文共&#xff1a;1111 字 2 图&#xff0c;预估阅读时间&#xff1a;1 分钟 如果要在Linux系统配置PPTP&#xff08;Point-to-Point Tunneling Protocol&#xff0c;点到点隧道协议&#xff09;VPN&#xff0c;一般是使用pptpd软件。pptpd命令通常从配置文件/etc/pptpd.conf中…

6.第二阶段x86游戏实战2-理解程序流程

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

CNSS Recruit 2024 Web方向 题解WriteUp

文章首发于【先知社区】&#xff1a;https://xz.aliyun.com/t/15586 babyHTTP 开题&#xff0c;Http传参问题 GET&#xff1a; ?CNSShackersPOST&#xff1a; webfunCookie&#xff1a; admintruePHPinfo 开题 根据题目描述&#xff0c;猜测phpinfo.php文件有东西。 phpinfo…

什么是嵌入式?行业前景如何?

目录 什么是嵌入式&#xff1f; 主要特点 常见应用场景 1. 工业自动化 2. 交通运输 3. 智能家居 4. 消费电子 5. 医疗设备 6. 航空航天 7. 物联网&#xff08;IoT&#xff09; 8. 能源管理 行业前景如何&#xff1f; 市场需求强劲 物联网&#xff08;IoT&#xff09;的爆发 汽车…

AutoDroid: LLM-powered Task Automation inAndroid论文学习

光看题目怎么和上一篇差不多&#xff1f;又是纯用LLM的&#xff1f; 当然还是有一点不一样的&#xff1a; 这里的最大特点是加上了UI领域知识&#xff0c;可以大幅增强LLM在处理UI方面的知识的能力。根据文章的说法&#xff0c;使用了这招他们的LLM可以吊打GPT4V&#xff0c;准…

优秀的安防视频监控平台应该具备怎样的视频编解码能力?

随着安防技术的飞速发展&#xff0c;监控平台作为保障公共安全、维护社会秩序的重要工具&#xff0c;其性能与效率日益成为行业关注的焦点。其中&#xff0c;监控平台的视频编码能力在视频监控系统中扮演着至关重要的角色&#xff0c;视频编码技术作为监控系统的核心组成部分&a…

记一次导入dbf文件后数据为空问题的解决方法

前言 省流&#xff1a;这篇文章最终采用的是更换导出文件格式的方法&#xff0c;看到这里觉得方法不适用的小伙伴可以不用浪费几秒钟看完这篇文章哦。 问题描述 作者使用的是Navicat数据库管理工具&#xff0c;然后在将源数据库的数据表导出为dbf格式文件后&#xff0c;再将…

自然语言处理系列六十九》搜索引擎项目实战》搜索框架技术选型

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》&#xff08;人工智能科学与技术丛书&#xff09;【陈敬雷编著】【清华大学出版社】 文章目录 自然语言处理系列六十九搜索引擎项目实战》搜索框架技术选型搜索…

9月11日

使用绘制事件完成钟表的绘制 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpSocket> #include<QMessageBox>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpub…

kitti数据深度图转点云坐标计算方法与教程(代码实现)

文章目录 前言一、kitti深度图官网介绍1、官网深度图介绍2、深度图读取官网代码(python)3、深度图解读1、数据格式内容2、深度图加工3、深度图转相机坐标深度二、kitti数据内参P矩阵解读1、P2矩阵举例2、内参矩阵 (3x3)3、特殊平移向量(第4列)4、kitti的bx与by解释三、kitti深…

CTFHub技能树-信息泄露-HG泄漏

目录 漏洞产生原因 解题过程 当开发人员使用 Mercurial 进行版本控制&#xff0c;对站点自动部署。如果配置不当,可能会将.hg 文件夹直接部署到线上环境。这就引起了 hg 泄露漏洞。 漏洞产生原因 Mercurial(hg)是一种分布式版本控制系统&#xff0c;它与Git类似也可以用于管…

猫眼电影字体破解(图片转码方法)

问题 随便拿一篇电影做样例。我们发现猫眼的页面数据在预览窗口中全是小方框。在当我们拿到源码以后&#xff0c;数据全是加密后的。所以我们需要想办法破解加密&#xff0c;拿到数据。 破解过程 1.源码获取问题与破解 分析 在我们刚刚请求url的时候是可以得到数据的&#xff…

杀毒软件 | Malware Hunter v1.189.0.816 绿色版

软件简介 Malware Hunter是由Glarysoft开发的一款专业安全防护软件。该软件的主要目的是保护用户的计算机免受恶意软件、病毒和其他网络威胁的侵害。它通过采用高效的云引擎和小红伞引擎&#xff0c;能够快速且全面地扫描电脑中的恶意软件&#xff0c;并进行强力清除&#xff…

Definition and Detection of Defects in NFT Smart Contracts论文解读、复现

背景知识\定义 NFT 是数字或物理资产所有权的区块链表示。不仅限于数字图片&#xff0c;视频和画作等艺术品也可以转化为 NFT 进行交易。近年来受到广泛关注&#xff0c;2021 年 NFT 交易额达到约 410 亿美元。 智能合约 是在区块链上运行的图灵完备程序。支持各种去中心化…

第 1 章:原生 AJAX

原生AJAX 1. AJAX 简介 AJAX 全称为 Asynchronous JavaScript And XML&#xff0c;就是异步的 JS 和 XML。通过 AJAX 可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;无刷新获取数据。AJAX 不是新的编程语言&#xff0c;而是一种将现有的标准组合在一…

C# Windows Forms实现绘制画板

目录 C# Windows Forms上绘制画板&#xff1a; 详细解释&#xff1a; TempData临时数据&#xff0c;用来保存画笔相关的信息&#xff0c;如&#xff1a;颜色&#xff0c;大小&#xff0c;坐标等 类声明和成员变量 构造函数 文件菜单项点击事件 保存菜单项点击事件 画笔大…

等待唤醒机制和阻塞队列

1. 等待唤醒机制 由于线程的随机调度&#xff0c;可能会出现“线程饿死”的问题&#xff1a;也就是一个线程加锁执行&#xff0c;然后解锁&#xff0c;其他线程抢不到&#xff0c;一直是这个线程在重复操作 void wait() 当前线程等待&#xff0c;直到被其他线程唤醒 void no…

网络安全(sql注入)

这里写目录标题 一. information_schema.tables 和 information_schema.schemata是information_schema数据库中的两张表1. information_schema.schemata2. information_schema.tables 二. 判断注入类型1. 判断数字型还是字符型注入2. 判断注入闭合是""还是 三. 判断表…

Keras深度学习中文文本分类

一.文本分类概述 文本分类旨在对文本集按照一定的分类体系或标准进行自动分类标记&#xff0c;属于一种基于分类体系的自动分类。文本分类最早可以追溯到上世纪50年代&#xff0c;那时主要通过专家定义规则来进行文本分类&#xff1b;80年代出现了利用知识工程建立的专家系统&…