Spring Data JPA学习笔记

最近看了这个教程学习SpringData技术。老师开头演示了传统的JDBC使用方法,非常的繁琐,需要在逻辑代码中进行配置。每一次操作数据库都要通过建立连接、执行查询、释放资源三部重复的代码,还要写一个JDBCUtil工具类来整合数据库连接和关闭的逻辑:

/**
 * JDBC工具类:
 * 1) 获取Connection
 * 2) 释放资源
 */
public class JDBCUtil {
    /**
     * 获取Connection
     * @return 所获得到的JDBC的Connection
     */
    public static Connection getConnection() throws Exception {

        /**
         * 不建议大家把配置硬编码到代码中
         *
         * 最佳实践:配置性的建议写到配置文件中
         */
//        String url = "jdbc:mysql:///spring_data";
//        String user = "root";
//        String password = "root";
//        String driverClass = "com.mysql.jdbc.Driver";

        InputStream inputStream =        JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
        Properties properties = new Properties();
        properties.load(inputStream);


        String url = properties.getProperty("jdbc.url");
        String user = properties.getProperty("jdbc.user");
        String password = properties.getProperty("jdbc.password");
        String driverClass = properties.getProperty("jdbc.driverClass");

        Class.forName(driverClass);
        Connection connection = DriverManager.getConnection(url, user, password);
        return connection;
    }

