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

【JavaEE】Spring事务-事务的基本介绍-事务的实现-@Transactional基本介绍和使用

【JavaEE】Spring 事务(1)

在这里插入图片描述

文章目录

  • 【JavaEE】Spring 事务(1)
    • 1. 为什么要使用事务
    • 2. Spring中事务的实现
      • 2.1 事务针对哪些操作
      • 2.2 MySQL 事务使用
      • 2.3 Spring 编程式事务(手动挡)
      • 2.4 Spring 声明式事务(自动挡)
      • 2.5 小疑问(@Transactional注解原理)
        • 2.5.1 @Transactional注解原理
        • 2.5.2 为什么必须被五大类注解修饰
        • 2.5.3 为什么@Transactional不支持static方法
    • 3. 实践
      • 3.1 创建项目
      • 3.2 编写代码
      • 3.3 测试
      • 3.4 注意事项
        • 3.4.1 事务不会自动回滚解决方案1:重新抛出
        • 3.4.2 事务不会自动回滚解决方案2:手动回滚

【JavaEE】Spring 事务(1)

1. 为什么要使用事务

比如跟钱相关的两个操作:

第一步操作:小马卡里 - 100元

第二步操作:老马卡里 + 100元

这就是一个事务,捆在一起的一组行为,就是事务

白色背景中捆在一起的两个蜡烛图片下载 - 觅知网

而它能保证的是,这个行为的原子性,一致性,隔离性,持久性:

  1. 两个操作都成功
  2. 两个操作都失败

要么一起成功,要么一起失败

但是,如果没有事务呢,则两个操作逻辑上是分开的:

  • 第一个操作成功,第二个操作失败,则小马直接亏了100!

2. Spring中事务的实现

Spring中的事务操作主要分为两类:

  1. 编程式事务(原生方式去写代码操作事务)
  2. 声明式事务(利用注解,“约定规则”去自动开启和提交事务)

2.1 事务针对哪些操作

事务一般针对的是

  1. 持久化相关的操作,如数据库操作、文件系统操作等

正如刚才的那样,两个用户的转账操作

  1. 保证数据完整性的操作,如消息队列等

通过使用事务,可以在消息队列中提供可靠的消息传递机制,减少消息丢失或重复处理的可能性,同时确保系统在出现故障情况下能够正确恢复

事务的概念适用于需要保证一系列操作的原子性和一致性的任何场景

  • 而其中,被持久化的数据,被传播的数据…等操作,都具有 “持久性影响” 的作用,所以要通过事务来控制其影响不要太糟糕
  • 而一些操作,比如打印,都打印到控制台了,不会回滚的,也没有必要回滚,例如查看执行日志…
    • 至于其他的不可见的操作,又没有持久化,是没有影响力的,程序出异常后,这些数据也销毁了~

2.2 MySQL 事务使用

--- 开启事务
start transaction;
--- transaction就是事务的意思--- 提交事务
commit;--- 回滚事务
rollback;

三个重要的操作:

  1. 开启事务
  2. 提交事务
  3. 回滚事务

2.3 Spring 编程式事务(手动挡)

与MySQL操作事务类似:

  1. 开启事务(获取一个事务/创建一个事务并获取)
  2. 提交事务
  3. 回滚事务

SpringBoot 内置了两个对象:

  1. DataSourceTransactionManager ⽤来获取事务(开启事务)、提交或 回滚事务的
  2. TransactionDefinition 是事务的属性,在获取事务的时候需要将 TransactionDefinition 传递进去从而获得⼀个事务 TransactionStatus

实现代码如下:

@RestController
public class UserController {@Resourceprivate UserService userService;// JDBC 事务管理器@Resourceprivate DataSourceTransactionManager dataSourceTransactionManager;// ------------定义事务属性------------@Resourceprivate TransactionDefinition transactionDefinition;@RequestMapping("/sava")public Object save(User user) {// ------------开启事务------------TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);// ------------插⼊数据库------------int result = userService.save(user);// ------------提交事务------------dataSourceTransactionManager.commit(transactionStatus);// // ------------回滚事务------------// dataSourceTransactionManager.rollback(transactionStatus);return result;}
}

反正就是,麻烦,难记,不简洁,容易出错(难写)

2.4 Spring 声明式事务(自动挡)

声明式事务的实现很简单

  • 只需要在需要的类或者方法上添加 @Transactional 注解 就可以实现了

无需手动开启/提交/回滚事务:

  1. 进入方法,自动开启事务
  2. 方法执行完会,自动提交事务
  3. 如果中途发生了没有处理的异常,自动回滚事务

