SSM框架11 Mybatis基础知识、原理和实现、CRUD实例、ResultMap、Log4j日志

一、Mybatis简介

Mybatis文档网站

Mybatis:一个持久层框架,支持定制化SQL、存储过程、高级映射,几乎避免了JDBC代码和手动参数以及获取结果集。

数据持久化:
持久化就是将程序的数据在持久状态和瞬时状态转化的过程
内存:断电就丢失
数据库(JDBC):io文件持久化

二、Mybatis实例

思路:搭建环境->导入Mybatis->编写代码->测试
1、编写mybatis配置文件

<?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">

<!-- Mybatis核心配置文件 configuration标签-->
<configuration>
    <properties resource="db.properties">
        <property name="username" value="username"/>
        <property name="password" value="password"/>
    </properties>
    <!-- 事务   、  数据源、  映射文件 -->
    <!-- 配置开发环境
        default:默认的环境
    -->
    <!--    给类配置别名 typeAliases标签-->
    <typeAliases>
        <!--        type为原名 alias为别名-->
        <typeAlias type="pojo.User" alias="User"/>
    </typeAliases>
<!--    如果pojo下类比较多,可以指定包名,在包下自动搜索javabean
        扫描到的实体类,默认别名就是类的类名,首字母小写
    -->
    <typeAliases>
        <package name="pojo"/>
    </typeAliases>
    <!--    配置环境 environments标签-->
    <!--    environments默认名随意  但是要和environment的id值相同-->
<!--    通过default来切环境 mybatis 默认事务管理器是jdbc 连接池是pooled-->
    <environments default="dev">
        <!--id值就是默认的环境  -->
        <environment id="dev">
            <!-- 配置事务管理 选择jdbc事务-->
            <transactionManager type="JDBC"></transactionManager>

            <!-- 配置数据源 type:池化的-->
            <dataSource type="pooled">
                <!--                配置链接数据库的基本信息-->
                <!--                配置驱动包-->
                <property name="driver"
                          value="com.mysql.jdbc.Driver"/>
                <!--                配置sql服务器地址-->
                <property name="url"
                          value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <!--                配置数据库账号-->
                <property name="username" value="${username}"/>
                <!--                配置数据库密码-->
                <property name="password" value="${password}"/>

            </dataSource>
        </environment>
    </environments>
    <!--    配置映射 每一个mapper都需要再mybatis配置文件中注册-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>

</configuration>

2、建立工具类

package until;

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 javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;

