Mybatis 学习记录

Mybatis简介

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

持久层

  • 持久化就是将程序在持久和瞬时状态转化过程
为什么需要mybatis
  • 传统的JDBC代码太复杂,需要一个框架将其简化
    • 帮助程序员将数据存入数据库
开始尝试创建Mybatis
  • 创建简单的Maven工程
  • 删除src目录,将单纯的Maven目录当做父目录
  • 导入依赖(jar包)
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.16</version>
</dependency>

步骤
  • 编写实体类
  • 编写核心配置文件
  • 编写接口
  • 编写Mapper.xml文件
  • 测试

核心配置文件

XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)

  • 在resources下建立Mybatis-config.xml文件

  • <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <!--        configuration核心配置文件-->
    <configuration>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/book?useSSL=true&amp;useUnicode=true&amp;serverTimezone=CST&amp;characterEncoding=utf-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value=""/>
                </dataSource>
            </environment>
        </environments>
    
        <!--    <mappers>-->
        <!--        <mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
        <!--    </mappers>-->
    </configuration>
    

    从 XML 中构建 SqlSessionFactory

  • 创建一个工具类utils

  • 加入静态方法

public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            //获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession() {//获取SqlSession对象
        return sqlSessionFactory.openSession();
    }
}
创建实体类
public class User {
    private int id;
    private String name;
    private String pwd;

    public User() {
    }

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}
dao接口
public interface UserDao {

    List<User> getUserList();
}

接口实现类由原来的DaoImpl转变为一个Mapper.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绑定一个Dao/Mapper接口-->
    <mapper namespace="com.suwenlong.dao.UserDao">

    <select id="getUserList" resultType="com.suwenlong.bean.User">
        select * from book.adminstor;
    </select>
</mapper>
junit包进行测试
  • 在测试的时候我遇到一个问题–Error : java 不支持发行版本5

在这里插入图片描述

解决办法一

①在Intellij中点击“File” -->“Project Structure”,看一下“Project”和“Module”栏目中Java版本是否与本地一致:

在这里插入图片描述

在这里插入图片描述

②点击“Settings”–>“Bulid, Execution,Deployment”–>“Java Compiler”,Target bytecode version设为本地Java版本。(可以在Default Settings中把Project bytecode version 一劳永逸地配置成本地Java版本)
在这里插入图片描述

但是最后我运行可以了,不再是这个错误了

解决办法二

①在 maven地址\conf\setting.xml
中设置默认jdk版本…

在这里插入图片描述

代码如下:

<profile>
    <id>development</id>
    <activation>
        <jdk>1.8</jdk>
        <activeByDefault>true</activeByDefault>
    </activation>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
    </properties>
</profile>

②并且复制一份到 C:\Users\本地用户名.m2中

在这里插入图片描述

对于SQLSessionFactoryBuiler和SQLSessionFactory及SQLSession的官方介绍

在这里插入图片描述

NullPointerException有一个错误困扰了我好久,就是MybatisUtils.java文件中的sqlSessionFactory全局变量声明要删除局部变量声明,困扰了我一天了。

在这里插入图片描述

CLUD

  • 增删改查
namespace
  • namespace中的包名要和Mapper接口中的包名一致
Update
Delete
select
  • id就是namespace中对应的方法名
  • resultType:SQL语句的返回值(结果集)
  • parameterType 参数类型(常见类型不写也行)
Insert
  • 编写接口
public interface UserMapper {

 List<User> getUserList();//获取全部用户

 User getUserById(int id);//通过id获取用户

 int addUser(User user);//插入一个用户

 int updateUser(User user);//更新用户

 int deleteUser(int id);//更新用户
}
  • 编写Mapper中对应的SQL语句

  • <?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">
    <!--        namespace绑定一个对应的Mapper接口-->
     <mapper namespace="com.suwenlong.dao.UserMapper">
    
     <select id="getUserList" resultType="com.suwenlong.bean.User">
         select * from mybatis.user;
     </select>
    
     <select id="getUserById" parameterType="int" resultType="com.suwenlong.bean.User">
         select * from mybatis.user  where id = #{id};
     </select>
    
    <!--    对象中的属性。可以直接取用-->
     <insert id="addUser" parameterType="com.suwenlong.bean.User">
         insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd})
     </insert>
    
     <update id="updateUser" parameterType="com.suwenlong.bean.User">
         update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
     </update>
    
     <delete id="deleteUser" parameterType="int">
         delete from mybatis.user where id=#{id};
     </delete>
    </mapper>
    
  • test

public class UserDaoTest {
    @Test
    public void test(){
        SqlSession sqlSession = null;
        List<User> userList = null;
        try {
            //获取SqlSession对象
            sqlSession = MybatisUtils.getSqlSession();
            //执行SQL
            //方式一:getMapper
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            userList = userMapper.getUserList();
         /*//        方式二
     List<User> userList1 = sqlSession.selectList("com.suwenlong.dao.UserMapper.getUserList");*/
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }
        for (User user : userList) {
            System.out.println(user);        }

    }