具体规则/作用范围是:

  • 加在类上,内部的所有非静态public方法都相当于加了 @Transactional 注解
  • 加在非静态public方法上,这个方法就是一个事务
  • 所在的类,必须被五大类注解修饰,这跟其事务的实现有关
    • 而且有了五大类注解,Spring开发才能进行呀~

代码实现:

@Service
@Transactional
public class Test {@Autowiredprivate Mapper mapper;public int save(User user) {mapper.save(user);}
}

@RequestMapping("/save")
@Transactional
public Object save(User user) {int result = userService.save(user);return result;
}

跟往常的注解版不使用注解版的代码一样:

  1. 不使用注解版: 灵活,能实现很多功能,但是麻烦,使用困难,甚至正常人压根没法写,例如事务传播机制的代码实现起来就比较复杂
  2. 使用注解版: 使用规则约束,实现特定功能,但是方便,使用简单,且足以面对正常开发环境,不关心一些极端的不正常开发
    • 对于注解的使用,就是:遵循约定,坐享其成,明白逻辑(作用),合理使用(逻辑分析合理)

编程式就相当于车的手动挡,声明式就相当于车的自动挡,那么现实咱们买不起偏贵的自动挡车,而我们现在可以无条件舒适地使用自动挡,那咋不用嘞🤣🤣🤣

2.5 小疑问(@Transactional注解原理)

2.5.1 @Transactional注解原理

在这里插入图片描述

  • 这个行为,可能你也意识到了,其实就是AOP,对@Transactional注解下的代码,进行统一的处理
    • 当然,对于不同的事务/复杂事务,处理可能不同~
  • 这个在执行日志中也能看到,可以平时观察观察~

@Transactional 实现思路图:

在这里插入图片描述

@Transactionl执行思路图:

在这里插入图片描述

默认就是这么一个事务管理器执行这样的逻辑

  • 而如果配置了多个事务管理器,则需要通过参数value/transactionManager去指定

2.5.2 为什么必须被五大类注解修饰

其实就是因为

@Transactional注解是基于Spring AOP的,而Spring AOP则通过JDK的或者CGLib的动态代理来实现AOP

对于使用@Transactional注解来实现事务管理,确实是通过动态代理来实现的

  • 当你在一个类或方法上添加了@Transactional注解时,Spring会通过动态代理在运行时为该类或方法创建一个代理对象。这个代理对象会拦截调用,并在适当的时机开启、提交或回滚事务

由于动态代理的实现方式,确实需要满足一些条件才能使@Transactional注解生效

  • 具体来说,被注解的类或方法必须是Spring容器中的bean,而Spring容器会自动为标注了@Service@Controller@Repository@Component@Configuration等注解的类创建bean实例。这也是为什么我之前提到了五大类注解

2.5.3 为什么@Transactional不支持static方法

其实就是因为

无论JDK还是CGlib都无法对静态方法提供代理。原因在于静态方法是类级别的,调用需要知道类信息,而类信息在编译器就已经知道了,并 不支持在运行期的动态绑定

3. 实践

3.1 创建项目

为了方便,我就直接使用之前mybatis项目里写过的代码了

  • 因为我们目前侧重学习的点是在事务的实现!

model.UserInfo:

@Component
@Data
public class UserInfo {private int id;private String username;private String password;private String photo;private LocalDateTime createtime;private LocalDateTime updatetime;private Integer state;public UserInfo(String username, String password, Integer state) {this.username = username;this.password = password;this.state = state;}public UserInfo() {}
}

mapper.UserMapper:

