【Spring】Spring中的事务

news2024/11/24 5:04:46

文章目录

  • 1. Spring事务简介
  • 2. Spring事务的案例
    • 案例代码
      • 代码目录结构
      • 数据库
      • pom.xml
      • Resource/jdbc.properties
      • config/SpringConfig.java
      • config/JdbcConfig.java
      • config/MyBatisConfig.java
      • dao/AccountDao.java
      • service/AccountService.java
      • service/impl/AccountServiceImpl.java
      • 测试方法
    • 问题分析
    • 事务管理三步
      • 第一步:在业务层接口上加上注解@Transactional
      • 第二步:在JdbcConfig.java中注册事务管理器
      • 第三步:在SpringConfig.java上加上开启事务管理的注解@EnableTransactionManagement
  • 3. Spring事务角色
  • 4. Spring事务属性
    • 事务配置
    • 案例:转账业务追加日志
      • 案例代码
        • 代码结构
        • 数据库表
        • dao/LogDao.java
        • service/LogService.java
        • service/LogServiceImpl.java
        • 修改service/impl/AccountServiceImpl.java如下
      • 改进
    • 事务传播行为

1. Spring事务简介

事务作用: 在数据层保障一系列的数据库操作同成功、同失败
Spring事务作用: 在数据层或业务层保障一系列的数据库操作同成功、同失败
Spring为事务提供的接口和实现类:

// 接口
public interface PlatformTransactionManager{
	void commit(TransactionStatus status) throws TransactionException;
	void rollback(TransactionStatus status) throws TransactionException;
}
// 实现类
public class DataSourceTransactionManager{
	...
}

2. Spring事务的案例

需求: 实现两个账户间的转账操作
需求微缩: A账户减钱,B账户加钱
分析:
在这里插入图片描述

案例代码

代码目录结构

在这里插入图片描述

数据库

在这里插入图片描述

pom.xml

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>project5</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>project5</name>
    <description>project5</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.11</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>3.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.13</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.25</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
    </dependencies>

</project>

Resource/jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=123456

config/SpringConfig.java

package com.example.project5.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.example.project5")
@Import({JdbcConfig.class, MyBatisConfig.class})
public class SpringConfig {
}

config/JdbcConfig.java

package com.example.project5.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

public class JdbcConfig {

    @Value("${jdbc.driver}")
    String driver;
    @Value("${jdbc.url}")
    String url;
    @Value("${jdbc.username}")
    String username;
    @Value("${jdbc.password}")
    String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUsername(username);
        ds.setPassword(password);
        ds.setUrl(url);
        return ds;
    }

}

config/MyBatisConfig.java

package com.example.project5.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class MyBatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.example.project5.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.example.project5.dao");
        return mapperScannerConfigurer;
    }

}

dao/AccountDao.java

package com.example.project5.dao;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Repository;

@Repository
public interface AccountDao {

    @Update("update account set money = money + #{money} where username = #{name}")
    void addMoney(@Param("name") String username, @Param("money") Double money);

    @Update("update account set money = money - #{money} where username = #{name}")
    void outMoney(@Param("name") String username, @Param("money") Double money);
}

service/AccountService.java

package com.example.project5.service;

public interface AccountService {
    /**
     * 转账操作
     * @param out 转出方
     * @param in 转入方
     * @param money 金额
     */
    public void transfer(String out, String in, double money);
}

service/impl/AccountServiceImpl.java

package com.example.project5.service.impl;

import com.example.project5.dao.AccountDao;
import com.example.project5.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountDao accountDao;
    @Override
    public void transfer(String out, String in, double money) {
        accountDao.outMoney(out, money);
        accountDao.addMoney(in, money);
    }
}

测试方法

package com.example.project5;

import com.example.project5.config.SpringConfig;
import com.example.project5.service.AccountService;
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;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class Project5ApplicationTests {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() {
        accountService.transfer("aaa", "bbb", 20);
    }

}

执行测试代码后,测试代码不会产生任何输出,但数据库中aaa的金额会由100变成80,bbb的金额会由111变成131:
在这里插入图片描述

问题分析

假如在AccountServiceImpl中手动制造一个错误:

package com.example.project5.service.impl;

import com.example.project5.dao.AccountDao;
import com.example.project5.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountDao accountDao;
    @Override
    public void transfer(String out, String in, double money) {
        accountDao.outMoney(out, money);
        int a = 1/0;
        accountDao.addMoney(in, money);
    }
}

这时候,程序在执行完outMoney方法,也就是aaa转出了20之后就不会继续执行了,这20并没有转入到bbb的账户之中,这就是事务的不一致性。接着上面的aaa金额为80,bbb的金额为131执行这个会报错的代码,结果是:
在这里插入图片描述

