当前位置: 首页 > news >正文

【Spring】@Transactional事务属性详解

文章目录

  • 1、事务传播行为
    • 注意事务传播行为在不同类之间调用生效
    • Propagation.REQUIRED(默认传播行为)
    • Propagation.REQUIRES_NEW
    • Propagation.NESTED
  • 2、事务的隔离级别
    • 隔离级别设置
  • 3、设置事务异常回滚
    • 3.1、默认情况
    • 3.2、设置回滚异常
    • 3.3、设置不回滚的异常
  • 4、超时时间
  • 5、只读

@Transactional 注解开启事务,其中注解的各种属性详解

1、事务传播行为

事务传播行为详解

事务传播行为是为了解决业务层方法之间互相调用的事务问题

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

举个例子:我们在 A 类的aMethod()方法中调用了 B 类的 bMethod() 方法。这个时候就涉及到业务层方法之间互相调用的事务问题。如果我们的 bMethod()如果发生异常需要回滚,如何配置事务传播行为才能让 aMethod()也跟着回滚呢?这个时候就需要事务传播行为的知识了,如果你不知道的话一定要好好看一下

如下 StudentService中的 changeName 方法有运行时异常

@Service
public class TopService {@Autowiredprivate StudentService studentService;//测试 事务的传播行为@Transactionalpublic void topService(){studentService.changeAge();studentService.changeName();}
}@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;//事务的传播行为@Transactionalpublic void changeAge(){studentDao.updateAgeById(998, 1);}//事务的传播行为,@Transactionalpublic void changeName(){studentDao.updateNameById("dasdas", 1);int i = 1/0;}}

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;......
}

不过,为了方便使用,Spring 相应地定义了一个枚举类:Propagation

package org.springframework.transaction.annotation;import org.springframework.transaction.TransactionDefinition;public enum Propagation {REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),NEVER(TransactionDefinition.PROPAGATION_NEVER),NESTED(TransactionDefinition.PROPAGATION_NESTED);private final int value;Propagation(int value) {this.value = value;}public int value() {return this.value;}}

注意事务传播行为在不同类之间调用生效

在同一个类中,对于@Transactional注解的方法调用,事务传播行为不会生效。

这是因为Spring框架中使用代理模式实现了事务机制,在同一个类中的方法调用并不经过代理,而是通过对象的方法调用,因此@Transactional注解的设置不会被代理捕获,也就不会产生任何事务传播行为的效果。

Propagation.REQUIRED(默认传播行为)

如果父方法有事务,就加入父方法事务,如果没有就新建自己独立的事务!

传播行为在子方法的@Transactional中通过propagation 进行设置。

下面代码中是父方法有事务的情况,propagation 设置为Propagation.REQUIRED,在topService()中调用了studentService.changeAge()和studentService.changeName(),因为事务传播行为为REQUIRED,所以changeAge()和changeName()方法在同一个事务中

此时changeName()发生运行时异常,两个方法同时回滚, 年龄和名字均不会被修改。

@Service
public class TopService {@Autowiredprivate StudentService studentService;//测试 事务的传播行为@Transactionalpublic void topService(){studentService.changeAge();studentService.changeName();}
}@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;//事务的传播行为@Transactional(propagation = Propagation.REQUIRED)public void changeAge(){studentDao.updateAgeById(998, 1);}//事务的传播行为,@Transactional(propagation = Propagation.REQUIRED)public void changeName(){studentDao.updateNameById("dasdas", 1);int i = 1/0;}}

Propagation.REQUIRES_NEW

不管父方法是否有事务,我都新建事务,都是独立的,子方法都是独立的事务

下面代码中,propagation 设置为Propagation.REQUIRES_NEW,在topService()中调用了studentService.changeAge()和studentService.changeName(),因为事务传播行为为REQUIRES_NEW,所以changeAge()和changeName() 子方法是两个独立的事务

此时changeName()发生运行时异常,changeName()发生回滚,不会影响changeAge()方法,年龄将被修改,名字不会修改。

