2 MyBatis Sql映射文件

MyBatis SQL映射文件

1.SQL映射的XML文件

MyBatis的核心是SQL管理

特色在于SQL映射语句,功能强大,使用简单

SQL映射文件的几个顶级元素

mapper – SQL映射文件的根元素,有一个namespace属性
cache – 配置给定命名空间的缓存
cache-ref – 从其他命名空间引用缓存配置
resultMap – 用来描述数据库结果集和对象的对应关系
sql – 可以重用的SQL块,也可以被其他语句引用
insert – 映射插入语句
update – 映射更新语句
delete – 映射删除语句
select – 映射查询语句

2.select条件查询

mapper层

package com.mapper;

import com.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
* 用户类Mapper层
*/
public interface UserMapper {

/**
    * 单一条件查询
    * @param name
    * @return
*/
public User getUserByName(@Param("name") String name);
//@Param() 是取别名的意思 
//取别名后在xml编写映射就可以通过别名来指定某一个变量了
//如果有多个参数的情况不取别名则无法通过变量的名字来指定某一个变量,需要通过param1 param2
//param3……这样的参数去获取第几个参数, 如果只有一个的话那不指定也行,建议指定一下比较好
}

xml映射文件

<!--单一条件查询-->
<select id="getUserByName" resultType="com.pojo.User" parameterType="string">
select * from `user` where name=#{name}
</select>
<!--
id:指定方法名
resultType:方法返回类型
parameterType:传入参数类型

#{name}指的是入参的变量名 也可以使用#{param1}来获取方法的第一个参数
-->

测试类

/**
    * MyBatis单一条件查询
*/
@Test
public void getUserByName() {
   SqlSession sqlSession=MyBatisUtil.createSqlSession();
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   //传入参数
   User user = userMapper.getUserByName("merchant");
   System.out.println("_________________________");
   System.out.println(user.getId());
   System.out.println(user.getName());
   System.out.println(user.getPwd());
   MyBatisUtil.closeSqlSession(sqlSession);
}

查询结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wFSzMraF-1653923555895)(C:\Users\luyod\AppData\Roaming\Typora\typora-user-images\1652846916726.png)]

3.多条件查询
1.将条件封装成Java对象作为入参

尝试传入User对象用 用户名字和用户密码作为 查询用户的id信息

编写代码:

mapper层

/**
   * 传入User对象查询对象id
   * @param user
   * @return
*/
public int getUserIdByUser(@Param("user") User user);

xml映射文件

<!--传入User对象查询对象id-->
<select id="getUserIdByUser" resultType="int" parameterType="com.pojo.User">
select id from `user` where name=#{user.name} and pwd=#{user.pwd}
</select>
<!--可以发现 是使用变量的名字.属性来使用-->

测试类

/**
   * 复杂参数查询数据
*/
@Test
public void getUserIdByUser() {
SqlSession sqlSession=MyBatisUtil.createSqlSession();
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  User user=new User();
  user.setName("merchant");
   user.setPwd("123");
   //传入对象作为参数
   int id = userMapper.getUserIdByUser(user);
   //输出
   System.out.println(id);
   MyBatisUtil.closeSqlSession(sqlSession);
}

运行结果:

在这里插入图片描述

2.将查询条件封装成Map集合作为入参

用map存入用户名字和用户密码作为 查询用户的id信息

编写代码:

mapper层

/**
* map作为入参条件实现多条件查询
* @param map
* @return
*/
public int getUserIdByMap(@Param("map")Map<String,String> map);

xml映射

<!--传入map作为查询条件-->
<select id="getUserIdByMap" resultType="int" parameterType="map">
select * from `user` where name=#{map.name} and pwd=#{map.pwd}
</select>
<!--此处#{map.name}意义是map中名为name的key值所对应的value-->

测试类

/**
    * map作为入参条件实现多条件查询
*/
@Test
public void getUserIdByMap() {
   SqlSession sqlSession = MyBatisUtil.createSqlSession();
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   Map<String, String> map = new HashMap<>();
   map.put("name", "merchant");
   map.put("pwd", "123");
   //传入Map对象
   int id = userMapper.getUserIdByMap(map);
   System.out.println(id);
   MyBatisUtil.closeSqlSession(sqlSession);
}

