druid 多数据源_面试官:数据持久化方式有哪几种?怎么配置多数据源?

Spring Boot整合持久层的三种方式

在实际的Java开发中,不可避免的要对数据持久化,常用的持久化技术有MyBatis、Spring自带的JdbcTemplate和Spring Boot提供的Jpa规范。在这篇文章中,我们会逐个讲解这三种方式的使用及多数据源的配置。

目录

  1. Spring Boot整合JdbcTemplate
  2. Spring Boot整合MyBatis
  3. Spring Boot整合Jpa

1、SpringBoot整合JdbcTemplate

大家一定都有这样的经历,刚开始学Java操作数据库时,当时没有什么框架可以使用,就使用原生的JDBC来操作数据,导致代码繁琐且冗余度高。为了解决这样的问题,Spring对JDBC做了封装形成了JdbcTemplate,它可以使用Spring的注入功能将数据源(DataSource)注入到JdbcTemplate中,Spring中的IOC容器可以将数据源当做Java Bean一样管理,以此简化了对数据库的操作。

1.1基本配置

  • POM文件配置

JdbcTemplate使用很简单,只需要我们在Spring  Boot项目中添加数据库驱动和Druid数据源依赖即可

    
        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.1.21version>
        dependency>

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>

NOTE:选取数据源依赖时,有两种选择,分别是druid和druid-spring-boot-starter,它们的区别是druid-spring-boot-starter可以配置多数据源

  • application.properties文件配置

application.properties主要是链接数据库的一些基本信息,如数据库的用户名、密码、数据源类型、驱动等等。具体的配置如下:

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring-boot-study?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root

1.2使用

在使用之前,我们首先创建一个实体类与指定数据库中的表对应起来,我们这里创建一个User Bean

public class User {
    private Long id;
    private String username;
    private String address;
    //省略getter/setter
}

接下来就是使用JdbcTemplate进行增删改查,除了几个查询的API,增删改都是采用update操作,只需要在update方法中传入SQL语句即可。

/**
     * 添加操作
     */
    public Integer addUser(User user){
       return jdbcTemplate.update("insert into user(username,address) values (?,?)",user.getUsername(),user.getAddress());
    }
    /**
     * 修改操作
     */

    public Integer updateUser(User user){
        return jdbcTemplate.update("update user set username=?,address=? where id=?",user.getUsername(),user.getAddress(),user.getId());
    }

    /**
     * 删除操作
     */
    public Integer deleteUserById(Integer id) {
        return jdbcTemplate.update("delete from user where id=?", id);
    }

    /**
     * 查询操作
     * getAllUsers1适用于数据库中表的字段名和实体中字段名不一致的情况
     */
    public List getAllUsers1(){
        return jdbcTemplate.query("select * from user", new RowMapper() {@Overridepublic User mapRow(ResultSet resultSet, int i) throws SQLException {
                User user=new User();int id=resultSet.getInt("id");
                String username=resultSet.getString("username");
                String address=resultSet.getString("address");
                user.setUsername(username);
                user.setId(id);
                user.setAddress(address);return user;
            }
        });
    }/**
     * 当实体类的属性和数据库表中的字段一致时,可以采用此方法
     */public List getAllUsers2(){return jdbcTemplate.query("select * from user",new BeanPropertyRowMapper<>(User.class));
    }

1.3配置多数据源

我们在开发中不可避免的要配置多数据源,JdbcTemplate配置多数据源相对MyBatis和Jpa简单些,只需要修改数据源依赖、添加多数据源信息和增加相应的配置文件即可,下面我们逐个详细介绍

1.3.1修改druid依赖

将依赖由druid改为druid-spring-boot-starter

<dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.1.21version>
        dependency>
1.3.2添加数据库信息

在application.properties定义两个数据库信息spring-boot-study和spring-boot-study2

spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.one.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.one.jdbc-url=jdbc:mysql://127.0.0.1:3306/spring-boot-study?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
spring.datasource.one.username=root
spring.datasource.one.password=root

spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.two.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.two.jdbc-url=jdbc:mysql://127.0.0.1:3306/spring-boot-study2?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
spring.datasource.two.username=root
spring.datasource.two.password=root
1.3.3编写配置文件

因为我们在application.properties文件中修改了配置信息,所以我么需要重新定义数据源配置文件(DataSourceConfig)和JdbcTemplateConfig

  • DataSourceConfig配置
@Configuration
public class DataSourceConfig {
    @Bean
    //因为我们在配置文件中修改了名称,为了使得配置类能够正确找到数据源,需要配置前缀
    @ConfigurationProperties(prefix = "spring.datasource.one")
    DataSource sourceOne(){
        return DataSourceBuilder.create().build();
    }
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.two")
    DataSource sourceTwo(){
        return DataSourceBuilder.create().build();
    }
}
  • JdbcTemplateConfig配置