@Service
public class TopService {@Autowiredprivate StudentService studentService;//测试 事务的传播行为@Transactionalpublic void topService(){studentService.changeAge();studentService.changeName();}
}@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;//事务的传播行为@Transactional(propagation = Propagation.REQUIRES_NEW)public void changeAge(){studentDao.updateAgeById(998, 1);}//事务的传播行为,@Transactional(propagation = Propagation.REQUIRES_NEW)public void changeName(){studentDao.updateNameById("dasdas", 1);int i = 1/0;}}

Propagation.NESTED

如果当前存在事务,就在嵌套事务内执行;

  • 在外围方法未开启事务的情况下Propagation.NESTEDPropagation.REQUIRED作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰
  • 在外围方法开启事务的情况下Propagation.NESTED修饰的内部方法属于外部事务的子事务外围主事务回滚,子事务一定回滚而内部子事务可以单独回滚而不影响外围主事务和其他子事务

举个例子:如果 bMethod() 回滚的话,aMethod()不会回滚。如果 aMethod() 回滚的话,bMethod()会回滚。

@Service
Class A {@AutowiredB b;@Transactional(propagation = Propagation.REQUIRED)public void aMethod {//do somethingb.bMethod();}
}@Service
Class B {@Transactional(propagation = Propagation.NESTED)public void bMethod {//do something}
}

2、事务的隔离级别

隔离级别在@Transactional中通过 isolation = Isolation.XXXX 进行设置。

数据库事务的隔离级别是指在多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定。常见的隔离级别包括:

  • 读未提交(READ_UNCOMMITTED):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。
  • 读已提交(READ_COMMITTED):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读
  • 可重复读(REPEATABLE_READ):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。
  • 串行化(SERIALIZABLE):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。

关于四种事务隔离级别,和什么是脏读、不可重复读和幻读,具体可查看MYSQL事务隔离级别知识点和文章Innodb中的事务隔离级别和锁的关系

TransactionDefinition 接口中定义了五个表示隔离级别的常量:

public interface TransactionDefinition {......int ISOLATION_DEFAULT = -1;int ISOLATION_READ_UNCOMMITTED = 1;int ISOLATION_READ_COMMITTED = 2;int ISOLATION_REPEATABLE_READ = 4;int ISOLATION_SERIALIZABLE = 8;......
}

和事务传播行为那块一样,为了方便使用,Spring 也相应地定义了一个枚举类:Isolation

public enum Isolation {DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);private final int value;Isolation(int value) {this.value = value;}public int value() {return this.value;}}

隔离级别设置

隔离级别在@Transactional中通过 isolation = Isolation.XXXX 进行设置。

//isolation = 设置事务的隔离级别,mysql默认是repeatable read!    @Transactional(isolation = Isolation.REPEATABLE_READ)
public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);studentDao.updateNameById("test1",1);
}

3、设置事务异常回滚

事务异常回滚在@Transactional中通过 rollbackFor = xxxException.class 进行设置。

3.1、默认情况

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

下面程序会终止,且不会回滚,且 updateAgeById(100,1) 的方法 会修改数据库成功

@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!* rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!* noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!*/@Transactional(readOnly = false,timeout = 3)public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);//主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内! new FileInputStream("xxxx");studentDao.updateNameById("test1",1);}
}

3.2、设置回滚异常

rollbackFor属性:指定哪些异常类才会回滚,默认是 RuntimeException and Error 异常方可回滚!

下面程序会终止,但是会回滚,因为 FileNotFoundException 属于 Exception异常,updateAgeById方法修改数据库不成功。

/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!* rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!* noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!*/
@Transactional(readOnly = false,timeout = 3,rollbackFor = Exception.class)
public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);//主动抛出一个检查异常,测试! 发现会回滚new FileInputStream("xxxx");studentDao.updateNameById("test1",1);
}

3.3、设置不回滚的异常

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

noRollbackFor属性:指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!

@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!* rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!* noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!*/@Transactional(readOnly = false,timeout = 3,rollbackFor = Exception.class,noRollbackFor = FileNotFoundException.class)public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);//主动抛出一个检查异常,测试! 发现不会回滚,因为设置了noRollbackFornew FileInputStream("xxxx");studentDao.updateNameById("test1",1);}
}

4、超时时间

