MyBatis进阶

时间:2024-05-25 星期六

MyBatis高级特性

MyBatis日志管理

日志
  • 日志文件是用于记录系统操作事件的记录文件或文件集合
  • 日志保存历史数据,使诊断问题以及理解系统活动的重要依据
SLF4J与Logback日志组件关系

在这里插入图片描述

SLF4j作为日志输出的门面,负责日志输出的表现;logback是对日志的底层实现。

使用MyBatis日志管理

步骤:

  • 在Maven工程中下的pom.xml文件中添加logback组件依赖,调价成功后,自动进行日志管理,运行程序能看到控制台有日志输出

    • <dependency>
                  <groupId>ch.qos.logback</groupId>
                  <artifactId>logback-classic</artifactId>
                  <version>1.2.3</version>
              </dependency>
      
  • 如果需要对日志输出的格式进一步要求,在resources目录下新建logback.xml文件用于配置日志的输出格式,包括如日志的输出级别(错误、调试、一般信息等),以及日志输出位置

    • <?xml version="1.0" encoding="UTF-8" ?>
      <configuration>
          <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
              <encoder>
                  <!--   %d{HH:mm:ss.SSS}时间格式
                   {%thread} 运行线程
                    %-5level 输出最低级别
                     %logger{36} 记录字符最长不超过36个
                      - %msg 日志信息
                      %n 换行
                           -->
                  <pattern>%d{HH:mm:ss.SSS} {%thread} %-5level %logger{36} - %msg%n</pattern>
              </encoder>
          </appender>
          <!--  日志输出级别(优先级高到低)
              error:错误-系统的故障日志
              warn:警告-存在风险或使用不当的日志
              info:一般性消息
              debug:程序内部用于调试信息
              trace:程序运行的跟踪信息
            -->
          <root level="debug">
              <!--   引用上面定义的<appender>标签name为console
              ,这个name名字可以随意,但是其class的值要按规定进行书写     -->
              <appender-ref ref="console"/>
          </root>
      </configuration>
      

MyBatis动态SQL

动态SQL是指在程序运行中根据参数数据动态组织SQL的技术。在MyBatis中有标签如<where>、<if test=“条件1”>这样根据条件动态添加SQL语句和参数。

使用过程:

  • 在MyBatis的映射.xml文件下添加动态SQL标签内容

    •     <!--MyBatis动态修改SQL语句-->
          <select id="dynamicSQL" parameterType="java.util.Map" resultType="com.imooc.mybatis.entity.Goods">
              select * from t_goods
              <where>
                  <!--如果传入的map参数中的categoryId不为空,则添加if标签内的SQL条件语句-->
                  <if test="categoryId != null">
                      and category_id = #{categoryId}
                  </if>
                  <!--&lt;是小于号<.在这里进行转义,不然语句出错-->
                  <if test="currentPrice != null">
                      and current_price &lt; #{currentPrice}
                  </if>
              </where>
          </select>
      
  • 测试脚本

    • // 测试MyBatis动态SQL
          @Test
          public void testDynamicSQL() throws Exception {
              SqlSession sqlSession = null;
              try {
                  sqlSession = MybatisUtils.openSession();
                  Map maps = new HashMap();
                  maps.put("categoryId",68);
                  maps.put("currentPrice",100);
                  List<Goods> list = sqlSession.selectList("goods.dynamicSQL", maps);
                  for (Goods goods : list){
                      System.out.println(goods.getTitle() +":"+goods.getCurrentPrice());
                  }
              }catch (Exception e){
                  throw e;
              }finally {
                  MybatisUtils.closeSession(sqlSession);
              }
          }
      