@Configuration
public class JdbcTemplateConfig {
    //以为有两个数据源,所以我们要使用@Qualifier确定那个是我们想要的
    @Bean
    JdbcTemplate jdbcTemplateOne(@Qualifier("sourceOne") DataSource sourceOne) {
        return new JdbcTemplate(sourceOne);
    }

    @Bean
    JdbcTemplate jdbcTemplateTwo(@Qualifier("sourceTwo") DataSource sourceTwo) {
        return new JdbcTemplate(sourceTwo);
    }

}

2、SpringBoot整合MyBatis

MyBatis是采用java编写的一个持久层框架,它同样封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动、创建链接等繁杂过程,使用ORM思想实现了对结果集的封装。

2.1基本配置

  • POM文件

和JdbcTemplate不同的是,MyBatis是第三方框架,因此在添加数据库驱动的基础上,需要添加MyBatis引入依赖,

<dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>2.1.3version>
        dependency>
  • application.properties

和JdbcTemplate相同,都需要在application.properties文件上配置连接数据库的信息

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring-boot-study?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root

2.2使用

在使用之前,需要创建实体类与数据库中的表对应,并创建Mapper.xml用于操作SQL语句

  • 定义 Bean
public class User {
    private Integer id;
    private String username;
    private String address;
    ......
    }
  • 定义配置文件mapper.xml

