SpringBoot第25讲:SpringBoot集成MySQL - MyBatis 注解方式
SpringBoot第25讲:SpringBoot集成MySQL - MyBatis 注解方式
本文是SpringBoot第25讲,上文主要介绍了Spring集成MyBatis访问MySQL,采用的是XML配置方式;我们知道除了XML配置方式,MyBatis还支持注解方式。本文主要介绍SpringBoot+MyBatis注解方式。
文章目录
- SpringBoot第25讲:SpringBoot集成MySQL - MyBatis 注解方式
- 1、准备知识
- 2、基本查改删操作
- 2.1、查询操作
- 1、@Results和@Result注解
- 2、@Select和@Param注解
- 3、@ResultMap注解
- 4、表关联查询
- 2.2、插入操作
- 1、@Insert注解
- 2、返回Insert后实体的主键值
- 2.3、更新操作
- 1、@Update 注解
- 2.4、删除操作
- 1、@Delete 注解
- 3、Provider注解
- 4、进一步理解
- 4.1、其它注解
- 4.2、xml方式和注解方式融合
- 4.3、为什么纯注解方式不是最佳选择?
- 5、示例源码
1、准备知识
MyBatis的相关知识体系。
具体可以参考 SpringBoot第24讲:SpringBoot集成MySQL - MyBatis XML方式
在构建知识体系时:我们最重要的目标并不是如何使用注解方式,而是要理解:
- 对于有原有xml方式改为注解方式(一定要有对比),如何写?
- 基本的CRUD怎么用注解写?
- 对于复杂的动态SQL如何写?
- 对于表关联的如何写?
- 为什么xml方式依然是比注解方式使用广泛?
- xml方式和注解方式混合使用?
- 注解方式是如何工作的呢?
2、基本查改删操作
我们从最基本的增删改操作开始,对比xml方式进行理解。
2.1、查询操作
1、@Results和@Result注解
对于xml配置查询时定义的ResultMap, 在注解中如何定义呢?
<resultMap type="springboot.mysql.mybatis.entity.User" id="UserResult1"><id property="id" column="id" /><result property="userName" column="user_name" /><result property="password" column="password" /><result property="email" column="email" /><result property="phoneNumber" column="phone_number" /><result property="description" column="description" /><result property="createTime" column="create_time" /><result property="updateTime" column="update_time" />
</resultMap>
使用注解方式,用@Results注解对应
@Results(id = "UserResult1",value = {@Result(id = true, property = "id", column = "id"),@Result(property = "userName", column = "user_name"),@Result(property = "password", column = "password"),@Result(property = "email", column = "email"),@Result(property = "phoneNumber", column = "phone_number"),@Result(property = "description", column = "description"),@Result(property = "createTime", column = "create_time"),@Result(property = "updateTime", column = "update_time")}
)
2、@Select和@Param注解
对于查询,用@Select注解;对于参数, 使用@Param注解
所以根据用户ID查询用户,使用注解方式写法如下:
String SELECT_USER_SQL = "select u.id, u.password, u.user_name, u.email, u.phone_number, u.description, u.create_time, u.update_time from tb_user u";@Results(id = "UserResult1",value = {@Result(id = true, property = "id", column = "id"),@Result(property = "userName", column = "user_name"),@Result(property = "password", column = "password"),@Result(property = "email", column = "email"),@Result(property = "phoneNumber", column = "phone_number"),@Result(property = "description", column = "description"),@Result(property = "createTime", column = "create_time"),@Result(property = "updateTime", column = "update_time")}
)
@Select({SELECT_USER_SQL, " where id = #{id}"})
User findById1(@Param("id") Long id);
3、@ResultMap注解
xml配置查询时定义的ResultMap是可以复用的,那么我们上面通过@Results定义在某个方法上的,如何复用呢?
比如查询所有用户返回用户实体@Results是和查询单个用户一致的,那么我们可以通过@ResultMap指定返回值对应关系
@ResultMap("UserResult1")
@Select(SELECT_USER_SQL)
User findAll1();
由此你可以猜到,@ResultMap定义在哪个方法上并没有什么关系,因为它会被优先通过注解解析为数据库字段与Java字段的映射关系。
4、表关联查询
用户和角色存在着一对多的关系,上面的查询只是查询了用户的基本信息,如何关联查询(查询用户同时返回角色信息)呢?
我们看下xml配置方式是如何做到的?
<resultMap type="springboot.mysql.mybatis.entity.User" id="UserResult"><id property="id" column="id" /><result property="userName" column="user_name" /><result property="password" column="password" /><result property="email" column="email" /><result property="phoneNumber" column="phone_number" /><result property="description" column="description" /><result property="createTime" column="create_time" /><result property="updateTime" column="update_time" /><collection property="roles" ofType="springboot.mysql.mybatis.entity.Role"><result property="id" column="id" /><result property="name" column="name" /><result property="roleKey" column="role_key" /><result property="description" column="description" /><result property="createTime" column="create_time" /><result property="updateTime" column="update_time" /></collection>
</resultMap>
使用注解方式, 可以通过@Results+@Many注解
@Results(id = "UserResult",value = {@Result(id = true, property = "id", column = "id"),@Result(property = "userName", column = "user_name"),@Result(property = "password", column = "password"),@Result(property = "email", column = "email"),@Result(property = "phoneNumber", column = "phone_number"),@Result(property = "description", column = "description"),@Result(property = "createTime", column = "create_time"),@Result(property = "updateTime", column = "update_time"),@Result(property = "roles", column = "id", many = @Many(select = "springboot.mysql.mybatis.anno.dao.IRoleDao.findRoleByUserId", fetchType = FetchType.EAGER))}
)
其中findRoleByUserId是通过user表中的id查找Role, 具体方法如下
@Results(id = "RoleResult",value = {@Result(id = true, property = "id", column = "id"),@Result(property = "name", column = "name"),@Result(property = "roleKey", column = "role_key"),@Result(property = "description", column = "description"),@Result(property = "createTime", column = "create_time"),@Result(property = "updateTime", column = "update_time")})@Select("select r.id, r.name, r.role_key, r.description, r.create_time, r.update_time from tb_role r, tb_user_role ur where r.id = ur.user_id and ur.user_id = #{userId}")List<Role> findRoleByUserId(Long userId);
对于一对一的可以使用@One注解。
2.2、插入操作
涉及插入操作的主要注解有:@Insert, @SelectKey等。
1、@Insert注解
对于插入操作,在xml配置可以定义为:
<insert id="save" parameterType="springboot.mysql.mybatis.xml.entity.User" useGeneratedKeys="true" keyProperty="id">insert into tb_user(<if test="userName != null and userName != ''">user_name,</if><if test="password != null and password != ''">password,</if><if test="email != null and email != ''">email,</if><if test="phoneNumber != null and phoneNumber != ''">phone_number,</if><if test="description != null and description != ''">description,</if>create_time,update_time)values(<if test="userName != null and userName != ''">#{userName},</if><if test="password != null and password != ''">#{password},</if><if test="email != null and email != ''">#{email},</if><if test="phoneNumber != null and phoneNumber != ''">#{phoneNumber},</if><if test="description != null and description != ''">#{description},</if>sysdate(),sysdate())
</insert>
特别是,这里通过<if>
判断条件更新的情况应该如何在注解中写呢?
可以通过@Insert + <script>
@Insert({"<script> ", "insert into tb_user(\n" +" <if test=\"userName != null and userName != ''\">user_name,</if>\n" +" <if test=\"password != null and password != ''\">password,</if>\n" +" <if test=\"email != null and email != ''\">email,</if>\n" +" <if test=\"phoneNumber != null and phoneNumber != ''\">phone_number,</if>\n" +" <if test=\"description != null and description != ''\">description,</if>\n" +" create_time,\n" +" update_time\n" +" )values(\n" +" <if test=\"userName != null and userName != ''\">#{userName},</if>\n" +" <if test=\"password != null and password != ''\">#{password},</if>\n" +" <if test=\"email != null and email != ''\">#{email},</if>\n" +" <if test=\"phoneNumber != null and phoneNumber != ''\">#{phoneNumber},</if>\n" +" <if test=\"description != null and description != ''\">#{description},</if>\n" +" sysdate(),\n" +" sysdate()\n" +" )", " </script>"})
@Options(useGeneratedKeys = true, keyProperty = "id")
int save(User user);
2、返回Insert后实体的主键值
上述
@Options(useGeneratedKeys = true, keyProperty = "id")
表示什么意思呢?
表示,如果数据库提供了自增列生成Key的方式(比如这里的id),并且需要返回自增主键时,可以通过这种方式返回实体。
那么,如果id的自增不使用数据库自增主键时, 在xml中可以使用SelectKey:
<selectKey keyColumn="id" resultType="long" keyProperty="id" order="AFTER">SELECT LAST_INSERT_ID()
</selectKey>
对应着注解:
@SelectKey(statement = "SELECT LAST_INSERT_ID()", keyColumn = "id", keyProperty = "id", resultType = Long.class, before = false)
before = false
, 相当于XML中的order=“AFTRE”,这是MySql数据库的配置。before = true
, 相当于XML中的order=“BEFORE”,这是Oracle数据库的配置。
注意事项:不同的数据库statement的值会不同,上面中的值适用于MySql数据库,使用其他类型的数据库时要注意修改。
2.3、更新操作
涉及更新操作的主要注解有:@Update等。
1、@Update 注解
对于xml的更新操作如下:
<update id="update" parameterType="springboot.mysql.mybatis.xml.entity.User">update tb_user<set><if test="userName != null and userName != ''">user_name = #{userName},</if><if test="email != null and email != ''">email = #{email},</if><if test="phoneNumber != null and phoneNumber != ''">phone_number = #{phoneNumber},</if><if test="description != null and description != ''">description = #{description},</if>update_time = sysdate()</set>where id = #{id}
</update><update id="updatePassword" parameterType="springboot.mysql.mybatis.xml.entity.User">update tb_user<set>password = #{password}, update_time = sysdate()</set>where id = #{id}
</update>
对应的注解写法如下:
@Update({"update tb_user set password = #{password}, update_time = sysdate()", " where id = #{id}"})
int updatePassword(User user);@Update({"<script> ", "update tb_user\n" +" <set>\n" +" <if test=\"userName != null and userName != ''\">user_name = #{userName},</if>\n" +" <if test=\"email != null and email != ''\">email = #{email},</if>\n" +" <if test=\"phoneNumber != null and phoneNumber != ''\">phone_number = #{phoneNumber},</if>\n" +" <if test=\"description != null and description != ''\">description = #{description},</if>\n" +" update_time = sysdate()\n" +" </set>\n" +" where id = #{id}", " </script>"})
int update(User user);
2.4、删除操作
涉及删除操作的主要注解有:@Delete等。
1、@Delete 注解
对于xml的删除操作如下:
<delete id="deleteById" parameterType="Long">delete from tb_user where id = #{id}
</delete><delete id="deleteByIds" parameterType="Long">delete from tb_user where id in<foreach collection="array" item="id" open="(" separator="," close=")">#{id}</foreach>
</delete>
对应的注解写法如下:
@Delete("delete from tb_user where id = #{id}")
int deleteById(Long id);@Delete({"<script> ", "delete from tb_user where id in\n" +"<foreach collection=\"array\" item=\"id\" open=\"(\" separator=\",\" close=\")\">\n" +"#{id}\n" +"</foreach>", " </script>"})
int deleteByIds(Long[] ids);
3、Provider注解
其实你可以发现通过注解方式,对于有一些需要通过动态构建查询条件的操作是非常不方便的。MyBatis的作者们自然就想到了动态构建SQL,动态构建SQL的方式是配合@Provider注解来完成的。
MyBatis提供了4种Provider注解,分别是 @SelectProvider、@InsertProvider、@UpdateProvider 和 @DeleteProvider。
这里以@SelectProvider为例来根据Id查询User:
1、定义包含自定义生成的动态SQL的类,比如UserDaoProvider
/*** @author qiwenjie*/
public class UserDaoProvider {public String findById(final Long id) {SQL sql = new SQL();sql.SELECT("u.id, u.password, u.user_name, u.email, u.phone_number, u.description, u.create_time, u.update_time");sql.FROM("tb_user u");sql.WHERE("id = " + id);return sql.toString();}
}
2、通过@SelectProvider注解关联到定义的类和方法
@ResultMap("UserResult")
@SelectProvider(type = UserDaoProvider.class, method = "findById")
User findById2(Long id);
4、进一步理解
让我们通过几个问题,进一步理解MyBatis注解方式。
4.1、其它注解
@CacheNamespace
:为给定的命名空间 (比如类) 配置缓存。对应xml中的<cache>
。@CacheNamespaceRef
:参照另外一个命名空间的缓存来使用。属性:value,应该是一个名空间的字 符串值(也就是类的完全限定名) 。对应xml中的<cacheRef>
标签。@ConstructorArgs
:收集一组结果传递给一个劫夺对象的构造方法。属性:value,是形式参数 的数组。@Arg
:单独的构造方法参数 , 是 ConstructorArgs 集合的一部分。属性: id,column,javaType,typeHandler。id 属性是布尔值, 来标识用于比较的属性,和XML元素相似。对应xml中的<arg>
标签。@Case
:单独实例的值和它对应的映射。属性: value,type,results。Results 属性是结果数组,因此这个注解和实际的 ResultMap 很相似,由下面的 Results 注解指定。对应xml中标签<case>
。@TypeDiscriminator
: 一组实例值被用来决定结果映射的表 现。 属性: column, javaType, jdbcType, typeHandler,cases。cases 属性就是实例的数组。对应xml中标签<discriminator>
。@Flush
: 在MyBatis 3.3以上版本,可以通过此注解在Mapper接口中调用SqlSession#flushStatements()
。
4.2、xml方式和注解方式融合
xml方式和注解方式是可以融合写的, 我们可以将复杂的SQL写在xml中
比如将resultMap定义在xml中
<resultMap type="springboot.mysql.mybatis.xml.entity.User" id="UserResult3"><id property="id" column="id" /><result property="userName" column="user_name" /><result property="password" column="password" /><result property="email" column="email" /><result property="phoneNumber" column="phone_number" /><result property="description" column="description" /><result property="createTime" column="create_time" /><result property="updateTime" column="update_time" /><collection property="roles" ofType="springboot.mysql.mybatis.xml.entity.Role"><result property="id" column="id" /><result property="name" column="name" /><result property="roleKey" column="role_key" /><result property="description" column="description" /><result property="createTime" column="create_time" /><result property="updateTime" column="update_time" /></collection>
</resultMap>
在方法中用 @ResultMap
@ResultMap("UserResult3")
@Select("select u.id, u.password, u.user_name, u.email, u.phone_number, u.description, u.create_time, u.update_time from tb_user u")
User findAll1();
4.3、为什么纯注解方式不是最佳选择?
纯注解方式为何很少大规模呢? 说说我的一些看法
- 1、对于复杂的SQL,特别是按照条件动态生成方式极为不便,即便有
<script>
, 代码的阅读体验和维护极为不佳; - 2、对于复杂的SQL,即便有@Provider方式,这种充其量是一个半成品
- 不是所见即所得的写法,需要再定义额外的类和方法
- 动态构建时不便利
- 函数式编程成为主流,lambda方式才是未来
这也是mybatis-plus等工具改进的地方。
5、示例源码
todo
相关文章:
SpringBoot第25讲:SpringBoot集成MySQL - MyBatis 注解方式
SpringBoot第25讲:SpringBoot集成MySQL - MyBatis 注解方式 本文是SpringBoot第25讲,上文主要介绍了Spring集成MyBatis访问MySQL,采用的是XML配置方式;我们知道除了XML配置方式,MyBatis还支持注解方式。本文主要介绍Sp…...

服务器返回 413 Request Entity Too Large
问题 上传一个大于1.5M的文件时,报错:413 Request Entity Too Large 使用的配置 1、用的是docker环境,还有一层代理,代理用的镜像是:jwilder/nginx-proxy 2、docker里是有php和nginx 确认配置 docker里的php和ngi…...

如何一目了然地监控远程 Linux 系统
动动发财的小手,点个赞吧! Glances 是一款免费的开源、现代、跨平台、实时 top 和类似 htop 的系统监控工具,与同类工具相比,它提供了先进的功能,并且可以在不同的模式下运行:作为独立模式、客户端/服务器模…...

9.环境对象和回调函数
9.1环境对象 指的是函数内部特殊的变量this,它代表着当前函数运行时所处的环境 作用: 弄清楚this的指向,可以让我们代码更简洁 ➢函数的调用方式不同,this指代的对象也不同 ➢[谁调用,this 就指代谁] 是判断this指向的…...
51单片机(普中HC6800-EM3 V3.0)实验例程软件分析概览
本专栏将分析普中HC6800-EM3 V3.0 (9.22)\5--实验程序\基础实验例程中的各个例程的代码。 引言:本专栏将对历程中的关键代码进行分析与拓展,再学习一遍51,记录与各位一起进步。 下面是文件列表: E:\USER\000study\000_51单片机\000普中HC6800-EM3 V3.0 (9.22)\5--实…...

ubuntu18.04 安装php7.4-xdebug
文章目录 场景解决 场景 apt install php7.4-xdebug 下载失败, 只好通过编译解决了 解决 https://xdebug.org/wizard 输入php -i的执行结果...

java 定时任务不按照规定时间执行
这里写目录标题 使用异步启动可能出现的问题排查代码中添加的定时任务步骤是否正确排查是否任务阻塞,如果定时任务出现异常阻塞后,将不会在次执行java中多个Scheduled定时器不执行为了让Scheduled效率更高,我们可以通过两种方法将定时任务变成…...

Android复习(Android基础-四大组件)—— Activity
Activity作为四大组件之首,是使用最为频繁的一种组件,中文直接翻译为"活动",不过如果被翻译为"界面"会更好理解。正常情况,除了Window,Dialog和Toast , 我们能见到的界面只有Activity。…...

Linux系统安装部署MongoDB完整教程(图文详解)
前言:本期给大家分享一下目前最新Linux系统安装部署MongoDB完整教程,我的服务器采用的是Centos7,在部署之前我重装了我的服务器,目的是为了干净整洁的给大家演示我是如何一步步的操作的,整体部署还是挺简洁,…...

CSS图片放到<div>里面,自适应宽高全部显示,点击图片跳到新页面预览,点击旋转按钮图片可旋转
有一个需求是图片放到一个固定宽高的<div>里面,不管是横图还是竖图,都要全部显示出来并且保持图片的长宽比例不变形,点击图片可以跳到一个新页面预览,代码如下: <!DOCTYPE html> <html> <head>…...

二阶段web基础与http协议
dns与域名 网络是基于tcp/ip协议进行通信和连接的 应用层-----传输层-----网络层-----数据链路层-----物理层 ip地址,每一台主机都有一个唯一的地址标识(固定的ip地址) 1.区分用户和计算机 2.通信 ip地址的问题在于32位二进制数组成的&…...
SpringBoot+Freemark根据html模板动态导出PDF
SpringBootFreemark根据html模板导出PDF 1、引入maven2、两个工具类2.1 test.html模板2.2 test.html模板中的Freemark语法 3、controller导出pdf 1、引入maven 导出pdf的一些必要jar包 <dependency><groupId>org.projectlombok</groupId><artifactId>…...

XPath数据提取与贴吧爬虫应用示例
XPath数据提取与贴吧爬虫应用示例 XpathXpath概述Xpath Helper插件 XPath语法基本语法查找特定节点选取未知节点选取若干路径 lxml模块使用说明使用示例 百度贴吧爬虫 Xpath Xpath概述 XPath(XML Path Language)是一种用于在XML文档中定位和选择节点的语…...
字符串匹配-KMP算法
KMP算法,字符串匹配算法,给定一个主串S,和一个字串T,返回字串T与之S匹配的数组下标。 在学KMP算法之前,对于两个字符串,主串S,和字串T,我们根据暴力匹配,定义两个指针,i指…...

Java面向对象之UML类图
UML类图 表示 public 类型, - 表示 private 类型,#表示protected类型方法的写法:方法的类型(、-) 方法名(参数名: 参数类型):返回值类型...

【机器学习】西瓜书学习心得及课后习题参考答案—第4章决策树
这一章学起来较为简单,也比较好理解。 4.1基本流程——介绍了决策树的一个基本的流程。叶结点对应于决策结果,其他每个结点则对应于一个属性测试;每个结点包含的样本集合根据属性测试的结果被划分到子结点中;根结点包含样本全集&a…...
2023.8.2
2022河南萌新联赛第(三)场:河南大学\神奇数字.cpp //题意:给定三个正整数a b c,求x满足满足abc同余x的个数。 //这个考虑同余的性质,就是两个数的差去取模为0的数肯定是这两个数的同余数,。因此我们计算三个数两两之…...
windows运行窗口常用快捷键命令
winr打开运行窗口,然后输入快捷命令:(当然utools和win11搜索也挺好用的) cmd : 命令行窗口(命令提示符窗口、cmd窗口)regedit : 注册表mspaint : 画图工具services.msc : 本地服务设置(比如查看mysql服务是否启动成功)devmgmt.ms…...

HDFS的QJM方案
Quorum Journal Manager仲裁日志管理器 介绍主备切换,脑裂问题解决---ZKFailoverController(zkfc)主备切换,脑裂问题解决-- Fencing(隔离)机制主备数据状态同步问题解决 HA集群搭建集群基础环境准备HA集群规…...

安装win版本的neo4j(2023最新版本)
安装win版本的neo4j 写在最前面安装 win版本的neo4j1. 安装JDK2.下载配置环境变量(也可选择直接点击快捷方式,就可以不用配环境了)3. 启动neo4j 测试代码遇到的问题及解决(每次环境都太离谱了,各种问题)连接…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...

Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践
前言:本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中,跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南,你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案,并结合内网…...
React父子组件通信:Props怎么用?如何从父组件向子组件传递数据?
系列回顾: 在上一篇《React核心概念:State是什么?》中,我们学习了如何使用useState让一个组件拥有自己的内部数据(State),并通过一个计数器案例,实现了组件的自我更新。这很棒&#…...