对运行的结果简单进行分析:
在这里插入图片描述
我们需要进行事务管理,使得数据层中的数据同加同减,而不是分开操作

事务管理三步

第一步:在业务层接口上加上注解@Transactional

package com.example.project5.service;

import org.springframework.transaction.annotation.Transactional;

public interface AccountService {
    /**
     * 转账操作
     * @param out 转出方
     * @param in 转入方
     * @param money 金额
     */
    @Transactional
    public void transfer(String out, String in, double money);
}

在这里插入图片描述

第二步:在JdbcConfig.java中注册事务管理器

package com.example.project5.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.lang.management.PlatformLoggingMXBean;

public class JdbcConfig {

    @Value("${jdbc.driver}")
    String driver;
    @Value("${jdbc.url}")
    String url;
    @Value("${jdbc.username}")
    String username;
    @Value("${jdbc.password}")
    String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUsername(username);
        ds.setPassword(password);
        ds.setUrl(url);
        return ds;
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }

}

在这里插入图片描述

第三步:在SpringConfig.java上加上开启事务管理的注解@EnableTransactionManagement

package com.example.project5.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.example.project5")
@Import({JdbcConfig.class, MyBatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}

启用事务管理后,保持刚才会报错的AccountServiceImpl.java的代码,恢复aaa金额为80,bbb金额为131,并再次进行测试,此时数据库中的内容不会发生任何改变:
在这里插入图片描述

3. Spring事务角色

在事务没有开启的时候:
在这里插入图片描述
outMoneyinMoney分别对应一个事务,我们手动写的异常是写在事务T1和事务T2之间的,则事务T1执行完毕以后发生了异常,所以事务T2不再执行
为了将两个事务统一起来,统一执行,或者统一不执行,我们在transfer方法上加了注解@Transactional,此时transfer本身是一个事务,我们将outMoneyinMoney都加入到这个事务中来:
在这里插入图片描述
此时我们将transfer方法称为事务管理员outMoneyinMoney称为事务协调员,具体定义如下:
在这里插入图片描述

4. Spring事务属性

事务配置

在@Transactional中还有很多属性
在这里插入图片描述
这里需要说明的是rollbackFor,默认的事务回滚,在我们没有定义rollbackFor的时候,只会在程序中出现运行时异常时候进行回滚,比如我们刚才手动指定的1/0就属于一个运行时抛出异常,假如修改这个异常如下:

package com.example.project5.service.impl;

import com.example.project5.dao.AccountDao;
import com.example.project5.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountDao accountDao;
    @Override
    public void transfer(String out, String in, double money) throws IOException {
        accountDao.outMoney(out, money);
        if(true) throw new IOException();
        accountDao.addMoney(in, money);
    }
}

再执行测试代码,就会发现数据库中的内容会从(aaa:80,bbb:131)->(aaa:60,bbb:131)
再次印证:没有定义rollbackFor的时候,只会在程序中出现运行时异常时候进行回滚
那么我们定义一下rollbackFor属性,如下:

package com.example.project5.service;

import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;

public interface AccountService {
    /**
     * 转账操作
     * @param out 转出方
     * @param in 转入方
     * @param money 金额
     */
    @Transactional(rollbackFor = {IOException.class})
    public void transfer(String out, String in, double money) throws IOException;
}

再执行测试代码,就会发现数据库中的内容(aaa:60,bbb:131)->(aaa:60,bbb:131),没有发生改变,所以我们需要通过rollbackFor来指定一些非运行时异常,在定义rollbackFor以后,程序在遇到运行时异常仍会回滚。

案例:转账业务追加日志

在这里插入图片描述

案例代码

在上述案例代码中加上如下内容:

代码结构

在这里插入图片描述

数据库表

在这里插入图片描述

dao/LogDao.java
package com.example.project5.dao;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;

import java.util.Date;

@Repository
public interface LogDao {
    @Insert("insert into log(content, date) VALUES(#{content}, #{date})")
    void insertLog(@Param("content") String content, @Param("date") Date date);
}

service/LogService.java

注意,该方法上也要加上事务注解

package com.example.project5.service;

import java.util.Date;

public interface LogService {
	@Transactional
    void insertLog(String content, Date date);
}
service/LogServiceImpl.java
package com.example.project5.service.impl;

import com.example.project5.dao.LogDao;
import com.example.project5.service.LogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class LogServiceImpl implements LogService {
    
    @Autowired
    LogDao logDao;
    
    @Override
    public void insertLog(String content, Date date) {
        logDao.insertLog(content, date);
    }
}
修改service/impl/AccountServiceImpl.java如下
package com.example.project5.service.impl;