mapper.xml文件是建立起数据库表与实体类的映射,使用SQL语句将结果返回给具体的方法。mapper.xml是默认放在resources目录下,其目录设置要同mapper方法接口的层级结构相同。如果想将mapper.xml放在其他位置,需要重新配置文件的扫描路径,假如mapper.xml同mapper接口方法接口放在同一包下,则需要对pom修改为

 
        <resources>
            <resource>
                <directory>src/main/javadirectory>
                <includes>
                    <include>**/*.xmlinclude>
                includes>
            resource>
            <resource>
                <directory>src/main/resourcesdirectory>
            resource>
        resources>

2.3配置多数据源

上文中的2.1和2.2是对单个数据库表进行的配置操作,在实际的应用场景中,需要对多个不同的数据库表进行操作,所以非常有必要学习多数据源的配置。

2.3.1修改druid依赖

同JdbcTemplate一样,当使用多个数据源时,需要将druid依赖由druid改为druid-spring-boot-starter。

   <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.1.21version>
        dependency>
2.3.2 添加数据库信息

同JdbcTemplate一样,需要在application.properties定义两个数据库信息spring-boot-study和spring-boot-study2作为测试

spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.one.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.one.url=jdbc:mysql://127.0.0.1:3306/spring-boot-study?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
spring.datasource.one.username=root
spring.datasource.one.password=root


spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.two.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.two.url=jdbc:mysql://127.0.0.1:3306/spring-boot-study2?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
spring.datasource.two.username=root
spring.datasource.two.password=root
2.3.3编写配置文件

因为我们配置两个数据库表,所以我们需要分别编写配置文件来各自处理不同的mapper。

2.3.3.1配置DataSourceConfig

同JdbcTemplate相同,我们需要编写配置文件(DataSourceConfig)来分别处理

@Configuration
public class DataSourceConfig {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.one")
    DataSource sourceOne() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.two")
    DataSource sourceTwo() {
        return DruidDataSourceBuilder.create().build();
    }

}

@ConfigurationProperties(prefix = "spring.datasource.one")是为了定义不同的数据库表。

2.3.3.2配置MyBatisConfig1

配置MyBatisConfig1是为了对应mapper1

@Configuration
@MapperScan(basePackages = "com.simon.mybatis2.mapper1",sqlSessionFactoryRef = "sqlSessionFactory1",sqlSessionTemplateRef = "sqlSessionTemplate1")
public class MyBatisConfigOne {
    @Resource(name = "sourceOne")
    DataSource sourceOne;

    @Bean
    SqlSessionFactory sqlSessionFactory1() {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        try {
            bean.setDataSource(sourceOne);
            return bean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Bean
    SqlSessionTemplate sqlSessionTemplate1() {
        return new SqlSessionTemplate(sqlSessionFactory1());
    }

}

在单数据库表下,使用MyBatis框架持久化数据是按照以下流程

248d66bfe5f19557444fec0390ca292a.png

对应的代码为

       //导入配置文件,SqlMapConfig.xml配置的是数据源即数据库信息
        InputStream in= Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建sqlSessionFactory的构建者对象
        SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
        //使用构建者创建工厂对象sqlSessionFactory
        SqlSessionFactory factory=builder.build(in);
        //4.使用 SqlSessionFactory 生产 SqlSession 对象
        SqlSession session = factory.openSession();
        //5.使用 SqlSession 创建 dao 接口的代理对象
        IUserDao userDao = session.getMapper(IUserDao.class);

而在我们配置文件MyBatisConfig1用到了SqlSessionFactory、SqlSessionTemplate,SqlSessionFactoryBean,因此会有同学有疑问,SqlSessionFactory、SqlSessionTemplate,SqlSessionFactoryBean和SqlSession之间的关系是什么

  • SqlSession

SqlSession实现了Closeable接口,是一种可关闭的连接,可以表示数据库客户端和数据库服务端之间的一种会话,并维护了两者之间的状态信息。SqlSession接口内有用于操作数据库执行sql语句的select、insert、update等方法。

  • SqlSessionTemplate

SqlSessionTemplate是SqlSession的具体实现类,除了实现SqlSession,它还实现了DisposableBean接口,这表明,SqlSessionTemplate的实例被Bean工厂发现后,会把他们纳入整个spring  bean生命周期的管理过程之中,当BeanFactory尝试销毁时,Beans的管理者会以回调的方式调用SqlSessionTemplate的destroy()方法。因此,我们就可以执行Dao层的sql语句

  • SqlSessionFactory

SqlSessionFactory也是一个接口,是生产SqlSession工厂(采用动态代理的方式),它可以打开一个SqlSession会话,而且重载了许多不同的参数,你可以改变这些参数自定义会话过程中的一些默认行为。例如:可以设置自动提交事务或是关闭自动提交;可以设置获取数据库连接的线程的类型(重用,每次新产生等等);也可以获取整个MyBatis的配置信息的Configuration对象实例等等。

  • SqlSessionFactoryBean

SqlSessionFactoryBean实现了FactorBean接口,表示SqlSessionFactoryBean的实例不再是一个普通的bean对象,而是可以产生自己Bean(SqlSessionFactory)的一个工厂,并且产生的Bean会被纳入spring的生命周期。

总结

SqlSessionFactoryBean是生产SqlSessionFactory的一种工厂bean。SqlSessionFactory是打开SqlSession会话的工厂,是一个接口,可以根据需求自己实现,它的默认实现类DefaultSqlSessionFactory使用了数据库连接池技术。SqlSession是客户端和数据库服务端之间的会话信息,里面有许多操作数据库的方法。SqlSessionTemplate是SqlSession的一个具体实现。

2.3.3.3配置MyBatisConfig2

MyBatisConfig2同MyBatisConfig1一样,只不过它对应的是mapper2

@Configuration
@MapperScan(basePackages = "com.simon.mybatis2.mapper2",sqlSessionFactoryRef = "sqlSessionFactory2",sqlSessionTemplateRef = "sqlSessionTemplate2")
public class MyBatisConfigTwo {
    @Resource(name = "sourceTwo")
    DataSource sourceTwo;

    @Bean
    SqlSessionFactory sqlSessionFactory2() {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        try {
            bean.setDataSource(sourceTwo);
            return bean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Bean
    SqlSessionTemplate sqlSessionTemplate2() {
        return new SqlSessionTemplate(sqlSessionFactory2());
    }

}

3、SpringBoot整合Jpa

Jpa(Java Persistence API)是SUN公司提出的ORM规范,提供了一种对象/映射工具来管理Java应用中的关系数据,使用XML或者注解的方式简化对数据库的操作。

它的出现是为了规范现有的ORM框架,采用Jpa开发时,我们仍将使用这些ORM框架,只是此时开发出来的应用不在依赖于某个持久化提供商。应用可以在不修改代码的情况下载任何JPA环境下运行,真正做到低耦合,可扩展的程序设计。类似于JDBC,在JDBC出现以前,我们的程序针对特性的数据库API进行编程,但是现在我们只需要针对JDBC API编程,这样能够在不改变代码的情况下就能换成其他的数据库。

614370881c2d6e760860b199d7a5eedb.png

而Spring-Data-jpa是在Jpa规范下提供的Resporsity实现,JpaResporsity拥有常用的CURD方法及分页、字段排序等,可以统一不同ORM框架对数据库操作的代码。

3.1基本配置

  • Pom文件

在Pom文件中添加MySQL驱动和spring-boot-starter-data-jpa依赖

    <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-jpaartifactId>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>
  • application.properties

在application.properties配置文件中添加数据库表的信息

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring-boot-study?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root

#jpa是自动生成Sql语句的,是否在命令行展示SQL语句
spring.jpa.show-sql=true
spring.jpa.database=mysql
spring.jpa.database-platform=mysql
#jpa可以根据实体类自动的数据库创建表,update表示,表存在时更新,不存在时创建
spring.jpa.hibernate.ddl-auto=update
#根据不同的数据版本确定,用于确定引擎,InnoDB
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

3.2使用

  • 实体类User

在使用之前,需要编写一个实体类与数据库中的表对应

@Entity(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private String address;

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

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

注解@Entity表明这是个实体类,并且可以根据实体类中的信息创建相应的表放在数据库中。@Id表明对用数据库表中的主键Id(必须有), @GeneratedValue(strategy = GenerationType.IDENTITY)表是Id自增长。

  • dao接口
public interface UserDao  extends JpaRepository<User,Integer> {
}

如果仅仅使用常见的CURD操作,接口中不需要写自定义的方法,JpaRepository已经实现常用的CURD操作,可以满足我们日常的操作。

3.3配置多数据源

3.3.1修改druid依赖

同JdbcTemplate、MyBatis一样,当使用多个数据源时,需要将druid依赖由druid改为druid-spring-boot-starter。

<dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.1.21version>
        dependency>
3.3.2添加数据库信息
spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.one.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.one.url=jdbc:mysql://127.0.0.1:3306/spring-boot-study?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
spring.datasource.one.username=root
spring.datasource.one.password=root

spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.two.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.two.url=jdbc:mysql://127.0.0.1:3306/spring-boot-study2?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
spring.datasource.two.username=root
spring.datasource.two.password=root

spring.jpa.properties.show-sql=true
spring.jpa.properties.database=mysql
spring.jpa.properties.database-platform=mysql
spring.jpa.properties.hibernate.ddl-auto=update
spring.jpa.properties.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
3.3.3编写配置文件
3.3.3.1配置数据源
@Configuration
public class DataSourceConfig {
    @Bean
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.one")
    DataSource sourceOne(){
        return DataSourceBuilder.create().build();
    }


    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.two")
    DataSource sourceTwo() {
        return DruidDataSourceBuilder.create().build();
    }

}
3.3.3.2配置JpaConfig1

配置JpaConfig1是为了对应Dao1

@Configuration
@EnableJpaRepositories(basePackages = "com.simon.jpa2.dao1",entityManagerFactoryRef = "localContainerEntityManagerFactoryBean1",transactionManagerRef = "platformTransactionManager1")
public class JpaConfig1 {
    @Autowired
    @Qualifier("sourceOne")
    DataSource sourceOne;

    @Autowired
    JpaProperties jpaProperties;

    @Bean
    @Primary
    /**
     * JPA EntityManagerFactory实例的构建器,允许你通过一个构建器模式创建一个或多个LocalContainerEntityManagerFactoryBean,并做一些常见配置。
     */
    LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean1(EntityManagerFactoryBuilder builder) {
        return builder.dataSource(sourceOne)
                .properties(jpaProperties.getProperties())
                .persistenceUnit("unit1")
                .packages("com.simon.jpa2.bean")
                .build();
    }

    @Bean
    PlatformTransactionManager platformTransactionManager1(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(localContainerEntityManagerFactoryBean1(builder).getObject());
    }

}
3.3.3.3配置JpaConfig2