    /**
     * 释放DB相关的资源
     * @param resultSet
     * @param statement
     * @param connection
     */
    public static void release(ResultSet resultSet,
                               Statement statement, Connection connection){

        if(resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

为了避免这样繁琐的配置操作,Spring框架内置的 JdbcTemplate模板来了,它将数据的配置独立到了.xml文件中,和逻辑代码独立开来,很大程度简化了代码,助力开发:

  • 添加Maven依赖(搜索Maven Repository,在Maven仓库中找到需要的依赖)
    这里需要添加两个依赖:spring-jdbc&spring-context?
  • 配置beans.xml:DataSource&JdbcTemplate
  • 开发spring jdbc版本的query和save方法
  • 写Test Case

在Spring中,我们将beans.xml配置文件放在Resources文件夹下,这里截取部分主要内容:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="url" value="jdbc:mysql:///spring_data"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="studentDAO" class="com.imooc.dao.StudentDAOSpringJdbcImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>
</beans>

上面是视频中的代码,由于各种更新,我在《精通Spring 4.x》中学习的下面的代码也是可行的,原理逻辑是一样的,自己对比:

<!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
	<context:component-scan base-package="com.smart.dao"/>
	<context:component-scan base-package="com.smart.service"/>

	<!-- 配置数据源 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		  destroy-method="close"
		  p:driverClassName="com.mysql.jdbc.Driver"
		  p:url="jdbc:mysql://localhost:3306/sampledb"
		  p:username="root"
		  p:password="123456"/>

	<!-- 配置Jdbc模板  -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
		  p:dataSource-ref="dataSource"/>

	<!-- 配置事务管理器 -->
	<bean id="transactionManager"
		  class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
		  p:dataSource-ref="dataSource"/>

总结弊端

  • DAO has many many code
  • DAOImpl has many duplicate code
  • Develop the page and other functions对于开发分页和开发其他方法工作量很大

Spring Data JPA

PS:这部分的总结参考这里
SpringData JPA只是SpringData中的一个子模块
JPA是一套标准接口,而Hibernate是JPA的实现
SpringData JPA 底层默认实现是使用Hibernate

(Hibernate是什么呢?Hibernate是一种Java语言下的对象关系映射(ORM)解决方案。是一种ORM框架,全称为 Object_Relative DateBase-Mapping,在Java对象与关系数据库之间建立某种映射,以实现直接存取Java对象!通俗点说,就是Hibernate能将一个领域对象绑定到数据库的一张表上,无论你是想新建表还是CRUD这张表的数据,你只需要对这个类进行操作就可以了,而不需要直接去操作数据库,教程中的实例就是雇员: 先开发实体类===>自动生成对应的数据表)

开发环境搭建
首先我们要写配置文件,也就是POM和resources里面的.xml文件。这部分我踩了个坑,我根据教程写好POM配置以后,并没有办法测试SpringDataTest。报错信息第一句是:

java.sql.SQLException: Unknown system variable 'query_cache_size'

感谢isxuran给我提供的解决方案,原因是mysql-connecter-java的版本过低,很显然是数据库驱动程序与数据库版本不对应。所以我修改了mysql-connector-java的依赖版本,由教程中的5.1.38改成了5.1.6。为什么选择5.1.6呢,我只是在maven repository里网上找了一个使用率较高的进行尝试,没有报错,问题解决。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.ting</groupId>
    <artifactId>springdataPratice</artifactId>
    <version>1.0-SNAPSHOT</version>

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


        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>

        <!--spring-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>

        <!--spring data jpa-->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.8.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.3.6.Final</version>
        </dependency>

    </dependencies>


</project>

beans.xml中主要配置了数据源dataSourceHibernate会用到的EntityManagerFactory

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!--1 配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="url" value="jdbc:mysql://localhost:3306/spring_data"/>
    </bean>

    <!--2 配置EntityManagerFactory-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
        </property>
        <property name="packagesToScan" value="com.ting"/>

        <property name="jpaProperties">
            <props>
            <!--命名策略-->
                <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
                <!--使用的方言,这里我们用的是Mysql-->
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
                <!--查询时是否显示sql语句-->
                <prop key="hibernate.show_sql">true</prop>
                <!--是否格式化,这里选择格式化的好处是控制台输出的Hibernate语句会分行清晰的显示,否则会一行到底看不清楚,下文附对比图-->
                <prop key="hibernate.format_sql">true</prop> 
                <!--自动创建领域对象对应的数据表-->
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>

    </bean>

    <!--3 配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <!--4 配置支持注解的事务-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!--5 配置spring data-->
    <jpa:repositories base-package="com.ting" entity-manager-factory-ref="entityManagerFactory"/>

	<!--能够自动扫描指定包中的文件-->
    <context:component-scan base-package="com.ting"/>

</beans>

Hibernate格式化与不格式化对比图(绿色框内的是格式化后的输出):
在这里插入图片描述
接着我们只需要写好domain object和测试类,进行单元测试即可:
在这里插入图片描述
单元测试通过后我们会发现数据库中已经新建了一个employee表:
在这里插入图片描述
然后我们再修改一下beans.xml中的事务管理配置、实现repository接口即可。

Spring Data JPA进阶

  1. Repository类的定义
public interface Repository<T, ID extends Serializable> {
}
import com.ting.domain.Employee;
import org.springframework.data.repository.Repository;

public interface EmployeeRepository extends Repository<Employee,Integer> {

    public Employee findByName(String name);
}
  • Repository是一个空接口,这种接口称为标记接口
    没有包含方法声明的接口
  • 如果我们定义的接口EmployeeRepository继承了核心的接口Repository,那么就是告诉了Spring来管理我们的接口。假如我们没有继承的会,运行就会报错:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.ting.repository.EmployeeRepository' available

这说明你没有继承核心接口,那么Spring就找不到你定义的接口。
另外我们也可以用注释来代替继承语句,作用是一样的:

import com.ting.domain.Employee;
import org.springframework.data.repository.RepositoryDefinition;

@RepositoryDefinition(domainClass = Employee.class,idClass = Integer.class)
public interface EmployeeRepository {

    public Employee findByName(String name);
}
  1. Repository子类接口:CrudRepository,JpaRepository,PagingAndSortingRepository,JpaSpecificationExecutor

在IDEA中crtl+t/h点击相应的类查看继承关系
在这里插入图片描述

  • CrudRepository:继承Repository,实现了CRUD相关操作
  • PagingAndSortingRepository:继承CrudRepository,实现了分页排序的相关方法
  • JpaRepository:继承PagingAndSortingRepository,实现JPA规范相关的方法
  1. 使用Repository接口查询方法定义规则和使用
// where name like ?% and age <?
public List<Employee> findByNameStartingWithAndAgeLessThan(String name, Integer age);

// where name like %? and age <?
public List<Employee> findByNameEndingWithAndAgeLessThan(String name, Integer age);

// where name in (?,?....) or age <?
public List<Employee> findByNameInOrAgeLessThan(List<String> names, Integer age);

// where name in (?,?....) and age <?
public List<Employee> findByNameInAndAgeLessThan(List<String> names, Integer age);

弊端:
1)方法名会比较长: 约定大于配置
2)对于一些复杂的查询,是很难实现
因此,对于这种情况下还是要写SQL语句简单得多。所以又引入了@Query注解:

@Query("select o from Employee o where id=(select max(id) from Employee t1)")
public Employee getEmployeeByMaxId();

@Query("select o from Employee o where o.name=?1 and o.age=?2")
public List<Employee> queryParams1(String name, Integer age);

@Query("select o from Employee o where o.name=:name and o.age=:age")
public List<Employee> queryParams2(@Param("name")String name, @Param("age")Integer age);

@Query("select o from Employee o where o.name like %?1%")
public List<Employee> queryLike1(String name);

@Query("select o from Employee o where o.name like %:name%")
public List<Employee> queryLike2(@Param("name")String name);

//nativeQuery = true 打开原生态查询
@Query(nativeQuery = true, value = "select count(1) from employee")
public long getCount();

对于修改数据,需要增加Modify注解、并且一定要在事务的管理下才能修改数据,读取不需要事务

@Modifying
@Query("update Employee o set o.age = :age where o.id = :id")
public void update(@Param("id")Integer id, @Param("age")Integer age);

补充知识:【JPQL】–JPQL和SQL的比较

然后在service层:

package com.ting.services;

import com.ting.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service //告诉Spring这是Service层
public class EmployeeService {