MyBatis二级缓存

  • 一级缓存默认开启,缓存范围是SqlSession会话

    • 在一个会话开启与关闭之间,进行对数据库的相同的操作,如查询相同的数据结果,第二次进行查询时就会使用第一次查询到的结果;如果SqlSession关闭后该缓存数据也会被清空
  • 二级缓存手动开启,属于范围Mapper Namespace,即缓存的数据是对同一个mapper的命名空间中的所有sql标签操作

    • 二级缓存运行规则

      • 二级缓存开启后默认所有查询操作均使用缓存

      • 写操作commit提交时对该namespace缓存强制清空

      • 配置uerCache=false可以不用缓存

        • 这个配置是在如<select id=“” result=“” useCache=false>这样的标签语句内容的进行属性设置
      • 配置flushCache=true代表强制清空缓存,这也是在SQL标签中进行设置

        •     <!--  <select>SQL标签名为selectAll,返回的数据结果为Goods实体类  -->
              <select id="selectAll" resultType="com.imooc.mybatis.entity.Goods" useCache="true" flushCache="true">
                  select * from t_goods order by goods_id desc limit 10
              </select>
          
    • 开启缓存,在resources目录mapper文件夹下的映射文件中添加配置

      •     <!-- 开启了二级缓存
               eviction:是缓存的清楚策略,当缓存对象数量达到上限后,自动触发对应算法对缓存对象清除
                       1.LRU - 最近最久未使用,移除最长事件不被使用的对象
                       2.FIFO - 先进先出:按对象进入缓存的顺序来移除它们
                       3.SOFT - 软引用:以处基于垃圾收集器状态和软引用规则的对象
                       4.WEAK - 弱引用:更积极的以处基于垃圾收集器状态和弱引用规则的对象
        
               flushInterval 代表间隔多长时间自动清空缓存,单位毫秒,600000毫秒-10分钟
               size 缓存存储上限,用于保存对象或集合(1个集合算一个对象)的数量上限
               readOnly 设置true,代表返回只读缓存,每次从缓存取出的是缓存对象本身,这种执行效率较高
                        设置为false,代表每次取出的是缓存对象的”副本“,每一次取出的对象都是不同的,这种安全性最高
               -->
            <cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"/>
        

MyBatis多表级联查询

与多表联合查询不同,多表联合查询使用同一个SQL语句去进行多个表之间的联合查询,而多表级联查询是关系型数据库中一对一、一对多、多对多的思想的实现。

案例:一个商品对应着多个商品详情

  • 编写商品详情实体类,mapper映射文件,商品查询语句

    • 编写商品详情实体类

      • package com.imooc.mybatis.entity;
        
        public class GoodsDetail {
            private Integer gdId;//自增属性,商品详情序号
            private Integer goodsId;//商品Id
            private String gdPicUrl;//商品图片链接
            private Integer gdOrder;//商品订单编号
        	//省略getter和setter方法
        }
        
        
    • 编写mapper映射类,goodsDetail.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="findGoodsById">
            <select id="selectGoodsByGoodsId" resultType="com.imooc.mybatis.entity.GoodsDetail">
                select * from t_goods_detail where goods_id = #{goodsId}
            </select>
        </mapper>
        
  • 在商品实体类中添加,属性为List<GoodsDetail>的商品详情信息属性,实现一个商品有多个商品详情的思想

    • package com.imooc.mybatis.entity;
      
      import java.util.List;
      
      //商品实体类
      public class Goods {
          private Integer goodsId;//商品编号
          private String title;//标题
          private String subTitle;//子标题
          private Float originalCost;//原价
          private Float currentPrice;//当前价格
          private Float discount;//折扣率
          private Integer isFreeDelivery;//是否包邮,1-包邮 0-不包邮
          private Integer categoryId;//分类编号
          private List<GoodsDetail> goodsDetailList;//商品详情,一个商品有着多个商品描述信息
      	//省略getter和setter方法
      }
      
      
  • 在商品类mapper映射中,编写对指定商品Id进行详情查询的操作

    • 定义新的结果映射,包含指定商品Id和对应的商品详情信息

      •     <!-- 编写一对多,结果映射结果
               resultMap可用于说明一对多获知多对一的映射逻辑
               id 是resultMap属性引用的标签
               type 执行one的实体(Goods)
               -->
            <resultMap id="rmGoods2" type="com.imooc.mybatis.entity.Goods">
                <!--  映射goods对象的主键到goods_id字段-->
                <id column="goods_id" property="goodsId"/>
                <!--
                    collection的含义是,在
                    select * from t_goods limit 0,1 得到结果后,对所有Goods对象遍历,以得到的goods_id字段值,
                    并带入到goodsDetail命名空间得findByGoodsId得SQL执行查询,
                    将得到的”商品详情“集合赋值给goodsDetails List对象-->
                <collection property="goodsDetailList" select="findGoodsById.selectGoodsByGoodsId"
                            column="goods_id"/>
            </resultMap>
        
    • 查询指定商品,返回的结果类型为定义的结果映射数据格式

      •     <select id="selectOneToMany" resultMap="rmGoods2">
                select * from t_goods limit 0,10
            </select>
        
    • 测试脚本

      •     //测试多表级联查询
            @Test
            public void testSelectOneToMany() throws Exception {
                SqlSession session = null;
                try {
                    session = MybatisUtils.openSession();
                    List<Goods> list = session.selectList("goods.selectOneToMany");
                    for (Goods goods : list){
                        List<GoodsDetail> goodsDetailList = goods.getGoodsDetailList();
                        for (GoodsDetail goodsDetail : goodsDetailList){
                            System.out.println(goods.getGoodsId()+" :"+goods.getTitle()+" :"+ goodsDetail.getGdPicUrl()+": "+goodsDetail.getGoodsId());
                        }
                    }
                }catch (Exception e){
                    throw e;
                }finally {
                    MybatisUtils.closeSession(session);
                }
            }
        