import com.example.project5.dao.AccountDao;
import com.example.project5.service.AccountService;
import com.example.project5.service.LogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountDao accountDao;
    
    @Autowired
    LogService logService;
    
    @Override
    public void transfer(String out, String in, double money) throws IOException {
        try{
            accountDao.outMoney(out, money);
            accountDao.addMoney(in, money);            
        } finally {
            logService.insertLog(out + "向" + in + "转账" + money + "元", new Date());
        }
    }
}

当捕捉到异常时执行日志记录。
将数据库中的金额恢复为:aaa->100,bbb->111,并执行测试代码,得到account表和log表的结果:
在这里插入图片描述
在这里插入图片描述
正常执行的时候,会修改数据库中的金额、向日志记录中添加日志
假设我们在AccountServiceImpl的try中加上:

accountDao.outMoney(out, money);
int a = 1/0;
accountDao.addMoney(in, money);

我们期望的结果是:不修改数据库中的金额、向日志记录中添加日志,使用修改后的代码再执行测试方法,得到结果是account表和log表中的内容都没有发生任何变化,所以我们归纳总结出存在的问题:

在这里插入图片描述

改进

我们需要定义事务的传播属性propagation,在LogService.java下重新写注解,改为:

@Transactional(propagation = Propagation.REQUIRES_NEW)

此时,再次运行上面的代码,结果为:
account表中的内容不变,log表中新添了日志:
在这里插入图片描述
在这里插入图片描述
我认为这样的改进可以理解为,使用默认的propagation时,事务协调员都被添加到事务管理员的事务中,从而统一提交或统一回滚:
在这里插入图片描述
当我们在LogService上写明了事务的传播行为为Requires_New后,即使原有了事务,我们还是会为这个service实例开启一个新事务,如下,这样就不是统一受到事务t的控制了:
在这里插入图片描述

事务传播行为

在这里插入图片描述

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

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

相关文章

电子元器件介绍——电感(三)

电子元器件 文章目录 电子元器件前言一、电感的基础知识二、电感的分类与作用三、电感的作用 总结 前言 这一节学习一下电感 一、电感的基础知识 电感是导线内通过交流电流时&#xff0c;在导线的内部及其周围产生交变磁通&#xff0c;导线的磁通量与生产此磁通的电流之比。…

[python][plotly]利用plotly绘制散点图

import plotly.express as px import pandas as pd# 创建示例数据 data pd.DataFrame({x: [1, 2, 3, 4, 5],y: [5, 4, 3, 2, 1] })# 使用 plotly.express 绘制散点图 fig px.scatter(data, xx, yy, titleScatter plot) fig.show() 结果&#xff1a;

LabVIEW开发地铁运行安全监控系统

LabVIEW开发地铁运行安全监控系统 最近昌平线发生的故障事件引起了广泛关注&#xff0c;暴露了现有地铁运行监控系统在应对突发情况方面的不足。为了提高地铁系统的运行安全性&#xff0c;并防止类似事件再次发生&#xff0c;提出了一套全面的地铁运行安全监控系统方案。此方案…

写好ChatGPT提示词原则之:清晰且具体(clear specific)

ChatGPT 的优势在于它允许用户跨越机器学习和深度学习的复杂门槛&#xff0c;直接利用已经训练好的模型。然而&#xff0c;即便是这些先进的大型语言模型也面临着上下文理解和模型固有局限性的挑战。为了最大化这些大型语言模型&#xff08;LLM&#xff09;的潜力&#xff0c;关…

使用java获取nvidia显卡信息

前言 AI开发通常使用到GPU&#xff0c;但通常使用的是python、c等语言&#xff0c;java用的则非常少。这也导致了java在gpu相关的库比较少。现在的需求是要获取nvidia显卡的使用情况&#xff0c;如剩余显存。这里给出两种较简单的解决方案。 基于nivdia-smi工具 显卡是硬件&a…

检查数组中是否含有数据类型为复数的元素np.iscomplexobj()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 检查数组中是否含有 数据类型为复数的元素 np.iscomplexobj() [太阳]选择题 下列关于代码的说法正确的是&#xff1a; import numpy as np anp.array([1,23j]) bnp.array([1,2,3]) print(&quo…

Python-数据分析可视化实例图

Python-数据分析可视化实例图 一&#xff1a;3D纹理图 运行效果图&#xff1a; Python代码&#xff1a; import math from typing import Unionimport pyecharts.options as opts from pyecharts.charts import Surface3Ddef float_range(start: int, end: int, step: Union[…

整合SpringSecurity

