myBatis入门学习_上

MyBatis 百度百科介绍

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)

下载地址

点我去下载地址

推荐下载版本 mybatis-3.1.1-bundle.zip

下载完成后,拷贝所有jar包到工程里面
这里写图片描述

环境搭建

创建一个数据库……….自己创建,
导入数据,复制sql,执行一下就行

/*
Navicat MySQL Data Transfer

Source Server         : connection
Source Server Version : 50715
Source Host           : localhost:3306
Source Database       : mybatis

Target Server Type    : MYSQL
Target Server Version : 50715
File Encoding         : 65001

Date: 2016-12-01 22:40:43
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for items
-- ----------------------------
DROP TABLE IF EXISTS `items`;
CREATE TABLE `items` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL COMMENT '商品名称',
  `price` float(10,1) NOT NULL COMMENT '商品定价',
  `detail` text COMMENT '商品描述',
  `pic` varchar(64) DEFAULT NULL COMMENT '商品图片',
  `createtime` datetime NOT NULL COMMENT '生产日期',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of items
-- ----------------------------
INSERT INTO `items` VALUES ('1', '台式机', '3000.0', '该电脑质量非常好!!!!', null, '2015-02-03 13:22:53');
INSERT INTO `items` VALUES ('2', '笔记本', '6000.0', '笔记本性能好,质量好!!!!!', null, '2015-02-09 13:22:57');
INSERT INTO `items` VALUES ('3', '背包', '200.0', '名牌背包,容量大质量好!!!!', null, '2015-02-06 13:23:02');

-- ----------------------------
-- Table structure for orderdetail
-- ----------------------------
DROP TABLE IF EXISTS `orderdetail`;
CREATE TABLE `orderdetail` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `orders_id` int(11) NOT NULL COMMENT '订单id',
  `items_id` int(11) NOT NULL COMMENT '商品id',
  `items_num` int(11) DEFAULT NULL COMMENT '商品购买数量',
  PRIMARY KEY (`id`),
  KEY `FK_orderdetail_1` (`orders_id`),
  KEY `FK_orderdetail_2` (`items_id`),
  CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of orderdetail
-- ----------------------------
INSERT INTO `orderdetail` VALUES ('1', '3', '1', '1');
INSERT INTO `orderdetail` VALUES ('2', '3', '2', '3');
INSERT INTO `orderdetail` VALUES ('3', '4', '3', '4');
INSERT INTO `orderdetail` VALUES ('4', '4', '2', '3');

-- ----------------------------
-- Table structure for orders
-- ----------------------------
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '下单用户id',
  `number` varchar(32) NOT NULL COMMENT '订单号',
  `createtime` datetime NOT NULL COMMENT '创建订单时间',
  `note` varchar(100) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`),
  KEY `FK_orders_1` (`user_id`),
  CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of orders
-- ----------------------------
INSERT INTO `orders` VALUES ('3', '1', '1000010', '2015-02-04 13:22:35', null);
INSERT INTO `orders` VALUES ('4', '1', '1000011', '2015-02-03 13:22:41', null);
INSERT INTO `orders` VALUES ('5', '10', '1000012', '2015-02-12 16:13:23', null);

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` date DEFAULT NULL COMMENT '生日',
  `sex` char(1) DEFAULT NULL COMMENT '性别',
  `address` varchar(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '王五', null, '2', null);
INSERT INTO `user` VALUES ('10', '张三', '2014-07-10', '1', '北京市');
INSERT INTO `user` VALUES ('16', '张小明', null, '1', '河南郑州');
INSERT INTO `user` VALUES ('22', '陈小明', null, '1', '河南郑州');
INSERT INTO `user` VALUES ('24', '张三丰', null, '1', '河南郑州');
INSERT INTO `user` VALUES ('25', '陈小明', null, '1', '河南郑州');
INSERT INTO `user` VALUES ('26', '王五', null, null, null);

搭建完数据库环境后,然后开始搭myBatis所需的配置

  • 创建pojo类
package cn.mybatis.pojo;

import java.util.Date;
/**
 *
 * @title User.java
 * <p>description:用户表映射的PO类</p>
 * <p>company: www.itheima.com</p>
 * @author ljh
 * @version 1.0
 */
public class User {
    private Integer id;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", sex=" + sex + ", birthday=" + birthday + ", address="
                + address + "]";
    }

}
  • 在src同级目录下创建config文件夹
    创建一个log4j.properties文件….
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