MyBatis分页插件PageHelper

使用流程
  • maven工程中,在pom.xmle文件中引入PageHelper与jsqlparser依赖

    •         <!--    PageHelper分页插件依赖    -->
              <dependency>
                  <groupId>com.github.pagehelper</groupId>
                  <artifactId>pagehelper</artifactId>
                  <version>5.1.11</version>
              </dependency>
              <dependency>
                  <groupId>com.github.jsqlparser</groupId>
                  <artifactId>jsqlparser</artifactId>
                  <version>2.0</version>
              </dependency>
      
  • 在mybatis-config.xml文件中添加Plugin配置

    •     <!-- 启用Pagehelper分页插件   -->
          <plugins>
              <plugin interceptor="com.github.pagehelper.PageInterceptor">
                  <!--   设置数据库类型   -->
                  <property name="helperDialect" value="mysql"/>
                  <!--    分页插件合理化,开启配置如每页显示多少数据-->
                  <property name="reasonable" value="true"/>
              </plugin>
          </plugins>
      
  • 在java程序代码中使用PageHelper.startPage()自动分页

    • 在映射类中编写一个过滤查询语句,id为selectPage用于提供分页数据

      •     <!--  查询现价小于1000得商品  -->
            <select id="selectPage" resultType="com.imooc.mybatis.entity.Goods">
                select * from t_goods where current_price &lt; 1000
            </select>
        
    • 测试脚本

      •  //测试使用Pagehelper分页插件
            @Test
            public void testSelectPage() throws Exception {
                SqlSession session = null;
                try {
                    session = MybatisUtils.openSession();
                    /*startPage方法会自动将下一次查询进行分页*/
                    PageHelper.startPage(2,10);//查询第二页,每一页展示10条数据
                    Page<Goods> page = (Page) session.selectList("goods.selectPage");
                    System.out.println("总页数:"+ page.getPages());
                    System.out.println("总记录数:"+ page.getTotal());
                    System.out.println("开始行数:"+ page.getStartRow());
                    System.out.println("结束行号:"+ page.getEndRow());
                    System.out.println("当前页码:"+ page.getPageNum());
                    List<Goods> data = page.getResult();//当前页数据
        
                    for (Goods goods : data){
                        System.out.println(goods.getTitle());
                    }
                }catch (Exception e){
                    throw e;
                }finally {
                    MybatisUtils.closeSession(session);
                }
            }
        

MyBatis整合C3P0连接池

替换Mybatis默认配置得数据库连接池,因为其不是最好得数据库连接池,通过替换,可以提高性能。