超时时间在@Transactional中通过 timeout = 3 进行设置。

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

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

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

@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!*/@Transactional(readOnly = false,timeout = 3)public void changeInfo(){studentDao.updateAgeById(100,1);//休眠4秒,等待方法超时!try {Thread.sleep(4000);} catch (InterruptedException e) {throw new RuntimeException(e);}studentDao.updateNameById("test1",1);}
}

5、只读

对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。

设置方式

// readOnly = true把当前事务设置为只读 默认是false!
@Transactional(readOnly = true)

针对DML动作设置只读模式

会抛出下面异常:

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

相关文章:

【Spring】@Transactional事务属性详解

文章目录 1、事务传播行为注意事务传播行为在不同类之间调用生效Propagation.REQUIRED(默认传播行为)Propagation.REQUIRES_NEWPropagation.NESTED 2、事务的隔离级别隔离级别设置 3、设置事务异常回滚3.1、默认情况3.2、设置回滚异常3.3、设置不回滚的异常 4、超时时间5、只读…...

通过css3的锚定滚动属性,实现分页加载时让滚动条不闪动

html标签 <div scroll"handleScroll" id"list-container"style"overflow-anchor:auto;overflow-y: auto;height: 80vh"><ul id"talks"v-for"(item,index) in msgList":key"item.roleiditem.timeitem.conten…...

使用Selenium与Scrapy处理动态加载网页内容的解决方法

博客正文&#xff08;包含详细注释&#xff09; 引言 在爬虫技术领域&#xff0c;处理动态加载的网页内容常常是一项挑战&#xff0c;尤其是对于那些通过用户滚动或其他交互动态加载更多内容的网站。本文将介绍如何结合使用Selenium和Scrapy来有效处理这类网页。 初探Seleni…...

Linux的权限(二)

目录 前言 文件类型和访问权限&#xff08;事物属性&#xff09; 补充知识 文件类型 文件操作权限 修改文件权限 chmod指令 文件权限值的表示方法 字符表示方法 8进制数值表示方法 权限有无带来的影响 修改文件角色 chown与chgrp指令 目录的rwx权限 补充知识 …...

网络服务IP属地发生变化的原因有哪些?

近期&#xff0c;许多用户发现自己的网络服务IP属地发生了变化。原本固定的IP地址不再是静态的&#xff0c;而是发生了变动。这一现象引起了广大用户的关注和疑惑&#xff0c;对网络服务的使用和信息安全产生了影响。为了解决用户的疑虑&#xff0c;我们对此现象进行了深入探究…...

OpenGL 着色器程序的保存和加载(二进制)

背景 为了提高OpenGL 着色器程序的编译和链接速度&#xff0c;我们可以将程序保存为二进制进行加载&#xff0c;可以大幅度提升加载效率。 方法 以下是加载和保存二进制程序的方法。 // 加载着色器程序的二进制文件到已创建的着色器程序中 bool loadPragram(const std::str…...

【Unity 实用工具篇】| 游戏多语言解决方案,官方插件Localization 实现本地化及多种语言切换

前言 【Unity 实用工具篇】| 游戏多语言解决方案&#xff0c;官方插件Localization 实现本地化及多种语言切换一、多语言本地化插件 Localization1.1 介绍1.2 效果展示1.3 使用说明 二、 插件导入并配置2.1 安装 Localization2.2 全局配置 三、多语言映射表3.1 创建多语言文本配…...

疯狂SQL转换系列- SQL for Tencent Cloud VectorDB

为了尽量保证使用者通过统一的SQL标准访问各类型数据库&#xff0c;我们这里开启了“疯狂SQL转换系列”。转换的语法效果不一定是最好的&#xff0c;更多是为用户提供一个统一的数据库交互体验。转换数据库目标的确认更多是内生的。基于我们对业务发展的需要。该向量库SQL转换的…...

Excel中的INDIRECT函数用法

当在 Excel 中使用 INDIRECT 函数时&#xff0c;它可以帮助我们通过引用字符串中的单元格地址来获取对应单元格的值。这个函数非常有用&#xff0c;特别是在需要动态地引用其他单元格的情况下。下面是 INDIRECT 函数的一些用法和示例&#xff1a; 基本用法&#xff1a; INDIREC…...