创建mybatis 核心文件SqlMapConfig.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> 
    <environments default="development">
        <environment id="development">      
            <transactionManager type="JDBC"/>       
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="itcast"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

创建sql映射文件,因为mybatis是半自动化sql的框架,所以sql这边还是我们自己写的……所以我们需要创建sql映射文件
在config目录下创建一个sqlmap文件夹,里面创建一个pojo的映射文件….

<?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 :命名空间,用于隔离sql语句,后面会讲另一层非常重要的作用。-->
<mapper namespace="test">

<!--parameterType:定义输入到sql中的映射类型,#{id}表示使用preparedstatement设置占位符号并将输入变量id传到sql,其实就相当于jdbc中的 ? -->
<!--resultType:定义输出结果映射类型。-->
    <select id="findUserById" parameterType="int" resultType="cn.mybatis.pojo.User">
        select * from user where id = #{id}
    </select>
</mapper>

这里说明一下. 类型如果是八种基本类型的一种,可以直接写,如果不是,需要写类的全路径 比如 java.lang.String,当然,mybatis也提供了一些别名,写别名也是可以的,我个人比较喜欢全路径名,,别名还要记,麻烦,下面是mybatis支持的别名,有兴趣的小伙伴可以看看

这里写图片描述

这里提一下,mybatis支持自定义别名,后面我会讲,

创建完映射文件后,我们需要在核心配置文件里引入一下映射文件,

    <mappers>
        <mapper resource="sqlmap/User.xml"></mapper>
    </mappers>

环境已经搭建的差不多了,我们来看看目录结构
这里写图片描述

现在我们就来使用刚刚写的一条sql语句,做一个查询小案例

package test;

import cn.mybatis.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

public class Demo {

    @Test
    public void fun01() throws IOException {
        String resources = "mybatisConfig.xml";

        //Resources.getResourceAsStream(resources)  这句代码的意思就是查找classpath目录下的mybatisConfig.xml文件
        InputStream stream = Resources.getResourceAsStream(resources);
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //获取工厂
        SqlSessionFactory factory = builder.build(stream);
        //获取session
        SqlSession session = factory.openSession();

        //test.findUserById      映射文件中的名称空间.查询语句的id    目的是确定使用具体的哪一条sql
        User user = session.selectOne("test.findUserById", 1);

        System.out.println(user);

        session.close();
    }
}

运行代码如下
这里写图片描述