    @Autowired //注入,注意注入的前提是在beans.xml中定义了<context:component-scan base-package="com.ting"/>上下文扫描路径
    private EmployeeRepository employeeRepository;

    @Transactional //告诉Spring要添加事务管理,否则无法执行修改数据库的操作
    public void update(Integer id,Integer age) {
        employeeRepository.update(id,age);
    }
}

事务在Spring data中的使用:
1事务一般是在Service层
2@Query、 @Modifying、@Transactional的综合使用

  1. 使用CrudRepository接口:
    这个接口继承源代码所提供的方法:
<S extends T> S save(S entity);
<S extends T> Iterable<S> save(Iterable<S> entities);
T findOne(ID id);
boolean exists(ID id);
Iterable<T> findAll();
Iterable<T> findAll(Iterable<ID> ids);
long count();
void delete(ID id);
void delete(T entity);
void delete(Iterable<? extends T> entities);
void deleteAll();

使用这个接口我们可以实现对数据库的批量操作CRUD

  1. 使用分页PagingAndSortingRepository接口:
    在这里插入图片描述
    EmployeePagingAndSortingRepositoryTest.java:
package com.ting.services;

import com.ting.domain.Employee;
import com.ting.repository.EmployeeCrudRepository;
import com.ting.repository.EmployeePagingAndSortingRepository;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

public class EmployeePagingAndSortingRepositoryTest {
    private ApplicationContext ctx = null;
    private EmployeePagingAndSortingRepository employeePagingAndSortingRepository = null;

    @Before
    public void setup() {
        ctx = new ClassPathXmlApplicationContext("beans.xml");
        employeePagingAndSortingRepository = ctx.getBean(EmployeePagingAndSortingRepository.class);
        System.out.println("setup");
    }

    @After
    public void tearDown() {
        ctx = null;
        System.out.println("tearDown");
    }