配置JpaConfig1是为了对应Dao2

@Configuration
@EnableJpaRepositories(basePackages = "com.simon.jpa2.dao2",entityManagerFactoryRef = "localContainerEntityManagerFactoryBean2",transactionManagerRef = "platformTransactionManager2")
public class JpaConfig2 {
    @Autowired
    @Qualifier("sourceTwo")
    DataSource sourceTwo;

    @Autowired
    JpaProperties jpaProperties;

    @Bean
    @Primary
    /**
     * JPA EntityManagerFactory实例的构建器,允许你通过一个构建器模式创建一个或多个LocalContainerEntityManagerFactoryBean,并做一些常见配置。
     */
    LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean2(EntityManagerFactoryBuilder builder) {
        return builder.dataSource(sourceTwo)
                .properties(jpaProperties.getProperties())
                .persistenceUnit("unit2")
                .packages("com.simon.jpa2.bean")
                .build();
    }

    @Bean
    PlatformTransactionManager platformTransactionManager2(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(localContainerEntityManagerFactoryBean2(builder).getObject());
    }

}

参考

[1]https://www.cnblogs.com/xichji/p/12342569.html

[2]https://blog.csdn.net/android_bar/article/details/81040580

[3]https://gitee.com/lenve/javaboy-video-samples/tree/master/%E7%AC%AC%2005%20%E7%AB%A0%20Spring%20Boot%20%E6%95%B4%E5%90%88%E6%8C%81%E4%B9%85%E5%B1%82%E6%8A%80%E6%9C%AF

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值