嗯,然后我们趁热打铁,写一个插入sql的小案例,
因为我们需要写sql,所以我们需要去映射文件里面添加一条sql语句 ,需要注意的是 #{}里面的值需要跟pojo里面的属性名一致,属性名可以理解为成员变量名

    <insert id="insert" parameterType="cn.mybatis.pojo.User">
        insert into user(username,birthday,sex,address) VALUE(#{username},#{birthday},#{sex},#{address});
    </insert>

然后写测试代码

 @Test
    public void insert() throws IOException {
        String resources = "mybatisConfig.xml";

        //Resources.getResourceAsStream(resources)  这句代码的意思就是查找classpath目录下的mybatisConfig.xml文件
        InputStream stream = Resources.getResourceAsStream(resources);
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //获取工厂
        SqlSessionFactory factory = builder.build(stream);
        //获取session
        SqlSession session = factory.openSession();

        //test.findUserById      映射文件中的名称空间.查询语句的id    目的是确定使用具体的哪一条sql
        User user = new User();

        user.setUsername("李白");
        user.setSex("男");
        user.setAddress("广东省");
        user.setBirthday(new Date());
        session.insert("test.insert", user);

        session.close();
    }

我们来看执行结果
这里写图片描述

回滚的原因是没有提交事务,我们需要手动提交事务,增删改都需要提交事务,这是需要注意的.

我们添加 session.commit();再进行测试

这里写图片描述

这里有个小案例,需求是:执行插入语句的同时获取插入时的id,
这里我一开始想的办法是先插入,再查询最大的id,返回,但是这种方案在访问量大的时候就会失效,这是不安全的,由此我们需要使用数据库提供的函数来解决这个问题, LAST_INSERT_ID()

我们来修改映射文件中的sql语句,

    <insert id="insert_getId" parameterType="cn.mybatis.pojo.User">
        <selectKey keyProperty="id" resultType="int" order="AFTER">
          select LAST_INSERT_ID()
        </selectKey>
        insert into user(username,birthday,sex,address) VALUE(#{username},#{birthday},#{sex},#{address});
    </insert>

keyProperty : 返回的主键存储在pojo中的哪个属性
resultType : 返回数据类型
order:selectKey的执行顺序,是相对与insert语句来说,由于mysql的自增原理执行完insert语句之后才将主键生成,所以这里selectKey的执行顺序为after

测试代码

 @Test
    public void insert() throws IOException {
        String resources = "mybatisConfig.xml";

        //Resources.getResourceAsStream(resources)  这句代码的意思就是查找classpath目录下的mybatisConfig.xml文件
        InputStream stream = Resources.getResourceAsStream(resources);
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //获取工厂
        SqlSessionFactory factory = builder.build(stream);
        //获取session
        SqlSession session = factory.openSession();

        //test.findUserById      映射文件中的名称空间.查询语句的id    目的是确定使用具体的哪一条sql
        User user = new User();

        user.setUsername("李白");
        user.setSex("男");
        user.setAddress("广东省");
        user.setBirthday(new Date());

        System.out.println("执行sql前的user对象user="+user);

        session.insert("test.insert_getId", user);

        System.out.println("执行sql后的user对象user="+user);
        session.commit();
        session.close();
    }

执行结果如下
这里写图片描述

我们可以发现,执行完sql后对象的id给赋值了

如果主键id 是uuid生成的,那么又要重写一条sql

<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey resultType="java.lang.String" order="BEFORE"
keyProperty="id">
select uuid()
</selectKey>
insert into user(id,username,birthday,sex,address) 
         values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>

这里使用uuid 的原因是, 执行sql前,id都没生成,所以我们需要提前生成id,

删改我就不演示了,重复造轮子,感觉没必要了.

嗯,我们来抽取一下代码,

  SqlSessionFactory factory;
    InputStream stream;
    @Before
    public void createSqlSessionFactory(){
        String resources = "mybatisConfig.xml";

        //Resources.getResourceAsStream(resources)  这句代码的意思就是查找classpath目录下的mybatisConfig.xml文件
        try {
            stream = Resources.getResourceAsStream(resources);
        }catch (Exception e){

        }

        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //获取工厂
        factory = builder.build(stream);
    }

看完这段代码估计就有小朋友有疑问了,@Before 是干啥的?? 其实在方法上增加@Before主键后,哪个方法就会在执行@Test注解方法前执行一遍,嗯,是的没错..相信我,
抽取完这段代码后,我们就可以不用再获取factory了,直接用成员变量就可以了…..

现在又有一个需求,其实就是模糊查询…给一个用户名,模糊查询所有,,,,,
我们先在映射文件里写一条sql语句

    <select id="mohuchaxun" resultType="cn.mybatis.pojo.User"       parameterType="java.lang.String">
        select * from user where username like '%${value}%'
    </select>

这里需要注意的是 这里不能使用 #{} 了,因为#{} 只是单词的占位符,并不能字符串拼接,, 还需要注意的是 ${}的值只能使用value,

然后写测试代码

@Test
    public void likeFind() throws IOException {
        //获取session
        SqlSession session = factory.openSession();

        List<User> users = session.selectList("test.mohuchaxun", "小");

        System.out.println("user="+users);
        session.close();
    }

测试运行结果如下
这里写图片描述

学到这里,我们的myBatis就已经是入门了,,,,,,,,

然后这里做个小结

#{}和${}
#{}其实是占位符的意思,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换.#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
${}的意思的拼接符的意思,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。
如果:
输入参数的数据类型是简单的数据类型,
#{} 里面的名称随意写 
而${} 里面的参数名称必须是value.
parameterType和resultType
parameterType指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼接在sql中,

resultType指定输出参数类型,mybatis 通过sql记录数据映射为resultType指定类型的对象,
如果返回的是集合,只需要指定集合里面的元素的数据类型即可,
selectOne和selectList
其实selectOne的底层就是使用selectList实现的,只不过selectOne做了一些封装,如果使用selectOne查询多条记录则抛出异常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)

selectList可以查询一条或多条记录。

我们来看看selectOne底层实现原理…….
这里写图片描述

顺便说一下好了,selectList的底层使用jdbc来做的,有兴趣的小伙伴可以翻翻,

嗯,看到这里应该有些小伙伴已经不耐烦了,那个,,,别打我,因为现在重点才开始


mybatis Mapper动态代理方式

学习代理方式之前,我们需要了解一些规则,因为我们需要根据这种规则才能使用功能,

Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
遵循规范如下:

  • namespace 需要与接口的类路径相同,
  • Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
  • Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
  • Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

Mapper.xml(映射文件)

定义mapper映射文件UserMapper.xml(内容同User.xml),需要修改namespace的值为 UserMapper接口路径。将UserMapper.xml放在classpath 下mapper目录 下。

接口定义

  • Mapper接口方法名和Mapper.xml中定义的statement的id相同
  • Mapper接口方法的输入参数类型和mapper.xml中定义的statement的parameterType的类型相同
    -Mapper接口方法的输出参数类型和mapper.xml中定义的statement的resultType的类型相同

创建一个UserMapper文件,拷贝之前的User.xml就行,放在config目录下的mapper目录下,
还需要在核心配置文件里引入一下mapper文件,,

<mappers>
        <!--<mapper resource="sqlmap/User.xml"></mapper>-->
        <mapper resource="mapper/UserMapper.xml"></mapper>
    </mappers>

然后创建一个dao接口,遵守以上规则,,,

package cn.mybatis.dao;

import cn.mybatis.pojo.User;

import java.util.List;

public interface IUserDao {
//这里就写一个模糊查询的例子了
    public List<User> mohuchaxun(String name);
}

写到这里,还需要改userMapper文件,因为规范要求namespace 跟 mapper接口的类路径一致,

<mapper namespace="cn.mybatis.dao.IUserDao">
    xxxxxx
</mapper>

测试代码如下

    @Test
    public void test_fun02(){
        //获取session
        SqlSession session = factory.openSession();
        IUserDao mapper = session.getMapper(IUserDao.class);

        List<User> user = mapper.mohuchaxun("小");

        System.out.println("user="+user);
    }

运行结果
这里写图片描述

代码就讲到这里,其实也没什么难度,,只要遵守规则写就好了,,,,
这里写图片描述

代码就讲的差不多了,接下来就讲讲SqlMapConfig.xml核心配置文件把,,,


配置内容如下
SqlMapConfig.xml中配置的内容和顺序如下:必须按照顺序配置

properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)