    @Test
    public void getUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.getUserById(1);
        System.out.println(user);

        sqlSession.close();
    }

    @Test
    public void addUser(){//增删改需要提交事务
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int res = mapper.addUser(new User(4, "哈哈哈", "12356"));
        sqlSession.commit();//提交事务
        sqlSession.close();
    }

    @Test
    public void updateUser(){//增删改需要提交事务
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int res = mapper.updateUser(new User(4,"updete","123456"));
        sqlSession.commit();//提交事务
        sqlSession.close();
    }

    @Test
    public void deleteUser(){//增删改需要提交事务
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int res = mapper.deleteUser(4);
        sqlSession.commit();//提交事务
        sqlSession.close();
    }
}
模糊查询的两种实现
  • java代码查询执行的视乎,传递通配符% %
List<User> userList = mappser.getUserList("%李%")
  • 在SQL拼接中使用通配符
select * from mybatis.user where name like %#{values}%

配置

properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)

环境配置

  • mybatis可以配置成适应多种环境
  • 不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
  • Mybatis默认事务管理器是JDBC,连接池POOLED
属性properties
  • 属性可以有外部的配置可以动态替换,也可以在java文件中配置,亦可以通过properties元素的子元素传递。
  • 编写一个dp.properties配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&serverTimezone=CST&useUnicode=true&characterEncoding=utf-8
uername=root
password=
在mybatis-config.xml中引入外部文件dp.properties
<properties resource="dp.properties"/>

在这里插入图片描述

  • 如果两个文件有同一个字段,优先使用外部配置文件dp.properties
类型别名typeAliases
  • 类型别名可为 Java 类型设置一个缩写名字。
  • 意在降低冗余的全限定类名书写。
<!--    可以给实体类起一个别名-->
<typeAliases>
    <typeAlias type="com.suwenlong.bean.User" alias="User"/>
</typeAliases>

在这里插入图片描述

在这里插入图片描述

  • 也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
  • 在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author
<!--    可以给实体类起一个别名-->
<typeAliases>
    <pa type="com.suwenlong.bean" />
</typeAliases>
  • 若有注解,则别名为其注解值

在这里插入图片描述

  • 实体类比较少的情况下可以使用mybatis-config.xml配置别名
  • 实体类比较多的情况下可以实体类注解起别名
@Alias("user")
public class User{}

设置

  • 这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

在这里插入图片描述

在这里插入图片描述

映射器 mappers

  • 就是注册那玩意
方式一 使用resource
<mappers>
        <mapper resource="com/suwenlong/dao/UserMapper.xml"/>
</mappers>
方式二 使用class文件绑定注册
<mappers>
        <mapper class="com/suwenlong/dao/UserMapper"/>
</mappers>
  • 方式二缺陷
    • 接口和他的配置Mapper文件必须同名
    • 接口和他的Mapper配置文件必须在同一个目录下
  • 方式二好处
    • 可以使用注释
方式三 使用扫描包
<mappers>
        <package class="com/suwenlong/dao"/>
</mappers>
  • 方式三缺陷
    • 接口和他的配置Mapper文件必须同名
    • 接口和他的Mapper配置文件必须在同一个目录

resultMap 结果集映射

id name pwd

id name password

  • 为了解决从数据库中取出的的数据为null(原因是实体类中的User的属性与数据库中的属性名不一致导致查询结果为空)

  • 将结果集映射为相同的User实体类

在这里插入图片描述

日志 logImpl

  • 排错手段 sout debug
  • 日志工程

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

  • SLF4J
  • LOG4J【掌握】
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING 【掌握】
  • NO_LOGGING
<settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
LOG4J
  • LOG4J 比较麻烦,这里单独说
  • 百度百科

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,我们也可以控制每一条日志的输出格式

  • 先导入包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

  • 配置log4j.properties配置文件
# priority  :debug<info<warn<error
#you cannot specify every priority with different file for log4j
#控制输出
log4j.rootLogger=debug,stdout,info,warn

#console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss a}]:%p %l%m%n
#info log
log4j.logger.info=info
log4j.appender.info=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.info.File=./src/com/hp/log/info.log
log4j.appender.info.Append=true
log4j.appender.info.Threshold=INFO
log4j.appender.info.layout=org.apache.log4j.PatternLayout 
log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#debug log
log4j.logger.debug=debug
log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.debug.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.debug.File=./src/com/hp/log/debug.log
log4j.appender.debug.Append=true
log4j.appender.debug.Threshold=DEBUG
log4j.appender.debug.layout=org.apache.log4j.PatternLayout 
log4j.appender.debug.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#warn log
log4j.logger.warn=warn
log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.warn.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.warn.File=./src/com/hp/log/warn.log
log4j.appender.warn.Append=true
log4j.appender.warn.Threshold=WARN
log4j.appender.warn.layout=org.apache.log4j.PatternLayout 
log4j.appender.warn.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#error
log4j.logger.error=error
log4j.appender.error = org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.error.File = ./src/com/hp/log/error.log 
log4j.appender.error.Append = true
log4j.appender.error.Threshold = ERROR 
log4j.appender.error.layout = org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
  • 配置settings