整合过程:

  • 在maven工程中得pom.xml文件中,添加C3P0数据库连接池依赖

    •         <!-- C3P0数据库连接池依赖    -->
              <dependency>
                  <groupId>com.mchange</groupId>
                  <artifactId>c3p0</artifactId>
                  <version>0.9.5.4</version>
              </dependency>
      
  • 编写C3P0数据库连接池配置类,该类继承自org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory,是属于Mybatis框架的。

    • package com.imooc.mybatis.datasource;
      
      import com.mchange.v2.c3p0.ComboPooledDataSource;
      import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
      
      /**
       * C3P0与MyBatis兼容使用得数据源工厂类
       */
      public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
          public C3P0DataSourceFactory(){
              this.dataSource = new ComboPooledDataSource();//使用c3p0数据源替换UnpooledDataSourceFactory得数据源,
              // 将mybatis中得UnpooledDataSourceFactory属性进行更改,进而整合MyBatis和C3P0
          }
      }
      
  • 到mybatis-config.xml中进行数据库连接池的配置

    •         <!--            采用C3P0连接池方式管理数据库连接-->
              <dataSource type="com.imooc.mybatis.datasource.C3P0DataSourceFactory">
                  <!--    C3P0与MyBatis的数据库连接池的属性名不太相同,所以需要对 name进行修改        -->
                  <property name="driverClass" value="com.mysql.jdbc.Driver"/>
                  <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&amp;characterCoding=UTF-8"/>
                  <property name="user" value="root"/>
                  <property name="password" value="123456"/>
                  <!--    设置数据库连接池的连接配置        -->
                  <!--    初始化连接池大小为5        -->
                  <property name="initialPoolSize" value="5"/>
                  <!--    设置最大最小连接数       -->
                  <property name="maxPoolSize" value="20"/>
                  <property name="minPoolSize" value="5"/>
              </dataSource>
          </environment>
      

​ 完成以上步骤,就完成了C3P0整合过程。

MyBatis批处理数据