那个,别担心,我们不是所有都需要会,,只需要会几个重点配置就行了……说实话,我第一次看到有这么多配置的时候都吓了一跳,,,,
需要了解的配置如下

  • properties(属性)
  • typeAliases(类型别名)
  • mappers(映射器)

需要注意的是 必须按照顺序配置 必须按照顺序配置 必须按照顺序配置,

首先我们来试试properties
  • 在resource目录下创建xxx.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=1039191520

然后在核心配置文件里引用一下

  <properties resource="datasource.properties"></properties>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

运行测试

这里写图片描述

typeAliases(类型别名)

之前我们就讲了myBatis支持的一些类型别名,, 这里我就大致的讲一些怎么自定义类型别名把……

配置核心配置文件,记住配置顺序,不能放在properties 配置前面,

  <properties resource="datasource.properties"></properties>

    <typeAliases>
        <!--<typeAlias alias="user" type="cn.mybatis.pojo.User"/>   &lt;!&ndash;单个别名定义&ndash;&gt;-->

        <package name="cn.mybatis.pojo" />   <!--多个别名定义,扫描整个包下的类,别名为类名(大小写不敏感)-->
    </typeAliases>

然后我们修改代码,使用别名,就改模糊查询的例子了,因为突出大小写不敏感,所以嘿嘿嘿,你懂的

    <select id="mohuchaxun" resultType="uSeR" parameterType="java.lang.String">
        select * from user where username like '%${value}%'
    </select>

代码测试
这里写图片描述

最后来讲讲mappers(映射器)

相信大家已经有所感悟了把,我们写mapper一直都是一个个的引入的,这样要是在开发中可是很头疼的一件事,mybatis提供了解决方案
此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

 <mappers>
        <!--<mapper resource="sqlmap/User.xml"></mapper>-->
        <!--<mapper resource="mapper/UserMapper.xml"></mapper>-->
        <package name="cn.mybatis.dao"></package>
    </mappers>

我们需要重新修改一下目录结构

这里写图片描述

然后运行测试
这里写图片描述

myBatis入门就讲到这里,下次讲myBatis拼接sql等等…..撤退啦

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值