<settings>
    <!--        标准的日志工程实现-->
    <setting name="logImpl" value="LOG4J"/>
</settings>
  • 直接测试运行
或者编写一个测试类
@Test
public void testLog4j(){

    logger.info("info:进入testLog4j");
    logger.debug("debug:进入testLog4j");
    logger.error("error:进入testLog4j");
}
分页实现查询
  • 类似 select * from user limit 0,2;但现在尚且用不到。在这里留下插槽,后面有时间补上

  • 首先实现接口类

List<User> getUserByLimit(Map<String,Integer> map);//分页
  • 实现Mapper
<!--    分页查询-->
<select id="getUserByLimit" resultType="User" parameterType="map">
    select * from mybatis.user limit #{startIndex},#{pageSize};
</select>
  • 编写测试类
@Test
public void getUserByLimit(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Integer> map = new HashMap<>();
    map.put("startIndex",0);
    map.put("pageSize",2);
    List<User> userByLimit = mapper.getUserByLimit(map);
    for (User user : userByLimit) {
        System.out.println(user);
    }
    sqlSession.close();
}
使用注解开发
  • 了解就行,在接口上直接注解
@Select("select * from user")

在这里插入图片描述

  • 然后在核心配置文件绑定接口
<mappers>
        <mapper class="com/suwenlong/dao/UserMapper"/>
</mappers>
多对一查询
  • 说实话没看懂回来抽时间看

在这里插入图片描述

一对多查询

  • 还没看,抽时间在看

动态SQL

  • 动态SQL指的是根据不同的条件生成不同的SQL语句
  • 官方介绍如下

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach
搭建环境
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '创建作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间', 
`view` INT(30) NOT NULL COMMENT '阅览量'
)ENGINE=INNODB DEFAULT CHARSET=UTF8
创建一个基础工程
  • 导包
  • 编写配置文件
  • 编写实体类
  • 编写实体类对应的Mapper接口和Mapper.xml文件

IF语句

这条语句提供了可选的查找文本功能。如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from mybatis.blog where 1=1
    <if test="title != null">
        and title=#{title}
    </if>
    <if test="author != null">
        and author=#{author}
    </if>
</select>

choose(when,otherwise)

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

  • choose必须选一个执行
<select id="queryBlogChoose" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <choose>
            <when test="title != null">
                title=#{title}
            </when>
            <when test="author != null">
                and author=#{author}
            </when>
            <otherwise>
                and view=#{view}
            </otherwise>
        </choose>
    </where>
</select>	

trim(where,set)

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <if test="title != null">
            and title=#{title}
        </if>
        <if test="author != null">
            and author=#{author}
        </if>
    </where>
</select>
<update id="updateBlog" parameterType="map">
    update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if>
    </set>
    where id = #{id}
</update>
SQL片段
<sql id="if-title-author">
    <if test="title != null">
        and title=#{title}
    </if>
    <if test="author != null">
        and author=#{author}
    </if>
</sql>

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>


foreach

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

<!--    select * from mybatis.blog where 1=1 and (id=1 or id=2 or id=3)-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id=#{id}
        </foreach>
    </where>
</select>
@Test
    public void queryBlogForeach(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        Blogmapper mapper = sqlSession.getMapper(Blogmapper.class);
        HashMap hashMap = new HashMap();
        ArrayList<String> ids = new ArrayList<>();
        ids.add("6a638b43921b4a3fa720022b3093061c");
        ids.add("040d32ef814248cc8d9425566afe991c");
        hashMap.put("ids",ids);
        List<Blog> blogs = mapper.queryBlogForeach(hashMap);
        for (Blog blog : blogs) {
            System.out.println(blog);

        }
        sqlSession.close();
    }
动态SQL就是在拼接语句,我们只要保证SQL语句的正确性,按照SQL 的格式,去排列组合就行

缓存

  • 因为每次连接数据库都需要消耗资源与时间,但是我们可以把一次查询的结果存在内存中,就不需要每一次都调用数据库了

  • 缓存是为了解决高并发问题

缓存失效情况
  • 查询不同的东西
  • 增删改操作,会改变原来的数据,必定刷新缓存
  • 查询不同的Mapper.xml
  • 手动清理缓存
sqlSession.cleanCache();//手动清理缓存

在这里插入图片描述

实体类最好序列化
  • implements Serializable

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值