@Mapper
//跟五大类注解@Repository,say 拜拜
public interface UserMapper {List<UserInfo> getAll(); //获得所有用户信息UserInfo getUserById(Integer id);//通过id查找用户UserInfo getUserByUsername(@Param("username") String username);//通过username查找用户List<UserInfo> getAll2(@Param("option") String option);UserInfo login(@Param("username") String username, @Param("password") String password);int update(UserInfo userInfo);//删除状态为state的用户int delete(@Param("state") Integer state);//增加用户int insert(UserInfo userInfo);List<UserInfo> getAllLikeSome(@Param("likeString") String likeString);//用户注册提交信息
//    int add(UserInfo userInfo);int add(String username, String password, Integer state, Integer id);int add2(UserInfo userInfo);List<UserInfo> select1(UserInfo userInfo);int update2(UserInfo userInfo);int deleteByIDs(List<Integer> list);int insertByUsers(List<UserInfo> list, List<UserInfo> list2);}

mybatis.UserInfoMapper.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.example.demo.mapper.UserMapper"><resultMap id="BaseMap" type="com.example.demo.model.UserInfo"></resultMap><select id="getAll" resultMap="BaseMap">select * from userinfo</select><!--    <select id="getAll" resultType="com.example.demo.model.UserInfo">-->
<!--        select id, username as name, password, photo,-->
<!--            createtime, updatetime, state from userinfo-->
<!--    </select>--><select id="getUserById" resultType="com.example.demo.model.UserInfo">select * from userinfo where id = #{id}</select><select id="getUserByUsername" resultType="com.example.demo.model.UserInfo">select * from userinfo where username = ${username}</select><select id="getAll2" resultType="com.example.demo.model.UserInfo">select * from userinfo order by id ${option}</select><select id="login" resultType="com.example.demo.model.UserInfo">select * from userinfo where username = '${username}'and password = '${password}'</select><update id="update">update userinfo set state = #{state} where username = #{username}</update><delete id="delete">delete from userinfo where state = #{state}</delete><insert id="insert" useGeneratedKeys="true"keyColumn="id" keyProperty="id">
<!--    自增主键 id 不能为null也没有默认值,如果id不设置或者设置为null,都会导致自增    -->insert into userinfo (username, password) values (#{username}, #{password});</insert><select id="getAllLikeSome" resultType="com.example.demo.model.UserInfo">select * from userinfo where username like concat('%', #{likeString}, '%')</select><insert id="add">insert into userinfo (<if test="id != 0">id,</if><if test="username != null">username,</if><if test="password != null">password,</if><if test="state != null">state</if>) values (<if test="id != 0">#{id},</if><if test="username != null">#{username},</if><if test="password != null">#{password},</if><if test="state != null">#{state}</if>)</insert><insert id="add2">insert into userinfo<trim prefix="(" suffix=")"suffixOverrides=","><if test="id != 0">id,</if><if test="username != null">username,</if><if test="password != null">password,</if><if test="state != null">state</if></trim>values<trim prefix="(" suffix=")"suffixOverrides=","><if test="id != 0">#{id},</if><if test="username != null">#{username},</if><if test="password != null">#{password},</if><if test="state != null">#{state}</if></trim></insert><select id="select1" resultType="com.example.demo.model.UserInfo">select * from userinfo<where><if test="id != 0">id = #{id}</if><if test="username != null">or username = #{username}</if><if test="password != null">or password = #{password}</if><if test="state != null">or state = #{state}</if></where>
<!--        <trim prefix="where" prefixOverrides="and">-->
<!--            <trim prefixOverrides="or">-->
<!--                <if test="id != 0">-->
<!--                    id = #{id}-->
<!--                </if>-->
<!--                <if test="username != null">-->
<!--                    or username = #{username}-->
<!--                </if>-->
<!--                <if test="password != null">-->
<!--                    or password = #{password}-->
<!--                </if>-->
<!--                <if test="state != null">-->
<!--                    or state = #{state}-->
<!--                </if>-->
<!--            </trim>-->
<!--        </trim>--></select><update id="update2">update userinfo<set><if test="username != null">username = #{username},</if><if test="password != null">password = #{password},</if><if test="state != null">state = #{state}</if></set>where id = #{id}</update><delete id="deleteByIDs">delete from userinfo where id in<foreach collection="list" open="(" close=")" item="x" separator=",">#{x}</foreach></delete><insert id="insertByUsers">insert into userinfo(username, password, state) values<foreach collection="list" item="x" open="(" close=")" separator="),(">#{x.username}, #{x.password}, #{x.state}</foreach>,<foreach collection="list2" item="x" open="(" close=")" separator="),(">#{x.username}, #{x.password}, #{x.state}</foreach></insert>
</mapper>

application.properties:

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test_db?characterEncoding=utf8
# MyBatis 基于jdbc实现~ 底层用的就是jdbc:mysql协议,这个地址是本地数据库的地址,test_db就是我们的那个数据库
spring.datasource.username=root
# 用户名,默认固定是root
spring.datasource.password=mmsszsd666
# 密码,是数据库的密码
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# MyBatis配置信息
mybatis.mapper-locations=classpath:mybatis/*Mapper.xml#  执行时打印SQL
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#由于其默认情况下的日志类型为Debug,重要程度不高,所以我们需要设置我们对应的目录下的日志级别
logging.level.com.example.demo.controller=debug#将数据库中的下换线转换成驼峰,比如 user_name -> userName
mybatis-plus.configuration.map-underscore-to-camel-case=true

目录结构:

在这里插入图片描述

3.2 编写代码

同样的controller接受请求,service调用方法~

在这里插入图片描述

在这里插入图片描述

加@Transactional:

在这里插入图片描述

3.3 测试

在这里插入图片描述

访问路由前:

delete from userinfo;

现在userinfo一条数据都没有了~

效果:

浏览器:

在这里插入图片描述

控制台:

在这里插入图片描述

数据库:

在这里插入图片描述

  • 符合预期:还是空的
    • 因为发生了因为@Transactional捕获到了异常,发生回滚

在这里插入图片描述

去这段代码后,效果:

在这里插入图片描述

在这里插入图片描述

3.4 注意事项

@Transactional 在异常被 try{}catch(){} 捕获的情况下,不会进行事务自动回滚,这也很好理解,因为 try{}catch(){} 后,后面的代码可以继续运行,这个异常是被我们写的 try{}catch(){} 抢走处理了,注解是捕获不到的~

代码:

在这里插入图片描述

效果:

浏览器:

在这里插入图片描述

控制台:

在这里插入图片描述

数据库:

在这里插入图片描述

说明没有回滚

3.4.1 事务不会自动回滚解决方案1:重新抛出

在这里插入图片描述

效果:

在这里插入图片描述

在这里插入图片描述

无新增数据,代表回滚成功

但是这不太美观,“优雅”,过于暴力

3.4.2 事务不会自动回滚解决方案2:手动回滚

TransactionAspectSupport.currentTransactionStatus() 可以得到当前的事务,然后设置回滚方法setRollbackOnly就可以实现将当前事务的回滚了

  • 跟切面有关=>aop

在这里插入图片描述

效果:

在这里插入图片描述

在这里插入图片描述

无新增数据,代表回滚成功

这种方式就比较“优雅”了~


文章到此结束!谢谢观看
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆

代码:事务/src/main · 游离态/马拉圈2023年8月 - 码云 - 开源中国 (gitee.com)


相关文章:

【JavaEE】Spring事务-事务的基本介绍-事务的实现-@Transactional基本介绍和使用

【JavaEE】Spring 事务&#xff08;1&#xff09; 文章目录 【JavaEE】Spring 事务&#xff08;1&#xff09;1. 为什么要使用事务2. Spring中事务的实现2.1 事务针对哪些操作2.2 MySQL 事务使用2.3 Spring 编程式事务&#xff08;手动挡&#xff09;2.4 Spring 声明式事务&…...

CentOs下面安装jenkins记录

目录 一、安装jenkins 二、进入jenkins 三、安装和Gitee&#xff0c;Maven , Publish Over SSH等插件 四、构建一个maven项目 一、安装jenkins 1 wget -O /etc/yum.repos.d/jenkins.repo \ https://pkg.jenkins.io/redhat-stable/jenkins.repo 2 rpm --im…...

海康威视相机-LINUX SDK 开发

硬件与环境 相机&#xff1a; MV-CS020-10GC 系统&#xff1a;UBUNTU 22.04 语言&#xff1a;C 工具&#xff1a;cmake 海康官网下载SDK 运行下面的命令进行安装 sudo dpkg -i MVSXXX.deb安装完成后从在/opt/MVS 路径下就有了相关的库&#xff0c;实际上我们开发的时候只需要…...

AI助力智能安检,基于图像目标检测实现危险品X光智能安全检测系统

基于AI相关的技术来对一些重复性的但是又比较重要的工作来做智能化助力是一个非常有潜力的场景&#xff0c;关于这方面的项目开发实践在我之前的文章中也有不少的实践&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a;《AI助力智能安检&#xff0c;基于目标检测模型实现…...

开源软件的崛起:历史与未来

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…...

apk 静默安装

apk 静默安装 - 欧颜柳 - 博客园 (cnblogs.com) 如果需要应用进行静默安装&#xff0c;则需要满足一下两个条件 1 必须添加权限 <uses-permission android:name"android.permission.INSTALL_PACKAGES" /> 2 必须是系统应用&#xff0c;或者系统签名应用 方法…...

Unity记录4.2-存储-从json文件获取Tile路径

文章首发见博客&#xff1a;https://mwhls.top/4813.html。 无图/格式错误/后续更新请见首发页。 更多更新请到mwhls.top查看 欢迎留言提问或批评建议&#xff0c;私信不回。 汇总&#xff1a;Unity 记录 摘要&#xff1a;从json文件获取Tile材质路径。 确定保存方案-2023/08/1…...

vue3页面传参?

...

NB水表和LoRa水表有哪些不同之处?

NB水表和LoRa水表是两种目前市场上常见的智能水表&#xff0c;它们在功能、性能、应用场景等方面存在一些不同之处。 一、技术方面 NB水表采用NB-IoT技术&#xff0c;而LoRa水表采用LoRa技术。NB-IoT技术是窄带物联网技术&#xff0c;它具有良好的低功耗、低成本、高覆盖、高可…...

Java进阶(6)——抢购问题中的数据不安全(非原子性问题) Java中的synchronize和ReentrantLock锁使用 死锁及其产生的条件

目录 引出场景&#xff1a;大量请求拥挤抢购事务的基本特征ACID线程安全的基本特征 加锁(java)synchronized锁ReentrantLock锁什么是可重入锁&#xff1f;如何保证可重入 滥用锁的代价&#xff1f;&#xff08;死锁&#xff09;死锁的四个必要条件死锁的案例 总结 引出 1.大量请…...

SpringBoot初级开发--加入Log4j进行日志管理打印(6)

日志记录在整个java工程开发中占着很重要的比重&#xff0c;因为很多问题的排查需要通过日志分析才能确认。在SpringBoot中我用得最多的就是log4j这个日志框架。接下来我们具体配置log4j. log4j定义了8个级别的log&#xff08;除去OFF和ALL&#xff0c;可以说分为6个级别&#…...

计算机竞赛 基于GRU的 电影评论情感分析 - python 深度学习 情感分类

文章目录 1 前言1.1 项目介绍 2 情感分类介绍3 数据集4 实现4.1 数据预处理4.2 构建网络4.3 训练模型4.4 模型评估4.5 模型预测 5 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于GRU的 电影评论情感分析 该项目较为新颖&#xff0c;适合作为竞…...

android logcat问题 怎么换成旧版

参考 如果想切换回旧版LOGCAT&#xff0c;按照下方步骤设置即可 File->Settings->Expermental->Logcat->Enable new Logcat tool window&#xff1a;取消勾选 设置好后上方会有一个Toast&#xff0c;询问你是否使用新版logcat&#xff0c;关掉即可 最新测试版移…...

监听的用法watch

1、当想停止某页面定时刷新&#xff08;监听路由的变化&#xff09; /**组件被移除时调用 */deactivated() {clearInterval(this.timer);this.timer null;},/**监听路由变化是否刷新 */watch: {// 方法1 //监听路由是否变化$route(to, from) {if (to.name "xxx") {…...

XML—标记语言

什么是XML&#xff1f; Extensible Markup Language&#xff0c;可扩展标记语言。 那标记语言是什么&#xff1f; 用文字做标记表达一些效果或携带一些数据。比如&#xff1a;HTML、XML 我的理解&#xff1a;用倾盆大雨表达雨很大 那XML为什么说是可扩展的呢&#xff1f; 还…...

图数据库Neo4j学习五渲染图数据库neo4jd3

文章目录 1.现成的工具2.Neo4j JavaScript Driver3.neovis4.neo4jd34.1neo4jd3和neovis对比4.2获取neo4jd34.3neo4jd3的数据结构4.4Spring data neo4.4.1 定义返回数据格式4.4.1.1NeoResults4.4.1.2GraphVO4.4.1.3NodeVO4.4.1.4ShipVO 4.4.2 SDN查询解析4.4.2.1 Repo查询语句4.…...

AI增强的社交网络·导师·电话客服……

本月共更新80条知识&#xff0c; 智能时代&#xff0c;人与人之间的差距&#xff0c;体现在前沿知识的整合上。 # BeFake AI AI-augmented social network AI增强的社交网络&#xff0c;用户使用文本提示来生成图像&#xff0c;拍摄自己的“AI”版本。任何人都可以创建全新的虚…...

c# Task异步使用

描述 Task出现之前&#xff0c;微软的多线程处理方式有&#xff1a;Thread→ThreadPool→委托的异步调用&#xff0c;虽然可以满足基本业务场景&#xff0c;但它们在多个线程的等待处理方面、资源占用方面、延续和阻塞方面都显得比较笨拙&#xff0c;在面对复杂的业务场景下&am…...

QuickLook概述和使用以及常用插件

1、QuickLook概述 QuickLook&#xff1a; 是可以快速预览的工具&#xff0c;开源、免费。通过空格键即可快速查看文件内容。 文件无需打开就可以用QuickLook一键快速预览。 说明文档&#xff1a;https://en.wikipedia.org/wiki/Quick_Look github地址&#xff1a;https://git…...

1A快恢复整流二极管型号汇总

快恢复整流二极管是二极管中的一种&#xff0c;开关特性好、反向恢复时间短&#xff0c;在开关电源、PWM脉宽调制器、变频器等电子电路中经常能看到它的身影。快恢复整流二极管的内部结构与普通PN结二极管不同&#xff0c;它属于PIN结型二极管&#xff0c;即在P型硅材料与N型硅…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...