    @Test
    public void testPage() {
        //注意page参数index从0开始
        Pageable pageable = new PageRequest(0,5);
        Page<Employee> page = employeePagingAndSortingRepository.findAll(pageable);

        System.out.println("查询的总页数" + page.getTotalPages());
        System.out.println("查询的总记录数" + page.getTotalElements());
        System.out.println("当前第几页" + (page.getNumber()+1));
        System.out.println("当前页面的集合" + page.getContent().toString());
        System.out.println("当前页面的记录数" + page.getNumberOfElements());
    }
}
  1. 使用JpaRepository接口
    接口源代码所提供的方法:
List<T> findAll();
List<T> findAll(Sort sort);
List<T> findAll(Iterable<ID> ids);
<S extends T> List<S> save(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
T getOne(ID id);
  1. 补充一个JpaSpecificationExecutor接口
    使用时同时extends JpaRepository<Employee,Integer>,JpaSpecificationExecutor
    Specification封装了JPA Criteria查询条件,可以操作分页、排序、条件查询.
@Test
    public void testQuery() {
        Sort.Order order = new Sort.Order(Sort.Direction.ASC,"id");
        Sort sort = new Sort(order);

        Pageable pageable = new PageRequest(1,5,sort);


        /**
         * Root:我们要查询的类型(Employee)
         * query:添加查询条件
         * cb:构建Predicate
         */
        Specification<Employee> specification = new Specification<Employee>() {
            @Override
            public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                //root(employee(age))
                Path path = root.get("age");
                return  cb.gt(path,50); //gt:greater than
            }
        };

        Page<Employee> page = employeeJpaSpecificationRepository.findAll(specification,pageable);
        System.out.println("查询的总页数" + page.getTotalPages());
        System.out.println("查询的总记录数" + page.getTotalElements());
        System.out.println("当前第几页" + (page.getNumber()+1));
        System.out.println("当前页面的集合" + page.getContent().toString());
        System.out.println("当前页面的记录数" + page.getNumberOfElements());

    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Data JPA 是什么? Spring Data JPASpring 框架中的一个模块,它提供了一种方便的方式来访问和操作数据库,同时也简化了开发人员的工作。它基于 JPA 规范,提供了一些常用的 CRUD 操作方法,同时也支持自定义查询和分页查询等功能。 Spring Data JPA 的优点是什么? Spring Data JPA 的优点包括: 1. 简化了数据访问层的开发,提高了开发效率。 2. 提供了一些常用的 CRUD 操作方法,减少了重复的代码编写。 3. 支持自定义查询和分页查询等功能,提高了查询效率。 4. 可以与 Spring 框架无缝集成,方便使用。 5. 支持多种数据库,包括关系型数据库和 NoSQL 数据库等。 6. 提供了一些高级特性,如二级缓存、延迟加载等。 Spring Data JPA 的缺点是什么? Spring Data JPA 的缺点包括: 1. 学习曲线较陡峭,需要掌握 JPA 规范和 Spring 框架的相关知识。 2. 对于复杂的查询,需要编写自定义查询语句,增加了开发难度。 3. 对于大规模数据的查询和操作,可能会出现性能问题。 4. 对于一些特殊的需求,可能需要使用原生 SQL 或其他 ORM 框架来实现。 Spring Data JPA 和 Hibernate 有什么区别? Spring Data JPA 是基于 JPA 规范的,而 Hibernate 是一个 ORM 框架,它实现了 JPA 规范。因此,Spring Data JPA 和 Hibernate 之间的区别主要在以下几个方面: 1. Spring Data JPA 是一个数据访问层框架,而 Hibernate 是一个 ORM 框架。 2. Spring Data JPA 提供了一些常用的 CRUD 操作方法,而 Hibernate 更加灵活,可以编写任意复杂的查询语句。 3. Spring Data JPA 可以与 Spring 框架无缝集成,而 Hibernate 可以与任何 Java 应用程序集成。 4. Spring Data JPA 支持多种数据库,包括关系型数据库和 NoSQL 数据库等,而 Hibernate 主要支持关系型数据库。 5. Spring Data JPA 提供了一些高级特性,如二级缓存、延迟加载等,而 Hibernate 也提供了类似的特性。 如何使用 Spring Data JPA? 使用 Spring Data JPA 的步骤如下: 1. 添加依赖:在项目的 pom.xml 文件中添加 Spring Data JPA 的依赖。 2. 配置数据源:在 Spring 的配置文件中配置数据源。 3. 定义实体类:定义与数据库表对应的实体类,并使用 JPA 注解进行映射。 4. 定义 DAO 接口:定义一个继承 JpaRepository 接口的 DAO 接口。 5. 编写业务逻辑:在 Service 层中编写业务逻辑,调用 DAO 接口中的方法进行数据操作。 6. 运行程序:启动应用程序,测试数据访问和操作是否正常。 如何进行分页查询? 使用 Spring Data JPA 进行分页查询的步骤如下: 1. 在 DAO 接口中定义一个继承 PagingAndSortingRepository 接口的方法。 2. 在 Service 层中调用 DAO 接口中的分页查询方法,并指定分页参数。 3. 在控制器中接收分页参数,并将查询结果传递给前端页面。 4. 在前端页面中显示分页信息和查询结果。 如何进行自定义查询? 使用 Spring Data JPA 进行自定义查询的步骤如下: 1. 在 DAO 接口中定义一个自定义查询方法,并使用 @Query 注解指定查询语句。 2. 在 Service 层中调用 DAO 接口中的自定义查询方法。 3. 在控制器中接收查询结果,并将结果传递给前端页面。 4. 在前端页面中显示查询结果。 如何进行事务管理? 使用 Spring Data JPA 进行事务管理的步骤如下: 1. 在 Spring 的配置文件中配置事务管理器。 2. 在 Service 层中使用 @Transactional 注解标记需要进行事务管理的方法。 3. 在控制器中调用 Service 层中的方法。 4. 如果方法执行成功,则事务会自动提交,否则事务会自动回滚。 如何进行多表查询? 使用 Spring Data JPA 进行多表查询的步骤如下: 1. 在 DAO 接口中定义一个自定义查询方法,并使用 @Query 注解指定查询语句。 2. 在查询语句中使用 JOIN 关键字连接多个表。 3. 在 Service 层中调用 DAO 接口中的自定义查询方法。 4. 在控制器中接收查询结果,并将结果传递给前端页面。 5. 在前端页面中显示查询结果。 如何进行级联操作? 使用 Spring Data JPA 进行级联操作的步骤如下: 1. 在实体类中使用 @OneToMany 或 @ManyToOne 注解定义关联关系。 2. 在 Service 层中编写业务逻辑,调用 DAO 接口中的方法进行级联操作。 3. 在控制器中接收操作结果,并将结果传递给前端页面。 4. 在前端页面中显示操作结果。 如何进行缓存管理? 使用 Spring Data JPA 进行缓存管理的步骤如下: 1. 在 Spring 的配置文件中配置缓存管理器。 2. 在实体类中使用 @Cacheable 或 @CacheEvict 注解指定缓存策略。 3. 在 Service 层中编写业务逻辑,调用 DAO 接口中的方法进行数据操作。 4. 在控制器中接收操作结果,并将结果传递给前端页面。 5. 在前端页面中显示操作结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值