运行结果:

在这里插入图片描述

3.使用@Param注解实现多参数入参

@Param注解可以为参数取别名,因此传入多个参数的时候可以通过对应的别名来注入sql,而不需要使用param1,param2……来获取某个变量

传入用户名字和用户密码作为 查询用户的id信息

mapper层

/**
    * 使用@Param注解实现多参数入参
    * @param name
    * @param pwd
    * @return
*/
public int getUserIdByUserNameAndPwd(@Param("userName") String name,
                                    @Param("userPwd") String pwd);

xml映射文件

<!--使用@Param注解实现多参数入参-->
<select id="getUserIdByUserNameAndPwd" resultType="int">
select * from `user` where name=#{userName} and pwd=#{userPwd}
</select>
测试类
/**
    * 使用@Param注解实现多参数入参
*/
@Test
public void getUserIdByUserNameAndPwd() {
   SqlSession sqlSession = MyBatisUtil.createSqlSession();
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   String name="merchant";
   String pwd="123";
   //传入多个参数
   int id = userMapper.getUserIdByUserNameAndPwd(name, pwd);
   System.out.println(id);
   MyBatisUtil.closeSqlSession(sqlSession);
}

运行结果:

在这里插入图片描述

如果需要模糊查询,只需要将更改sql语句即可例如:

concat('%',#{内容},'%') 拼接字符串
4.MyBatis结果映射(resultMap元素自定义结果映射
1.mybatis自动映射简单介绍、resultMap映射sql映射对象信息

前面我们编写了sql语句,并没有手动映射sql查询的结果集给对象。

是mybatis自动映射实现,Mybatis框架会获取sql结果集中的列名并查找具有相同命名的属性的set方法进行赋值

但实际开发过程中会更加复杂,例如数据库和应用程序的命名不统一,列名和属性名不相同

或者多表查询需要查询其他表信息绑定在集合中(一条订单信息包含多条商品信息)

这种情况MyBatis就无法通过自动映射实现了

所以Mybatis提供了resultMap机制来自定义结果集(手动映射)

新建一个实体类UserResultMap代替User,修改变量名,用于展示效果

public class UserResultMap {
private long id_ResultMap;  //用户id
private String name_ResultMap; //用户名
private String img_ResultMap;  //用户头像
private String pwd_ResultMap; //密码
	//省略set/get方法
}

mapper层

/**
    * resultMap手动映射结果集
    * @return
*/
public List<UserResultMap> getUserAll_Result();

先用原先的方式让mybatis自动映射试试

<!--测试resultMap手动映射结果集-->
<select id="getUserAll_Result" resultType="com.pojo.UserResultMap">
select * from `user`
</select>

然后编写测试类

/**
    * 测试resultMap
*/
@Test
public void getUserAll_Result() {
   SqlSession sqlSession = MyBatisUtil.createSqlSession();
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   List<UserResultMap> userResultMapList = userMapper.getUserAll_Result();
   System.out.println("_________________________");
   userResultMapList.forEach(v->{
       System.out.println(v.getName_ResultMap());
   });
   System.out.println("_________________________");
   MyBatisUtil.closeSqlSession(sqlSession);
}

运行结果:

结果显示空指针异常,因为mybatis找不到对应的命名的set方法,所以并没有给传值给user对象,自然集合中的对象就是个空了

在这里插入图片描述

解决方式1:可以试试将set方法名根据数据库属性字段名字

这个是数据库字段名

在这里插入图片描述

修改set方法名:

在这里插入图片描述

然后重新结果:

可以得到结论:mybatis是通过set方法的名字去寻找的

在这里插入图片描述

好了这上面是一些测试

那接下来介绍resultMap吧

修改xml映射文件

<!--测试resultMap手动映射结果集-->
<select id="getUserAll_ResultMap" resultMap="userResultMap">
select id,`name`,pwd from `user`
</select>
<!--编写resultMap映射规则-->
<resultMap id="userResultMap" type="com.pojo.UserResultMap">
<id property="id_ResultMap" column="id"/>
<result property="name_ResultMap" column="name"/>
<result property="pwd_ResultMap" column="pwd"/>
</resultMap>

<!--
可以发现select标签的返回值resultType改为了 resultMap,返回值指定的是一个resultMap的规则

而编写的resultMap进行了一次手动映射
resultMap的两个属性 id属性 和 type属性
id:映射规则集的唯一标识,可以被select元素的resultMap属性引用
type:映射的结果集返回的类型,这里指定的是UserResultMap的对象
(由于resultMap也是指定返回值,所以需要在resultMap中设置返回类型)

resultMap里面有两个标签 id标签 和 result标签
id标签:指定和数据表主键字段对应的标识属性,设置此项可以提升MyBatis框架的性能,特别是应用缓存和嵌套结果集映射的时候
result标签:指定结果集字段和实体类属性的映射关系

property:代表的是type属性指定的实体类中的某个set方法
column:代表的是sql语句查询的字段名,如果不指定(会报红,但如果名字写对了不会报错,所以建议为sql语句写上每一个字段名,如果查询字段名有重复,则可以设置别名,如果不设置,默认绑定最左边的那个字段名的值

-->

修改测试类让他多输出几个值

/**
    * 测试resultMap
*/
@Test
public void getUserAll_Result() {
   SqlSession sqlSession = MyBatisUtil.createSqlSession();
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   List<UserResultMap> userResultMapList = userMapper.getUserAll_ResultMap();
   System.out.println("_________________________");
   userResultMapList.forEach(v->{
       System.out.println(v.getId_ResultMap());//id
       System.out.println(v.getName_ResultMap());//名字
       System.out.println(v.getPwd_ResultMap());//密码
       System.out.println(v.getImg_ResultMap());//头像
   System.out.println("_________________________");
   });
   MyBatisUtil.closeSqlSession(sqlSession);
}

再次运行:

在这里插入图片描述

可以发现映射后的结果都存在,没有映射的结果则是空

另外一提,使用了resultMap后就算字段名对应了set方法名,也不会再自动映射了,因为resultMap就代表着使用手动映射,不手动映射的内容查出来了也不会进行绑定

而且resultMap是可以重复利用的 他只是一个映射规则 查询标签只需要用resultMap属性指定即可

2.resultMap嵌套结果映射association标签

一般实体类中会包含多个实体类相关的信息

如果要查询,就设计到多表联查,查询其他对象

mybatis提供了association元素用来处理查询实体类赋值属性包含其他实体类的情况

就拿这个来做示范

在这里插入图片描述

新增实体类UserType

package com.pojo;


public class UserType {

private long id;
private String typename;
}

为UserResultMap实体类添加一个UserType属性

//新增一个属性
private UserType userType_ResultMap;  //userType对象 用于测试
//省略set\get

mapper层

/**
    * association映射UserResultMap的实体类属性
    * @return
*/
public UserResultMap getUserByUserId(@Param("userId") long userId);

xml映射文件

<!--association映射UserResultMap的实体类属性-->
<select id="getUserByUserId" resultMap="association">
select user.id as userId,
  user.name,
  user.pwd,
  ut.id as userTypeId,
  ut.typename
from user
join user_type ut on user.usertype = ut.id
where user.id=#{userId}
</select>
<resultMap id="association" type="com.pojo.UserResultMap">
<id property="id_ResultMap" column="userId"/>
<result property="name_ResultMap" column="name"/>
<result property="pwd_ResultMap" column="pwd"/>
<association property="userType_ResultMap" javaType="com.pojo.UserType">
   <id property="id" column="userTypeId"/>
   <result property="typename" column="typename"/>
</association>
</resultMap>
<!--
可以发现 association是一个标签用来指定某个实体类属性,然后格外给这个实体类赋值
里面有property和javaType的两个属性
property:某个实体类的set方法
javaType:指定返回类型

其中的id和result标签的含义跟之前解说的是一样的
-->

测试类

/**
    * association映射UserResultMap的实体类属性
*/
@Test
public void getUserByUserId() {
   SqlSession sqlSession = MyBatisUtil.createSqlSession();
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   UserResultMap userResultMap = userMapper.getUserByUserId(10004);
   System.out.println("_________________________");
   //输出用户信息
   System.out.println(userResultMap.getId_ResultMap());
   System.out.println(userResultMap.getName_ResultMap());
   System.out.println(userResultMap.getPwd_ResultMap());
   //获取user中的UserType实体类
   UserType userType = userResultMap.getUserType_ResultMap();
   System.out.println("_________________________");
   //输出用户类型实体类信息
   System.out.println(userType.getId());
   System.out.println(userType.getTypename());
   System.out.println("_________________________");
   MyBatisUtil.closeSqlSession(sqlSession);
}

运行结果:

在这里插入图片描述

3.collection标签元素实现一对多(集合)

collection和association标签相似,不过collection是代表着一对多

一个用户可以有多个收货地址,拿下面这个来做示范

在这里插入图片描述

添加地址信息的实体类

package com.pojo;


public class UserAddress {

private long id;
private long userId;
private String name;
private String phone;
private String province;
private String city;
private String region;
private String particular;
private long state;
//省略set\get
}

为UserResultMap实体类添加地址集合属性

private List<UserAddress> userAddressList;    //用户地址集合
//省略set\get

mapper层

/**
    * collection一对多映射
    * @return
*/
public List<UserResultMap> getUserAllAndUserAddress();

collection层

<!--collection一对多查询-->
<select id="getUserAllAndUserAddress" resultMap="collection">
select user.id   as userId,
  user.name as userName,
  user.img,
  user.pwd,
  ua.id     as addressId,
  ua.user_id,
  ua.name as addressUserName,
  ua.phone,
  ua.province,
  ua.city,
  ua.region,
  ua.particular,
  ua.state
from user
    join user_address ua on user.id = ua.user_id
where user.id=#{userId}
</select>
<resultMap id="collection" type="com.pojo.UserResultMap">
<id property="id_ResultMap" column="userId"/>
<result property="name_ResultMap" column="userName"/>
<result property="pwd_ResultMap" column="pwd"/>
<collection property="userAddressList" ofType="com.pojo.UserAddress">
   <id property="id" column="addressId"/>
   <result property="name" column="addressUserName"/>
   <result property="phone" column="phone"/>
   <result property="province" column="province"/>
   <result property="city" column="city"/>
   <result property="region" column="region"/>
   <result property="particular" column="particular"/>
   <result property="state" column="state"/>
</collection>
</resultMap>

重点

注意sql查询出来的数据是:

在这里插入图片描述

可以发现sql查询的数据前面三条是一样的

后面是我们需要存放的用户集合列表

那怎么将三条数据合并为一条数据呢?

这时id标签就起到关键作用了 查询user信息的id指定了userId的这一列,而这一类所有值都是10001所以,只注入一条用户信息

而后面的多条数据通过collection标签指定了集合的属性 而collection中的id标签指定的主键列每条都是不一样的所以不会将三条数据合并为一条,从而存入三条数据存入到集合

这就是为什么需要id标签和result标签两个标签来手动赋值的原因

编写测试类

/**
    * collection实现一对多查询
*/
@Test
public void getUserAllAndUserAddress() {
   SqlSession sqlSession = MyBatisUtil.createSqlSession();
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   List<UserResultMap> userList = userMapper.getUserAllAndUserAddress(10001);
   userList.forEach(v->{
       System.out.println(v.getId_ResultMap());
       System.out.println(v.getName_ResultMap());
       System.out.println(v.getPwd_ResultMap());
       System.out.println("____________多条地址信息如下_____________");
       v.getUserAddressList().forEach(v2->{
           System.out.println(v2.getId());
           System.out.println(v2.getName());
           System.out.println(v2.getPhone());
           System.out.println(v2.getProvince());
           System.out.println(v2.getCity());
           System.out.println(v2.getRegion());
           System.out.println(v2.getParticular());
           System.out.println("_________________________");
       });
   });
   MyBatisUtil.closeSqlSession(sqlSession);
}

运行结果:

在这里插入图片描述

4.小结resultType和resultMap

使用select查询sql语句使用resultType和resultMap来返回数据源结果

从使用场景来看

**resultType:**直接指定结果类型,依靠自动映射实现对实体类等复杂数据类型的封装,适用于比较简单,直接的数据封装场景

**resultMap:**是对外部resultMap定义的引用可以自由控制结果映射规则和封装范围,能够处理结果集字段名与实体类属性名不一致,获需要对连接查询结果使用嵌套映射等较为复杂的问题

从底层实现类看

Mybatis将查询出来的结果集首先存储在Map结构中,以字段名为key

当select元素使用resultType数兴指定结果类型时,通过调用seter访问器实现;

使用resultMap属性时,则根据所引用的resultMap元素中定义的映射规则把Map中的键值对对赋值给指定的实体类属性

最后resultType和resultMap属性封装查询字段结果本质上是一样的给予Map数据结果。

但是在select查询元素中,二者是不能同时使用的,如果同时使用则忽略resultType的结果或者直接报错

5.resutlMap自动映射行为

上面说了使用了resultMap映射就必须全部手工映射,但那个操作太繁琐了,所以可以修改Mybatis的自动映射的规则

在MyBatis框架核心配置文件中设置自动映射的行为

<!-- 2.设置 MyBatis 框架的运行时行为-->
<settings>
   <!-- 设置自动引射行为 -->
   <setting name="autoMappingBehavior" value="FULL"/>
</settings>

修改完毕后,resultMap未手工映射的值也会被自动映射上去,前提是名字要对应上

autoMappingBehavior的value有三个值

**NONE:**禁用自动映射(resultType会失效)

PARTIAL:(默认值)对于没有用resultMap的使用自动映射,有resultMap的则改为手动映射

**FULL:**全部使用自动映射,resultMap也会使用自动映射

6.MyBatis的增删改操作

对于增删改提供有三个标签

insert、delete、update三个标签

同时用于增删改这三个操作返回的都是成功的行数,所以这三个标签默认就返回了int类型数据,也就

没有resultType和resultMap的返回值属性

1.新增操作介绍

测试下新增操作

实体类

public class User {

private long id;  //用户id
private String name; //用户名
private String pwd; //密码
//由于测试的数据库是某个项目中取出来的,所以下面的属性不展示,稍微演示一下就可以了
private String img;  //用户头像
private String phone;
private long usertype;
private long sex;
private long state;
private long credit;
//省略set/get
}

Mapper接口

/**
     * 新增用户信息
     * @return
*/
public int addUserByUser(@Param("user") User user);

xml映射

<!--新增用户信息-->
<insert id="addUserByUser"><!--默认返回受影响行数(int)所以没有resultType和resultMap-->
insert into user(`name`, img, pwd, phone, usertype, sex, state, credit)
VALUE (#{user.name},#{user.img},#{user.pwd},#{user.phone},#{user.usertype},#{user.sex},#{user.state},#{user.credit})
</insert>

测试类

@Test
public void addUserByUser() {
  //工具类构建SqlSession的时候是设置了开启事务(false),增删改这类操作需要手动提交
  SqlSession sqlSession=MyBatisUtil.createSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user=new User();
user.setName("新增用户");
user.setImg("图片信息");
user.setPwd("123");
user.setPhone("12345678911");
user.setUsertype(0);
  user.setSex(1);
   user.setState(1);
   user.setCredit(100);
   int i = userMapper.addUserByUser(user);
   System.out.println("成功行数:"+i);
   if (i>0){
       //提交数据
       sqlSession.commit();
   }else{
       //数据回滚
       sqlSession.rollback();
   }
   MyBatisUtil.closeSqlSession(sqlSession);
}

运行结果:

在这里插入图片描述

数据库:

在这里插入图片描述

删除和修改操作步骤一致,就不详细介绍了

2.新增后获取主键方法

mapper接口

/**
    * 新增用户信息
*
    * @return
*/
public int addUserByUser(@Param("user") User user);

xml映射文件

<!--新增用户信息-->
<insert id="addUserByUser" useGeneratedKeys="true" keyProperty="user.id">
insert into user(`name`, img, pwd, phone, usertype, sex, state, credit)
VALUE (#{user.name},#{user.img},#{user.pwd},#{user.phone},#{user.usertype},#{user.sex},#{user.state},#{user.credit})
</insert>
<!-- 
useGeneratedKeys:是否返回自增主键(true|false)
keyProperty:返回主键值引射到指定变量
-->

测试类

@Test
public void addUserByUser() {
   //工具类构建SqlSession的时候是设置了开启事务(false),增删改这类操作需要手动提交
   SqlSession sqlSession=MyBatisUtil.createSqlSession();
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   User user=new User();
   user.setName("新增用户");
   user.setImg("图片信息");
   user.setPwd("123");
   user.setPhone("12345678911");
   user.setUsertype(0);
   user.setSex(1);
   user.setState(1);
   user.setCredit(100);
   int i = userMapper.addUserByUser(user);
   System.out.println("成功行数:"+i);
   System.out.println("返回的主键:"+user.getId());
   if (i>0){
       //提交数据
       sqlSession.commit();
   }else{
       //数据回滚
       sqlSession.rollback();
   }
   MyBatisUtil.closeSqlSession(sqlSession);
}

断点调试:

添加数据前

在这里插入图片描述

可以发现添加数据前,传入的User的id未赋值,为0

添加数据后

在这里插入图片描述

可以发现添加数据后生成的主键返回到了传入的user参数的id中(xml中指定的user.id

7.Mybatis框架缓存(了解即可)

Mybatis的缓存有两个级别

一级缓存

Mybatis一级缓存是基于PerpetualCache的HashMap本地缓存,默认SqlSession级别的缓存,即SqlSession的一个生命周期内有效,SqlSession关闭后,该SqlSession的一级缓存会被清空,Mybatis的一级缓存默认是开启的

二级缓存

二级缓存是SqlSessionFactory级别的,即application级别,缓存中的数据可以被所有的SqlSession共享。

Mybatis的二级缓存默认是关闭的,使用时需要在Mybatis核心配置中设置

二级缓存使用方法:

1.配置核心文件

<!-- 2.设置 MyBatis 框架的运行时行为-->
<settings>
   <!-- 开启全局式二级缓存 -->
   <setting name="cacheEnabled" value="true"/>
</settings>

2.sql映射xml文件配置缓存

开机全局二级缓存后,默认也是不使用二级缓存的需要在Sql映射文件中配置缓存,为当前namespace开启二级缓存

<mapper namespace="com.mapper.UserMapper">

<!--缓存配置-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>


</mapper>

cache标签中各种元素作用如下:

**eviction:**选择缓存回收选项,主要包括以下几种策略

​ 1.(默认选项)最近最少回收,移除最长时间不被使用的缓存对象

​ 2. 先进先出,按照对象进入缓存的顺序来移除它们

​ 3. 软引用,移除基于垃圾回收器状态和软引用规则对象

​ 4. 弱引用,更积极地移除基于垃圾回收器和弱引用规则的对象

**flushInterval:**设定缓存刷新间隔,单位(毫秒ms)设定缓存多长时间自动刷新一次,默认不自动刷新

**size:**设定缓存中最多存放多少个对象,默认1024

**readOnly:**设定缓存数据是否只读,默认是false,标识缓存数据只用于读操作,Mybatis会为所有从缓存中获取数据的操作返回缓存对象的相同实例,以获取更好的性能

3.在配置sql支持二级缓存后,如需对个别查询进行调整,可以在select元素中单独设置

例如:

<!--传入User对象查询对象id-->
<select id="getUserIdByUser" resultType="int" parameterType="com.pojo.User" useCache="false"> <!-- useCache="false"代表禁用当前sql二级缓存 -->
select id from `user` where name=#{user.name} and pwd=#{user.pwd}
</select>

对于Mybatis的缓存做简单的了解即可,因为数据量达到一定规模,内置的缓存机制将无法满足要求。Mybatis的核心是Sql管理,缓存数量并不是Mybatis擅长的,所以缓存数据后续会采用Redis、MongoDB、OSCache、Memcached等专业的缓存服务器会更加合理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值