Spring-temp

IOC/DI实现步骤 1.配置元数据 2.实例化IOC 3.获取Bean 基于XML配置方式 管理组件 1.基于构造函数&#xff1a;有参、无参 2.基于静态工厂方法&#xff1a;有参、无参 依赖注入 1.构造函数 2.setter方法 Bean组件高级特性 1.作用域 2.生命周期 FactoryBean 基于注解 IOC Bean作…...

【C++干货铺】会搜索的二叉树(BSTree)

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 前言&#xff1a; 二叉搜索树 二叉搜索树概念 二叉搜索树操作 二叉搜索树的查找 二叉搜索树的插入 二叉搜索树元素的删除 ​二叉搜索树的实现 BSTree结点 …...

【Spring AOP】 动态代理

一.AOP常见的实现方式 1.Spring AOP 2.aspectJ 注意:spring使用的是aspectJ的注解,但实现是spring自身实现的. 二.AOP原理 Spirng AOP原理 , 基于动态代理实现的. 三.代理模式 作用就是提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类…...

NAT——网络地址转换

目录 一、概念 二、NAT的分类 1.静态NAT 1.1 静态NAT的配置 1.2 利用eNSP小实验加强对静态NAT的理解 2、动态NAT 三、NAPT——端口映射 四、Easy IP 使用一个公网地址可以让所有人都上公网 一、概念 随着Internet的发展和网络应用的增多&#xff0c;IPv4地址枯竭已经成为…...

Lambda 表达式的常见用法

文章目录 Lambda 表达式的常见用法使用Lambda表达式集合遍历使用Lambda表达式排序使用Lambda表达式过滤使用Lambda表达式映射使用Lambda表达式归约使用Lambda表达式分组使用Lambda表达式函数式接口的实现使用Lambda表达式线程的创建使用Lambda表达式进行Optional 操作使用Lambd…...

成本管理常用的ChatGPT通用提示词模板

成本分析&#xff1a;如何进行成本分析&#xff1f; 成本核算&#xff1a;如何进行成本核算&#xff1f; 成本控制&#xff1a;如何控制成本&#xff1f; 成本效益分析&#xff1a;如何进行成本效益分析&#xff1f; 成本预测&#xff1a;如何预测成本&#xff1f; 成本决…...

如何在PHP中处理日期和时间?

在 PHP 中&#xff0c;你可以使用内置的 DateTime 类和相关函数来处理日期和时间。以下是一些常见的日期和时间操作的示例&#xff1a; 使用 DateTime 类&#xff1a; 获取当前日期和时间&#xff1a; $currentDateTime new DateTime(); echo $currentDateTime->format(Y-…...

NO-IOT翻频,什么是翻频,电信为什么翻频

1.1 翻频迁移最终的目的就是减少网络的相互干扰&#xff0c;提供使用质量. 1.2 随着与日俱增的网络规模的扩大&#xff0c;网内干扰已成了影响网络的质量标准之一&#xff0c;为了保障电信上网体验&#xff0c;满足用户日益增长的网速需求,更好的服务客户&#xff0c;电信针对…...

云原生之深入解析OOM和CPU节流

一、前言 使用 Kubernetes 时&#xff0c;内存不足 (OOM) 错误和 CPU 节流是云应用程序中资源处理的主要难题&#xff0c;这是为什么呢&#xff1f;云应用程序中的 CPU 和内存要求变得越来越重要&#xff0c;因为它们与云成本直接相关。通过 limits 和 requests &#xff0c;可…...

数据结构与算法之递归: LeetCode 93. 复原 IP 地址 (Typescript版)

复原 IP 地址 https://leetcode.cn/problems/restore-ip-addresses/ 描述 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 ‘.’ 分隔。 例如&#xff1a;“0.1.2.201” 和 “192.…...

json模块与jsonpath详解

数据提取之JSON与JsonPATH JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式&#xff0c;它使得人们很容易的进行阅读和编写。同时也方便了机器进行解析和生成。适用于进行数据交互的场景&#xff0c;比如网站前台与后台之间的数据交互。 JSON和XML的比较可谓不…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...