Java之路(十):JDBC及DAO

 JDBC学习

1.获取JDBC的连接对象

1.加载注册驱动

Class.forName("com.mysql.jdbc.Driver");

当执行这一步时,会把Driver这份字节码加载进JVM,然后执行该字节码的静态代码块

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

2.获取连接

通过DriverManager类的getConnection方法获取连接对象

    @Test
    public void test1() throws Exception {
        Class.forName("com.mysql.cj.jdbc.Driver");

        Connection conn =
                DriverManager.getConnection("jdbc:mysql:///stu", "root", "password");
        Thread.sleep(5000);
    }

连接过后出现这样的错误:

java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more 

找了资料之后发现意思是 mysqljdbc . driver被弃用了新的驱动类是“com.mysql.cjdbc.driver”。驱动程序通过SPI自动注册,而手动加载类通常是不必要的,只要将com.mysql.jdbc.Driver 改为com.mysql.cj.jdbc.Driver 即可。

另一个异常是系统时区出了问题,只要在URL之后加上"?serverTimezone=GMT%2B8"就可以了( GMT%2B8代表东八区)

或者在Navicat的命令行中输入:set global time_zone='+8:00';

另外,在mysql的my.ini文件中的[mysqld]中加入default-time_zone = '+8:00'语句,再重启MySQL服务就可以了。

在MySQL命令行再次查看系统时间:show variables like '%time_zone%'; 和 select now();

然后我们用mysql命令行的show processlist 检测连接。

3.操作JDBC

  1. 加载注册驱动
  2. 获取连接对象
  3. 创建语句对象
  4. 执行SQL语句
  5. 释放资源
    @Test
    public void test() throws Exception {
        String sql = "CREATE TABLE test (id bigint(10) PRIMARY KEY AUTO_INCREMENT, name varchar(20), age int(2))";
        //1.加载注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获取连接对象
        Connection conn =
                DriverManager.getConnection("jdbc:mysql:///stu", "root", "password");
        //3.创建语句对象
        Statement st = conn.createStatement();
        //4.执行SQL语句
        int row = st.executeUpdate(sql);
        st.close();;
        conn.close();
        System.out.println(row);
    }

运行后发现数据库中就会多出一张表test

4.异常处理