public class MybatisUntil {
    private static SqlSessionFactory sqlSessionFactory;
    private static SqlSession sqlSession;
    static {
        try {
//          mybatis第一步:获取SqlSessionFactory对象
            InputStream is = Resources.getResourceAsStream("mybatis/mybatis.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
//      有了获取SqlSessionFactory对象,自然可以创建出SqlSession的实例
//      SqlSession完全包含了执行sql的所有方法
    }

    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}


3、编写业务代码
①实体类
②Dao接口

package dao;

import pojo.User;

import java.util.List;

public interface UserDao {
    List<User> getUserList();
}

③接口实现类由原本的UserDaoImpl转变为UserMapper映射文件

<?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">
<!--映射约束-->
<!-- 映射文件,存放SQL
	namespace绑定一个对应的DAO接口
-->
<mapper namespace="dao.UserDao">

    <!-- select查询用户表的所有数据
        id对应接口方法名 唯一标识 用id才可以知道进行什么crud操作
    resultType要把查询到的结果封装给哪个实体对象
    resultMap返回多个
    -->
    <select id="getUserList"
            resultType="pojo.User">
        select * from mybatis.user
    </select>


</mapper>

三、实现增删改查

用Mapper比用User更灵活

参数的传递:#{}
Map传递参数,直接在sql中取出key
对象传递参数,直接在sql中去对象的属性
只有一个基本类型参数的情况下,直接在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">
<!--映射约束-->
<!-- 映射文件,存放SQL
	namespace绑定一个对应的DAO接口
-->
<mapper namespace="dao.UserDao">

    <!-- select查询用户表的所有数据
        id对应接口方法名 唯一标识 用id才可以知道进行什么crud操作
    resultType要把查询到的结果封装给哪个实体对象 返回多个
    resultMap返回1个
    parameterType 参数类型
    -->
    <select id="getUserList"
            resultType="pojo.User">
        select * from mybatis.user;
    </select>

    <select id="selectuser"
            resultType="pojo.User" parameterType="int">
        select * from mybatis.user where id=#{id};
    </select>

    <insert id="adduser" parameterType="pojo.User">
        insert into mybatis.user (id,name,pwd) values (null,#{name},#{pwd});
    </insert>

    <delete id="deleteuser" parameterType="String">
        delete from mybatis.user where name=#{name};
    </delete>

    <update id="updateuser" parameterType="pojo.User">
        update mybatis.user set pwd=#{pwd} where name=#{name};
    </update>

    <select id="SelectUserLike" parameterType="String" resultType="pojo.User">
        select * from mybatis.user where name like #{value};
    </select>



</mapper>
package dao;

import pojo.User;

import java.util.List;

public interface UserDao {
    List<User> getUserList();

    User selectuser(int id);

    int adduser(User user);

    int deleteuser(String name);

    int updateuser(User user);

    User SelectUserLike(String name);
}


import dao.UserDao;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import pojo.User;
import until.MybatisUntil;

import java.util.List;

public class UserTest {

    @Test
    public void test1(){
//      第一步 获取sqlsession对象
        SqlSession sqlSession = MybatisUntil.getSqlSession();
//      方式一 getMapper
        UserDao userDao =sqlSession.getMapper(UserDao.class);
        List<User> userList = userDao.getUserList();
        System.out.println(userList.toString());

//      方式二
        List<User> list = sqlSession.selectList("dao.UserDao.getUserList");
        System.out.println(list.toString());

        sqlSession.close();
    }
    @Test
    public void test2(){
        SqlSession sqlSession = MybatisUntil.getSqlSession();
        User user = sqlSession.selectOne("dao.UserDao.selectuser",1);
        System.out.println(user.toString());
        sqlSession.close();
    }

    @Test
    public void test3(){
        SqlSession sqlSession=MybatisUntil.getSqlSession();
//        增删改 需要提交事务
        int res = sqlSession.insert("dao.UserDao.adduser", new User(1, "liuli", "123"));
        sqlSession.commit();
        if (res>0){
            System.out.println("插入成功!");
        }
        sqlSession.close();
    }

    @Test
    public void test4(){
        SqlSession sqlSession = MybatisUntil.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        int res = mapper.deleteuser("zhuqi");
        if (res>0){
            System.out.println("删除成功");
        }
        sqlSession.commit();
        sqlSession.close();
    }

    @Test
    public void test5(){
        SqlSession sqlSession = MybatisUntil.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        int res = mapper.updateuser(new User(1, "zhangsan", "987"));
        if (res>0){
            System.out.println("修改成功!");
        }
        sqlSession.commit();
        sqlSession.close();
    }

//    模糊查询
    @Test
    public void test6(){
        SqlSession sqlSession = MybatisUntil.getSqlSession();
        List<User> list = sqlSession.selectList("dao.UserDao.SelectUserLike", "z%");
        System.out.println(list.toString());
        sqlSession.close();
    }
}


四、属性优化

在mybatis配置文件 优化属性

<properties resource="db.properties">
        <property name="username" value="username"/>
        <property name="password" value="password"/>
    </properties>

五、设置别名

<!-- 事务   、  数据源、  映射文件 -->
    <!--    第一种、给类配置别名 typeAliases标签-->
    <typeAliases>
        <!--        type为原名 alias为别名-->
        <typeAlias type="pojo.User" alias="User"/>
    </typeAliases>
<!--    第二种、如果pojo下类比较多,可以指定包名,在包下自动搜索javabean
        扫描到的实体类,默认别名就是类的类名,首字母小写
    -->
    <typeAliases>
        <package name="pojo"/>
    </typeAliases>

第二种方法类名要改变,则需要再实体类中通过注释改变别名

@Alias("user")
public class User {}

基本类型默认类型加_
不加 则是封装类型
例如int 默认别名是_int

六、配置设置

设置名				描述												有效值			默认值
cacheEnabled		全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。	true | false	true
lazyLoadingEnabled	延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。												true | false	false
aggressiveLazyLoading	开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)true | false	false (在 3.4.1 及之前的版本中默认为 true)
multipleResultSetsEnabled	是否允许单个语句返回多结果集。					true | false	true
useColumnLabel	使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。																	true | false	true
useGeneratedKeys	允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。					true | false	False
autoMappingBehavior	指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。	NONE, PARTIAL, FULL	PARTIAL
autoMappingUnknownColumnBehavior	指定发现自动映射目标未知列(或未知属性类型)的行为。
NONE: 不做任何反应
WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARNFAILING: 映射失败 (抛出 SqlSessionException)
NONE, WARNING, FAILING	NONE
defaultExecutorType	配置默认的执行器。		SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); 	BATCH 执行器不仅重用语句还会执行批量更新。		SIMPLE REUSE BATCH	SIMPLE
defaultStatementTimeout	设置超时时间,它决定数据库驱动等待数据库响应的秒数。	任意正整数		未设置 (null)
defaultFetchSize	为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。	任意正整数	未设置 (null)
defaultResultSetType	指定语句默认的滚动策略。(新增于 3.5.2FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置)	未设置 (null)
safeRowBoundsEnabled	是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 falsetrue | false	False
safeResultHandlerEnabled	是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 falsetrue | false	True
mapUnderscoreToCamelCase	是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。	true | false	False
localCacheScope	MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。	SESSION | STATEMENT	SESSION
jdbcTypeForNull	当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULLVARCHAROTHERJdbcType 常量,常用值:NULLVARCHAROTHEROTHER
lazyLoadTriggerMethods	指定对象的哪些方法触发一次延迟加载。	用逗号分隔的方法列表。	equals,clone,hashCode,toString
defaultScriptingLanguage	指定动态 SQL 生成使用的默认脚本语言。	一个类型别名或全限定类名。	org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler	指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5)	一个类型别名或全限定类名。	org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls	指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet()null 值进行初始化时比较有用。注意基本类型(intboolean 等)是不能设置成 null 的。	true | false	false
returnInstanceForEmptyRow	当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2true | false	false
logPrefix	指定 MyBatis 增加到日志名称的前缀。	任何字符串	未设置
logImpl	指定 MyBatis 所用日志的具体实现,未指定时将自动查找。	SLF4J | LOG4J3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING	未设置
proxyFactory	指定 Mybatis 创建可延迟加载对象所用到的代理工具。	CGLIB3.5.10 起废弃) | JAVASSIST	JAVASSISTMyBatis 3.3 以上)
vfsImpl	指定 VFS 的实现	自定义 VFS 的实现的类全限定名,以逗号分隔。	未设置
useActualParamName	允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1true | false	true
configurationFactory	指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)	一个类型别名或完全限定类名。	未设置
shrinkWhitespacesInSql	从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5)	true | false	false
defaultSqlProviderType	指定一个拥有 provider 方法的 sql provider 类 (新增于 3.5.6. 这个类适用于指定 sql provider 注解上的type(或 value) 属性(当这些属性在注解中被忽略时)。 (e.g. @SelectProvider)	类型别名或者全限定名	未设置
nullableOnForEach	为 'foreach' 标签的 'nullable' 属性指定默认值。(新增于 3.5.9true | false	false
argNameBasedConstructorAutoMapping	当应用构造器自动映射时,参数名称被用来搜索要映射的列,而不再依赖列的顺序。(新增于 3.5.10true | false	false
<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="aggressiveLazyLoading" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="safeResultHandlerEnabled" value="true"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
  <setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/>
  <setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumTypeHandler"/>
  <setting name="callSettersOnNulls" value="false"/>
  <setting name="returnInstanceForEmptyRow" value="false"/>
  <setting name="logPrefix" value="exampleLogPreFix_"/>
  <setting name="logImpl" value="SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING"/>
  <setting name="proxyFactory" value="CGLIB | JAVASSIST"/>
  <setting name="vfsImpl" value="org.mybatis.example.YourselfVfsImpl"/>
  <setting name="useActualParamName" value="true"/>
  <setting name="configurationFactory" value="org.mybatis.example.ConfigurationFactory"/>
</settings>

七、注册映射器

在mybatis配置文件注册绑定mapper文件
方式一:

    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>

方式二:

    <mappers>
<!--class  要求接口名和xml文件名一样 接口和配置文件在一个包下-->
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>

方式三:
扫描包进行注册

    <mappers>
        <package name="dao"/>
    </mappers>

八、生命周期和作用域

SqlSessionFactoryBuild:
目的就是为了创建SqlSessionFactory,局部变量
SqlSessionFactory:
类似数据库连接池,用来存会话,创造会话
作用域是应用作用域,一旦创建运行期间一直存在,用单例模式和静态单例模式
SqlSession:
连接到数据库的一个请求,作用域是请求或方法作用域,不能被共享
用完之后需要赶紧关闭,否则资源占用
一个mapper就是一个业务

九、ResultMap结果映射

出现属性名和字段名不一样的情况
1、可以通过在sql中使用as 将字段和属性对应上

    <select id="getUserList"
            resultType="pojo.User">
        select id,name,pwd as password from mybatis.user;
    </select>

2、用resultMap解决
哪个属性不一样,映射哪个

<!--    结果集映射-->
    <resultMap id="UserMap" type="pojo.User">
<!--        column代表数据库的字段 property代表实体类的属性-->
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="pwd" property="password"/>
    </resultMap>
    <select id="getUserList"
            resultMap="UserMap">

        select id,name,pwd from mybatis.user;
    </select>

十、日志

日志最好的用处就是数据库操作出现了异常的时候,进行排除
类似于java的sout和debug
在settings标签中
通过logImpl指定Mybatis所用日志的实现形式,未指定时则自动查找
SLF4J
LOG4J(3.5.9 起废弃)
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING 标准日志输出
NO_LOGGING

<settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

LOG4J:需要先导包

log4j
properties配置文件

#ERROR:日志级别
#debug,error,Console:日志输出类型
log4j.rootLogger=ERROR,debug,error,Console,File

#打印日志
log4j.appender.Console = org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target = System.out
log4j.appender.Console.ImmediateFlush = true
log4j.appender.Console.Threshold=debug
log4j.appender.Console.layout = org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern = [%p][%-d{yyyy-MM-dd HH:mm:ss}] %m %n

#输出到日志文件
log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.debug.layout=org.apache.log4j.PatternLayout 
log4j.appender.debug.layout.ConversionPattern=[%-5p] [%d{HH:mm:ss}] %c - %m%n 
log4j.appender.debug.datePattern='.'yyyy-MM-dd
#Threshold: 阀值; 级别大于等于该值时才输出
log4j.appender.debug.Threshold=INFO 
log4j.appender.debug.append=true 
log4j.appender.debug.File=logs/web.info.log

#输出到日志文件
log4j.appender.error=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.error.layout=org.apache.log4j.PatternLayout 
log4j.appender.error.layout.ConversionPattern=[%-5p] [%d{HH:mm:ss}]%l - %m%n 
log4j.appender.error.datePattern='.'yyyy-MM-dd
#Threshold: 阀值; 级别大于等于该值时才输出
log4j.appender.error.Threshold=ERROR 
log4j.appender.error.append=true 
log4j.appender.error.File=logs/web.error.log

#输出到日志文件
log4j.appender.File = org.apache.log4j.DailyRollingFileAppender
log4j.appender.File.layout = org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern =[%-5p] [%d{HH:mm:ss}]%l - %m%n
#Threshold: 阀值; 级别大于等于该值时才输出
log4j.appender.File.Threshold=ERROR
log4j.appender.File.append=true
log4j.appender.File.File = logs/web.file.log

#包级别设置日志级别,rootLogger设置失效
log4j.logger.com.web=INFO
log4j.logger.org.apache.commons.beanutils=ERROR
log4j.logger.org.apache.http=ERROR

最常用的标准log4j.properties

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/xu.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
//    在需要使用log4j的类中,导入org.apache.log4j.Logger包 getLogger方法获取Logger 参数为当前类
    static Logger logger= Logger.getLogger(UserTest.class);
    @Test
    public void testlog4j(){
        logger.info("hello1");
        logger.debug("hello2");
        logger.error("hello3");
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值