目录 前言 数据库设计 用户表 角色表 用户角色表 权限表 角色权限表 插入数据 表的实体类 用户表实体类 角色表实体类 权限表实体类 mapper层接口 UserMapper RoleMapper AuthorityMapper 封装登录信息 统一响应结果 上下文相关类 jwt令牌工具类 依赖导入…

[Verilog] Verilog 基本格式和语法

主页&#xff1a; 元存储博客 全文 3000 字 文章目录 1. 声明格式1.1 模块声明1.2 输入输出声明1.3 内部信号声明1.4 内部逻辑声明1.5 连接声明1.6 数据类型声明1.7 运算符和表达式1.8 控制结构 2. 书写格式2.1 大小写2.2 换行2.3 语句结束符2.4 注释2.5 标识符2.6 关键字 1. 声…

docker入门小结

docker是什么&#xff1f;它有什么优势&#xff1f; 快速获取开箱即用的程序 docker使得所有的应用传输就像我们日常通过聊天工具文件传输一样&#xff0c;发送方将程序传输到超级码头而接收方也只需通过超级码头进行获取即可&#xff0c;就像一只鲸鱼拖着货物来回运输一样。…

linux一次性调度执行_at命令

........................................................................................................................................................... 9.1 一次性调度执行 Schedule one-time tasks with at. ............................................…

早上好,我的leetcode 【hash】(第二期)

写在前面&#xff1a;坚持才是最难的事情 C代码还是不方便写&#xff0c;改用python了&#xff0c;TAT 文章目录 1.两数之和49. 字母异位词分组128.最长连续序列 1.两数之和 你好&#xff0c;梦开始的地方~ https://leetcode.cn/problems/two-sum/description/?envTypestudy…

n维随机变量、n维随机变量的分布函数

设随机试验E的样本空间是&#xff0c;其中表示样本点。 设是定义在上的随机变量&#xff0c;由它们构成一个n维向量&#xff0c;叫做n维随机向量&#xff0c;也叫n维随机变量。 对于任意n个实数&#xff0c;n元函数 称为n维随机变量的分布函数&#xff0c;也叫联合分布函数。

qt 标准对话框的简单介绍

qt常见的标准对话框主要有,标准文件对话框QFileDialog,标准颜色对话框QColorDialog,标准字体对话框QFontDialog,标准输入对话框QInputDialog,标准消息框QMessageBox...... 1. 标准文件对话框QFileDialog,使用函数getOpenFileName()获取用户选择的文件. //qt 函数getOpenFileN…

geolife笔记:比较不同轨迹相似度方法

1 问题描述 在geolife 笔记&#xff1a;将所有轨迹放入一个DataFrame-CSDN博客中&#xff0c;已经将所有的轨迹放入一个DataFrame中了&#xff0c;我们现在需要比较&#xff0c;在不同的轨迹距离度量方法下&#xff0c;轨迹相似度的效果。 这里采用论文笔记&#xff1a;Deep R…

arthas 线上排查问题基本使用

一、下载 [arthas下载地址]: 下载完成 解压即可使用 二、启动 java -Dfile.encodingUTF-8 -jar arthas-boot.jar 如果直接使用java -jar启动 可能会出现乱码 三、使用 启动成功之后 arthas会自动扫描当前服务器上的jvm进程 选择需要挂载的jvm进程 假如需要挂在坐标【1】的…

【MySQL】(DDL) 数据类型 和 表操作-修改 删除

目录 介绍&#xff1a; 1.数值类型 3.日期类型 修改表&#xff1a; 示列&#xff1a; 介绍&#xff1a; 在之前建表语句内&#xff0c;用到了 int cvarchar &#xff0c;那么在mysql内除了 以上的数据类型 还有那些常见数据类型 mysql 中的数据类型有很多种 &#xff0c…

机器学习 | 决策树 Decision Tree

—— 分而治之&#xff0c;逐个击破 把特征空间划分区域 每个区域拟合简单模型 分级分类决策 1、核心思想和原理 举例&#xff1a; 特征选择、节点分类、阈值确定 2、信息嫡 熵本身代表不确定性&#xff0c;是不确定性的一种度量。 熵越大&#xff0c;不确定性越高&#xff0c;…

maui中实现加载更多 RefreshView跟ListView 跳转到详情页 传参(3)

效果如图 这里的很多数据是通过传参过来的的。 代码 例表页加入跳转功能&#xff1a; <ListView ItemsSource"{Binding Items}" ItemAppearing"OnItemAppearing" ItemTapped"OnItemTapped" RowHeight"70" Margin"20"…

【C++11特性篇】一文助小白轻松理解 C++中的【左值&左值引用】【右值&右值引用】

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 一.【左值&#xff06;左值引用】&…