@Test
    public void testHandleException() {
        String sql = "CREATE TABLE test (id bigint(10) PRIMARY KEY AUTO_INCREMENT, name varchar(20), age int(2))";

        //声明需要关闭的资源
        Connection conn = null;
        Statement st = null;
        try {
            //1.加载注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取连接对象
            conn = DriverManager.getConnection("jdbc:mysql:///stu","root","password");
            //3.创建语句对象
            st = conn.createStatement();
            //4.执行SQL语句
            st.executeUpdate(sql);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //5.释放资源
            try {
                if (st != null) {
                    st.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (conn != null) {
                        conn.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
    }

5.DML操作

仍然使用executeUpdate,没什么好说的,再写一遍熟练一下

@Test
    public  void testInsert() throws Exception{
        String sql = "INSERT INTO test (name,age) values('Bobbui',19)";
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection conn = DriverManager.getConnection("jdbc:mysql:///stu","root","password");
        Statement st = conn.createStatement();
        int row = st.executeUpdate(sql);
        System.out.println(row);
        st.close();;
        conn.close();
    }

    @Test
    public  void testUpdate() throws Exception{
        String sql = "DELETE FROM test WHERE name='Bobbui'";
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection conn = DriverManager.getConnection("jdbc:mysql:///stu","root","password");
        Statement st = conn.createStatement();
        st.executeUpdate(sql);
        st.close();
        conn.close();
    }

6.DQL操作

有个地方比较有意思,在Java中,只有JDBC和JPA中的索引是从1开始的。

ResultSet可以用迭代器取出对象:

        List<String> list = Arrays.asList("A", "B", "C", "D");
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
public class DQLTest {

    @Test
    public void test() throws Exception {
        String sql = "SELECT score.Student_id 学号,Student_name 姓名,SUM(Grade) 总分\n" +
                "FROM score,student\n" +
                "WHERE (score.Student_id=student.Student_id) GROUP BY student.Student_name";
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection conn = DriverManager.getConnection("jdbc:mysql:///studentinfo", "root", "password");
        Statement st = conn.createStatement();
        ResultSet rs = st.executeQuery(sql);
        //处理结果集
        while (rs.next()) {
            String stuNum = rs.getString("学号");
            String stuName = rs.getString("姓名");
            int stuScore = rs.getInt("总分");
            System.out.println(stuNum + "," + stuName + "," + stuScore);
        }
        //关闭资源
        rs.close();
        st.close();
        conn.close();
    }
}

得到的结果为:

Java运行结果

 

MySQL运行结果

7.DAO介绍

DAO(Data Access Object):数据存取对象,位于业务逻辑和持久化数据之间,能够实现对持久化数据的访问

DAO在实体类与数据库之间起着转换器的作用,能够把实体类转换为数据库中的记录

DAO模式是作用:

  1. 隔离业务逻辑代码和数据访问代码
  2. 隔离不同数据库的实现

DAO模式的组成部分:

  1. DAO接口
  2. DAO实现类
  3. 实体类
  4. 数据库连接和关闭工具类

分层开发:

一种化大为小,分而治之的软件开发方法

分层的特点:

     1.每一层都有自己的职责

     2.上层不用关心下次的实现细节,上层通过下层提供的对外接口来使用其功能

     3.上一层调用下一层的功能,下一层不能调用上一层的功能

分层开发的好处:

     1.各层专注于自己功能的实现,便于提高质量

     2.便于分工协作,提高开发效率

     3.便于代码复用

     4.便于程序扩展

分层原则:

    封装性原则

       每个层次向外公开接口,但是隐藏内部细节

    顺序访问原则

       下一层为上一层服务,但不使用上层的服务

分层结构中,不同层之间通过实体类传输数据

8.DAO设计

DAO组件

  1. DAO接口
  2. DAO实现类
  3. DAO测试类

包名规范

package 域名倒写.模块名称.组件名称

package com.Bryan.smis.domain       存储所有的domain类

package com.Bryan.smis.dao             存储所有的DAO接口

package com.Bryan.smis.dao.impl      存储所有的DAO接口的实现类

package com.Bryan.smis.test              存储DAO组件的测试类

 Student类:

public class Student {

    private String id;
    private String name;
    private Integer age;

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", age=" + age + '}';
    }
}

 IStudentDAO接口:

//封装Student对象的CRUD操作
public interface IStudentDAO {

    /**
     * 保存操作
     * @param stu 学生对象
     */
    void save(Student stu);

    /**
     * 删除操作
     * @param id 被删除学生的主键值
     */
    void delete(String id);

    /**
     * 更新操作
     * @param id 被查询学生的主键值
     * @param newStu 如果id存在,返回该学生对象,否则返回null
     */
    void update(String id,Student newStu);

    /**
     * 查询指定Id的学生对象
     * @param id 被查询学生的主键值
     * @return 如果id存在,返回该学生对象,否则返回null
     */
    Student get(String id);

    /**
     * 查询并返回所有学生对象
     * @return 如果结果集为空,则返回null
     */
    List<Student> listAll();
}

StudentDAOImpl类:

public class StudentDAOImpl implements IStudentDAO {

    public void save(Student stu) {
        //String sql = "INSERT INTO student (Student_id,sex,Student_name,age) VALUES ('20178013','M','Cherry',19)";
        StringBuilder sb = new StringBuilder(80);
        sb.append("INSERT INTO student (Student_id,sex,Student_name,age) VALUES ('");
        sb.append(stu.getId());
        sb.append("','");
        sb.append(stu.getSex());
        sb.append("','");
        sb.append(stu.getName());
        sb.append("',");
        sb.append(stu.getAge());
        sb.append(")");
        //System.out.println(sb);

        //声明资源
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            //1.加载注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取连接对象
            conn = DriverManager.getConnection("jdbc:mysql:///studentinfo", "root", "password");
            //3.创建语句对象
            st = conn.createStatement();
            //4.执行SQL
            st.executeUpdate(sb.toString());

        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            try {
                if (st != null) {
                    st.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (conn != null) {
                        conn.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //DELETE FROM student WHERE Student_id = '20178013'
    public void delete(String id) {
        StringBuilder sb = new StringBuilder(80);
        sb.append("DELETE FROM student WHERE Student_id = '");
        sb.append(id);
        sb.append("'");

        //System.out.println(sb);

        //声明资源
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            //1.加载注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取连接对象
            conn = DriverManager.getConnection("jdbc:mysql:///studentinfo", "root", "password");
            //3.创建语句对象
            st = conn.createStatement();
            //4.执行SQL
            st.executeUpdate(sb.toString());

        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            try {
                if (st != null) {
                    st.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (conn != null) {
                        conn.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //UPDATE student SET Student_name = 'XXX',age = 12 WHERE Student_id = '20178013'
    public void update(String id, Student newStu) {
        StringBuilder sb = new StringBuilder(80);
        sb.append("UPDATE student SET Student_name = '");
        sb.append(newStu.getName());
        sb.append("',age = ");
        sb.append(newStu.getAge());
        sb.append(" WHERE Student_id = '");
        sb.append(id);
        sb.append("'");

        //System.out.println(sb);

        //声明资源
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            //1.加载注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取连接对象
            conn = DriverManager.getConnection("jdbc:mysql:///studentinfo", "root", "password");
            //3.创建语句对象
            st = conn.createStatement();
            //4.执行SQL
            st.executeUpdate(sb.toString());

        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            try {
                if (st != null) {
                    st.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (conn != null) {
                        conn.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public Student get(String id) {

        String sql = "SELECT * FROM student WHERE Student_id = " + id;

        //声明资源
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            //1.加载注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取连接对象
            conn = DriverManager.getConnection("jdbc:mysql:///studentinfo", "root", "password");
            //3.创建语句对象
            st = conn.createStatement();
            //4.执行SQL
            rs = st.executeQuery(sql);
            //5.处理结果集
            //--------------------------------
            if (rs.next()) {
                Student stu = new Student();
                //获取当前光标所在的行的列值,并设置到stu中
                stu.setAge(rs.getInt("age"));
                stu.setId(rs.getString("Student_id"));
                stu.setName(rs.getString("Student_name"));
                return stu;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (st != null) {
                        st.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (conn != null) {
                            conn.close();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }

    public List<Student> listAll() {
        List<Student> list = new ArrayList<>();

        String sql = "SELECT * FROM student";

        //声明资源
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            //1.加载注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取连接对象
            conn = DriverManager.getConnection("jdbc:mysql:///studentinfo", "root", "password");
            //3.创建语句对象
            st = conn.createStatement();
            //4.执行SQL
            rs = st.executeQuery(sql);
            //5.处理结果集
            //--------------------------------
            while (rs.next()) {
                Student stu = new Student();
                //获取当前光标所在的行的列值,并设置到stu中
                stu.setAge(rs.getInt("age"));
                stu.setId(rs.getString("Student_id"));
                stu.setName(rs.getString("Student_name"));
                list.add(stu);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (st != null) {
                        st.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (conn != null) {
                            conn.close();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return list;
    }
}

StudentDAOTest类:

public class StudentDAOTest {

    //依赖DAO对象
    private IStudentDAO dao = new StudentDAOImpl();

    @Test
    public void testSave() {
        Student stu = new Student();
        stu.setName("Dans");
        stu.setAge(19);
        dao.save(stu);
    }

    @Test
    public void testDelete() {

        dao.delete("20178013");
    }

    @Test
    public void testUpdate() {
        Student stu = new Student();
        stu.setName("lrh");
        stu.setSex("F");
        stu.setAge(18);
        dao.update("20178013",stu);
    }

    @Test
    public void testGet() {
        Student stu = dao.get("20010103");
        System.out.println(stu);
    }

    @Test
    public void testListAll() {
        List<Student> stus = dao.listAll();
        for (Student stu : stus) {
            System.out.println(stu);
        }
    }
}

到现在为止,已经完成了DAO模式的封装,但是我们发现在StudentDAOImpl中重复代码太多,不符合软件代码的高内聚低耦合原则,下面我们进行重构。

9.重构设计

       重构(Refactoring)就是通过调整程序代码,改善软件的质量、性能,使其程序的设计模式和架构更加合理,提高软件的扩展性和维护性。

抽取出JDBCUtil类

我们发现连接数据库的几行代码每次都要重写一遍,因此考虑将其改成成员变量,但是当DAO太多的时候,这样也会造成代码冗余,因此我们考虑封装成一个Util类。

/**
 * JDBC工具类
 */
public class JdbcUtil {

    //连接数据库的四要素
    public static String driverClassName = "com.mysql.cj.jdbc.Driver";
    public static String url = "jdbc:mysql:///studentinfo";
    public static String userName = "root";
    public static String password = "password";
}

//---------------------------------------------------------
//1.加载注册驱动
Class.forName(JdbcUtil.driverClassName);
//2.获取连接对象
conn = DriverManager.getConnection(JdbcUtil.url, JdbcUtil.userName, JdbcUtil.password);

但是考虑到封装的Util字段为公有,安全性不高。因此我们考虑将创建Connection对象也封装到Util类。然而,每次都需要重新注册驱动,没有必要,因此我们在JdbcUtil中的静态代码块中进行驱动注册,并将关闭资源进行封装。

/**
 * JDBC工具类
 */
public class JdbcUtil {

    //连接数据库的四要素
    private static String driverClassName = "com.mysql.cj.jdbc.Driver";
    private static String url = "jdbc:mysql:///studentinfo";
    private static String userName = "root";
    private static String password = "password";

    //静态代码块,当JdbcUtil加载进JVM就开始执行
    static {
        try {
            Class.forName(driverClassName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //创建并返回一个Connection对象
    public static Connection getConnection() {
        try {
            return DriverManager.getConnection(url, userName, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //释放资源
    public static void close(Connection conn, Statement st, ResultSet rs) {
        try {
            if (st != null) {
                st.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

封装完的StudentDAOImpl类如下:

public class StudentDAOImpl implements IStudentDAO {

    public void save(Student stu) {
        //String sql = "INSERT INTO student (Student_id,sex,Student_name,age) VALUES ('20178013','M','Cherry',19)";
        StringBuilder sb = new StringBuilder(80);
        sb.append("INSERT INTO student (Student_id,sex,Student_name,age) VALUES ('");
        sb.append(stu.getId());
        sb.append("','");
        sb.append(stu.getSex());
        sb.append("','");
        sb.append(stu.getName());
        sb.append("',");
        sb.append(stu.getAge());
        sb.append(")");
        //System.out.println(sb);

        //声明资源
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtil.getConnection();
            //3.创建语句对象
            st = conn.createStatement();
            //4.执行SQL
            st.executeUpdate(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, st, null);
        }
    }

    //DELETE FROM student WHERE Student_id = '20178013'
    public void delete(String id) {
        StringBuilder sb = new StringBuilder(80);
        sb.append("DELETE FROM student WHERE Student_id = '");
        sb.append(id);
        sb.append("'");
        //声明资源
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            //3.创建语句对象
            st = conn.createStatement();
            //4.执行SQL
            st.executeUpdate(sb.toString());

        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            JdbcUtil.close(conn, st, null);
        }
    }

    //UPDATE student SET Student_name = 'XXX',age = 12 WHERE Student_id = '20178013'
    public void update(String id, Student newStu) {
        StringBuilder sb = new StringBuilder(80);
        sb.append("UPDATE student SET Student_name = '");
        sb.append(newStu.getName());
        sb.append("',age = ");
        sb.append(newStu.getAge());
        sb.append(" WHERE Student_id = '");
        sb.append(id);
        sb.append("'");
        //声明资源
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            //3.创建语句对象
            st = conn.createStatement();
            //4.执行SQL
            st.executeUpdate(sb.toString());

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, st, null);
        }
    }

    public Student get(String id) {

        String sql = "SELECT * FROM student WHERE Student_id = " + id;

        //声明资源
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtil.getConnection();
            //3.创建语句对象
            st = conn.createStatement();
            //4.执行SQL
            rs = st.executeQuery(sql);
            //5.处理结果集
            //--------------------------------
            if (rs.next()) {
                Student stu = new Student();
                //获取当前光标所在的行的列值,并设置到stu中
                stu.setAge(rs.getInt("age"));
                stu.setId(rs.getString("Student_id"));
                stu.setName(rs.getString("Student_name"));
                return stu;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, st, rs);
        }
        return null;
    }

    public List<Student> listAll() {
        List<Student> list = new ArrayList<>();
        String sql = "SELECT * FROM student";
        //声明资源
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtil.getConnection();
            //3.创建语句对象
            st = conn.createStatement();
            //4.执行SQL
            rs = st.executeQuery(sql);
            //5.处理结果集
            //--------------------------------
            while (rs.next()) {
                Student stu = new Student();
                //获取当前光标所在的行的列值,并设置到stu中
                stu.setAge(rs.getInt("age"));
                stu.setId(rs.getString("Student_id"));
                stu.setName(rs.getString("Student_name"));
                list.add(stu);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, st, rs);
        }
        return list;
    }
}

这样看上去代码更加简洁了。

但是,这些还是可以继续进行封装。

抽取db.properties

在JdbcUtil中存在硬编码,还是不利于维护,我们可以将配置信息专门提取到配置文件中去,因此,我们抽取出了db.properties文件,存放于source folder目录中:

修改后的JdbcUtil一部分:

    private static Properties p = new Properties();

    //静态代码块,当JdbcUtil加载进JVM就开始执行
    static {
        try {
            //加载和读取db.properties
            InputStream inStream = Thread.currentThread().getContextClassLoader()
                    .getResourceAsStream("db.properties");
            p.load(inStream);
            Class.forName(p.getProperty("driverClassName"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //创建并返回一个Connection对象
    public static Connection getConnection() {
        try {
            return DriverManager.getConnection(
                    p.getProperty("url"),
                    p.getProperty("username"),
                    p.getProperty("password"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

       但是SQL语句看上去还是过于冗余,而且用字符串拼接来进行查询,不易于查询和修改。因此我们使用PreparedStatement(预编译语句对象)。

       让我们深入分析该Demo,发现在DAO方法中,每次都新创建一个Connection,然后关闭,所消耗的资源非常大。所以我们考虑可以重复利用Connection对象,解决方案:数据库连接池(DataSource)。

       对于DML(增删改)操作来说,代码模板其实是相同的,DQL(查询)操作代码模板其实也是相同的,我们考虑将其重构。因此我们重构出Jdbc Template类,封装DML和DQL操作的通用模板。

       我们发现SQL语句是很麻烦的,我们可以考虑不写SQL语句而能操作数据库。因此我们考虑模拟Hibernate框架。

具体内容请关注JAVA学习之DAO优化。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值