批处理与单处理插入
  • <insert>语句

    •     <!-- 批量插入数据,使用集合数据类型进行批量插入   -->
          <!-- 一次性输入多条数据的sql语句格式为
             INSERT INTO table
             VALUES ("a","a1","a2"),("b","b1","b2"),(值1,值2,值3),(....)
             -->
          <insert id="batchInsert" parameterType="java.util.List">
              INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
              VALUES
              <!-- 批量插入数据,使用集合数据类型进行批量插入   -->
              <foreach collection="list" item="item" index="index" separator=",">
                  (#{item.title},#{item.subTitle},#{item.originalCost},#{item.currentPrice},#{item.discount}
                  ,#{item.isFreeDelivery},#{item.categoryId})
              </foreach>
          </insert>
          <!--一条一条的插入-->
          <insert id="normalInsert" parameterType="com.imooc.mybatis.entity.Goods">
              insert into `babytun`.`t_goods`(`title`,`sub_title`,`original_cost`,`current_price`,`discount`,`is_free_delivery`,
                                              `category_id`)
              values (#{title},#{subTitle},#{originalCost},#{currentPrice},#{discount},#{isFreeDelivery},#{categoryId})
          </insert>
      
  • 测试脚本

    • //测试批量插入
          @Test
          public void testBatchInsert() throws Exception {
              SqlSession session = null;
              try {
                  long st = new Date().getTime();
                  session = MybatisUtils.openSession();
                  List list = new ArrayList();
                  for (int i = 0; i < 10000; i++){
                      Goods goods = new Goods();
                      goods.setTitle("测试商品");
                      goods.setSubTitle("测似子标题");
                      goods.setOriginalCost(200f);
                      goods.setCurrentPrice(100f);
                      goods.setDiscount(0.5f);
                      goods.setIsFreeDelivery(1);
                      goods.setCategoryId(43);
      
                      list.add(goods);
                  }
                  int result = session.insert("goods.batchInsert", list);
                  System.out.println("插入"+result+"条数据");
                  session.commit();//提交事务数据
                  long et = new Date().getTime();
                  System.out.println("执行时间:"+(et-st) + "毫秒");
              }catch (Exception e){
                  throw e;
              }finally {
                  MybatisUtils.closeSession(session);
              }
          }
      
          //测试每次单条插入
          @Test
          public void testNormalInsert() throws Exception {
              SqlSession session = null;
              Integer result = 0;//累计插入条数
              try {
                  long st = new Date().getTime();
                  session = MybatisUtils.openSession();
                  List list = new ArrayList();
      
                  for (int i = 0; i < 10000; i++){
                      Goods goods = new Goods();
                      goods.setTitle("测试商品");
                      goods.setSubTitle("测似子标题");
                      goods.setOriginalCost(200f);
                      goods.setCurrentPrice(100f);
                      goods.setDiscount(0.5f);
                      goods.setIsFreeDelivery(1);
                      goods.setCategoryId(43);
      
                      session.insert("goods.normalInsert",goods);
                      result += 1;
                  }
                  System.out.println("插入"+result+"条数据");
                  session.commit();//提交事务数据
                  long et = new Date().getTime();
                  System.out.println("执行时间:"+(et-st) + "毫秒");
              }catch (Exception e){
                  throw e;
              }finally {
                  MybatisUtils.closeSession(session);
              }
          }
      
  • 对比处理速度,批处理比单处理速度快了两到三倍。

删除批处理
  • <delete>语句

    •     <!-- 批量删除数据  -->
          <!-- 一次性删除多条数据的sql语句格式为
         delete from table where 字段1 in(值1,值2)
         -->
          <delete id="batchDelete" parameterType="java.util.List">
              DELETE FROM t_goods WHERE goods_id in
              <foreach collection="list" item="item" index="index" open="(" close=")" separator=",">
                  #{item}
              </foreach>
          </delete>
      
  • 测试脚本

    •     //测试删除批处理
          @Test
          public void testBatchDelete() throws Exception {
              SqlSession session = null;
              try {
                  long st = new Date().getTime();
                  session = MybatisUtils.openSession();
                  List list = new ArrayList();
                  list.add(1930);
                  list.add(1931);
                  list.add(1932);
                  int result = session.insert("goods.batchDelete", list);
                  session.commit();//提交事务数据
                  long et = new Date().getTime();
                  System.out.println("执行时间:"+(et-st) + "毫秒");
              }catch (Exception e){
                  session.rollback();
                  throw e;
              }finally {
                  MybatisUtils.closeSession(session);
              }
          }
      

MyBatis注解开发方式

除了使用mapper映射文件.xml进行SQL语句的编写之外,还可以使用注解得方式进行MyBatis的SQL语句编写,实现对数据库的操作。

MyBatis常用注解

在Maven工程的src/main/java/…/…/…/目录下新建DAO包用于存放数据访问对象接口类,如GoodsDAO

注解方式的查询操作

实现步骤:

  • 在接口类GoodsDAO中添加查询注解和方法定义

    • package com.imooc.mybatis.dao;
      
      import com.imooc.mybatis.entity.Goods;
      import org.apache.ibatis.annotations.Param;
      import org.apache.ibatis.annotations.Select;
      
      import java.util.List;
      
      //使用MyBatis的注解进行数据访问对象编写
      public interface GoodsDAO {
          @Select("select * from t_goods where current_price between #{min} and #{max}order by current_price limit 0,#{limit}")
          public List<Goods> selectGoodsByPrice(@Param("min") Float min,@Param("max") Float max, @Param("limit") Integer limit);
      }
      
    • 在mybatis-config.xml中的<mapper>标签处,进行GoodsDAO的声明,有两种方式,推荐使用包路径配置

      •     <!-- MyBatis使用注解进行开发   -->
                    <!--  (1).加载GoodsDAO接口类  -->
        <!--    <mapper class="com.imooc.mybatis.dao.GoodsDAO"/>-->
                    <!--  (2).加载com.imooc.mybatis.dao该路径下的所有dao资源  -->
            <package name="com.imooc.mybatis.dao"/>
        
    • 测试脚本

      •     //测试注解查询接口
            @Test
            public void testGoodsDAOSelect() throws Exception {
                SqlSession session = null;
                try {
                    session = MybatisUtils.openSession();
                    GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
                    List<Goods> list = goodsDAO.selectGoodsByPrice(10f, 100f, 10);
                    for (Goods goods : list){
                        System.out.println(goods.getTitle());
                    }
                }catch (Exception e){
                    throw e;
                }finally {
                    MybatisUtils.closeSession(session);
                }
            }
        
注解方式的插入操作
  • 顶入插入方法

    •     //数据插入,并获取插入的goods_id(这是自增的数据字段)
          @Insert("insert into `babytun`.`t_goods`(`title`,`sub_title`,`original_cost`,`current_price`,`discount`,`is_free_delivery`,`category_id`)  values (#{title},#{subTitle},#{originalCost},#{currentPrice},#{discount},#{isFreeDelivery},#{categoryId})")
          //下面的内容同xml文件中的selectKeys等价
          @SelectKey(statement = "select last_insert_id()",before = false, keyProperty = "goodsId", resultType = Integer.class)
          public int insert(Goods goods);
      
  • 测试

    • //测试注解插入接口
          @Test
          public void testGoodsDAOInsert() throws Exception {
              SqlSession session = null;
              try {
                  session = MybatisUtils.openSession();
                  GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
                  Goods goods = new Goods();
                  goods.setTitle("测试Title");
                  goods.setSubTitle("测试subTitle");
                  goods.setOriginalCost(1000f);
                  goods.setCurrentPrice(1200f);
                  goods.setDiscount(0f);
                  goods.setIsFreeDelivery(1);
                  goods.setCategoryId(2001);
                  Integer num = goodsDAO.insert(goods);
                  session.commit();//提交事务数据
                  System.out.println("插入成功条数为:"+ num);
                  System.out.println("插入的goodsId为:"+goods.getGoodsId());
              }catch (Exception e){
                  throw e;
              }finally {
                  MybatisUtils.closeSession(session);
              }
      
注解方式进行结果映射
  • 定义接口方法

    • 
          //使用注解进行结果映射resultMap
          @Select("select * from t_goods")
          //xml文件中结果映射
          /**
           *   <!--  结果映射  -->
           *     <resultMap id="rmGoods" type="com.imooc.mybatis.dto.GoodsDTO">
           *         <!--        设置主键与属性映射-->
           *         <id property="goods.goodsId" column="goods_id"></id>
           *         <!-- 设置非主键字段与属性映射  -->
           *         <result property="goods.title" column="title"></result>
           *         <result property="goods.currentPrice" column="current_price"></result>
           *     </resultMap>
           */
          //下面的注解内容,等价于<resultMap>标签内容
          @Results({
                  //id,主键
                  @Result(column = "goods_id",property = "goodsId",id = true),
                  //<result>
                  @Result(column = "title", property = "title"),
                  @Result(column = "current_price", property = "currentPrice")
          })
          public List<GoodsDTO2> selectAll();
      
  • 测试

    •  //测试注解方式进行结果映射
          @Test
          public void testSelectAll2() throws Exception {
              SqlSession session = null;
              try {
                  session = MybatisUtils.openSession();
                  GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
                  List<GoodsDTO2> list = goodsDAO.selectAll();
                  System.out.println(list.size());
              }catch (Exception e){
                  throw e;
              }finally {
                  MybatisUtils.closeSession(session);
              }
          }
      

(column = “current_price”, property = “currentPrice”)
})
public List selectAll();
```

  • 测试

    •  //测试注解方式进行结果映射
          @Test
          public void testSelectAll2() throws Exception {
              SqlSession session = null;
              try {
                  session = MybatisUtils.openSession();
                  GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
                  List<GoodsDTO2> list = goodsDAO.selectAll();
                  System.out.println(list.size());
              }catch (Exception e){
                  throw e;
              }finally {
                  MybatisUtils.closeSession(session);
              }
          }
      
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

这啥呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值