Spring第五章:JDBC Template

该系列为imooc Spring从入门到进阶笔记,跟随课程加入自己见解,同时也为项目中碰到一些问题做了解答
本门课程是使用Spring组件JDBC Template简化持久化操作

1、课程介绍

课程目标

  • 了解Spring组件JDBC Template
  • 能使用JDBC Template进行 持久化操作
  • 帮助自己学习Hibernate、 MyBatis等ORM框架

前置条件

  • JDBC
  • Spring IOC、Spring AOP
  • Mysql

课程中老师演示项目的环境

  • 操作系统:Win7
  • 数据库:MySQL5.7
  • JDK:jdk1.8.0

2、JDBC Template概念

Java访问数据库传统形式 与 通过JDBC Template访问数据库形式的对比
在这里插入图片描述
JDBC Template提供统一的模板方法,在保留代码灵活性的基础上, 尽量减少持久化代码

在这里插入图片描述

3、案例准备

项目示例:学生选课系统,包含三张表,表结构如下

  1. 准备数据库
    数据库表结构
    在这里插入图片描述

建表语句

drop database if exists selection_course;

create database selection_course;
use selection_course;

create table course
(
   id                   int not null auto_increment,
   name                 char(20),
   score                int,
   primary key (id)
);

create table selection
(
   student              int not null,
   course               int not null,
   selection_time       datetime,
   score                int,
   primary key (student, course)
);

create table student
(
   id                   int not null auto_increment,
   name                 varchar(20),
   sex                  char(2),
   born                 date,
   primary key (id)
);

alter table selection add constraint FK_Reference_1 foreign key (course)
      references course (id) on delete restrict on update restrict;

alter table selection add constraint FK_Reference_2 foreign key (student)
      references student (id) on delete restrict on update restrict;

insert into course(id,name,score) values(1001,'英语',5);
insert into course(id,name,score) values(1002,'操作系统',5);
insert into course(id,name,score) values(1003,'数据结构',3);

commit;
  1. 创建项目
    在这里插入图片描述
    在这里插入图片描述
  2. 配置项目POM文件
    <properties>
        <spring.version>4.0.2.RELEASE</spring.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.44</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
    </dependencies>
  1. 配置Spring applicationContext.xml文件
<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/selection_course?useUnicode=true&amp;characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

</beans>

4、JDBC Template基本使用及持久化案例

JDBC Template作为一个工具类,给使用者提供的简化持久化操作的功能是通过方法来呈现的,方法分为四类

  • execute方法
  • update与batchUpdate方法
  • query与queryXXX方法
  • call方法

4.1、execute方法

execute方法,一般用来创建数据表

使用流程

  1. 获取Bean中的JdbcTemplate对象
  2. 调用jdbcTemplate.execute(String sql)方法执行sql语句

代码演示

在test>java文件夹下>新建Test类

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

public class Test {
    private JdbcTemplate jdbcTemplate;
    {
        //1、获取JDBC Template对象
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        jdbcTemplate=(JdbcTemplate)applicationContext.getBean("jdbcTemplate");
    }
    
    @org.junit.Test
    //execute方法
    public void testExecute(){
        //2、通过调用execute()方法执行DDL语句
        jdbcTemplate.execute("create table user1(id int,name varchar (20))");
    }
}

启动测试
在这里插入图片描述
可见execute方法执行成功,新增了user1表

4.2、update方法和batchUpdate方法

  • update方法 ,对数据进行增删改操作
int update(String sql, Object[] args) 
int update(String sql, Object… args)
  • batchUpdate方法,批量增删改操作
int[] batchUpdate(String[] sql) //一次执行多条sql,每条sql需要写明确
int[] batchUpdate(String sql, List<Object[]> args)//多次执行一条相同的sql,每次执行的参数放在list中

返回值为影响的行数

代码演示

4.2.1、演示int update(String sql,Object[] args)方法

在test>java文件夹下>Test类中>新增testUpdate方法

    //演示update(String sql, Object[] args) 方法 ,对数据进行增删改操作
    @org.junit.Test
    public void testUpdate(){
        String sql="insert into student(name,sex) values(?,?)";
        jdbcTemplate.update(sql,new Object[]{"张飞","男"});

    }

启动测试
在这里插入图片描述
可见update(String sql, Object[] args) 方法执行成功

4.2.2、演示int update(String sql,Object… args)方法

在test>java文件夹下>Test类中>新增testUpdate2方法

    //演示update(String sql, Object… args)方法 ,对数据进行增删改操作
    @org.junit.Test
    public void testUpdate2(){
        String sql="update student set sex=? where id=?";
        jdbcTemplate.update(sql,"女",1);
    }

启动测试
在这里插入图片描述
可见update(String sql, Object… args)执行成功

4.2.3、演示int[] batchUpdate(String[] sql)方法

在test>java文件夹下>Test类中>新增testBatchUpdate方法

    @org.junit.Test
    //演示batchUpdate(String[] sql) ,对数据批量操作
    public void testBatchUpdate(){
        String[] sqls={
                "insert into student(name,sex) values('关羽','女')",
                "insert into student(name,sex) values('刘备','男')",
                "update student set sex='男' where id=1"
        };
        jdbcTemplate.batchUpdate(sqls);
    }

启动测试
在这里插入图片描述
可见 batchUpdate(String[] sql) 执行成功

4.2.4、演示int[] batchUpdate(String sql, List<Object[]> args)方法

在test>java文件夹下>Test类中>新增testBatchUpdate2方法

    @org.junit.Test
    //演示batchUpdate(String sql, List<Object[]> args) ,对数据批量操作
    public void testBatchUpdate2(){
        String sql = "insert into selection(student,course) values(?,?)";
        List<Object[]> list = new ArrayList<Object[]>();
        list.add(new Object[]{1,1001});
        list.add(new Object[]{1,1003});
        jdbcTemplate.batchUpdate(sql,list);
    }

启动测试
在这里插入图片描述
可见batchUpdate(String sql, List<Object[]> args)执行成功

4.3、query方法与queryXXX方法

4.3.1、查询简单数据项

  • 查询结果为单条记录,查询SQL的结果必须是单行,不然会报错
T queryForObject(String sql, Class<T> type) 
T queryForObject(String sql, Object[] args, Class<T> type) 
T queryForObject(String sql, Class<T> type,Object… arg) 
  • 查询结果为多条记录 ,查询SQL的结果必须与参数Class<T> type类型相同,不然会报错
List<T> queryForList(String sql , Class<T> type) 
List<T> queryForList(String sql , Object[] args , Class<T> type) 
List<T> queryForList(String sql , Class<T> type,Object… arg)
4.3.1.1、演示T queryForObject(String sql,Class<T> type)方法

在test>java文件夹下>Test类中>新增testQuerySimple1方法

    @org.junit.Test
    //演示queryForObject(String sql, Class<T> type)方法
    public void testQuerySimple1(){
        String sql = "select count(*) from student";
        int count = jdbcTemplate.queryForObject(sql,Integer.class);
        System.out.println(count);
    }

我们先看看student表中有多少数据
在这里插入图片描述
启动测试
在这里插入图片描述
可见queryForObject(String sql, Class type)方法执行成功

4.3.1.2、演示List<T> queryForList(String sql,Class<T> type,Object… arg)方法

在test>java文件夹下>Test类中>新增testQuerySimple2方法

    @org.junit.Test
    //演示queryForList(String sql , Class<T> type,Object… arg)方法
    public void testQuerySimple2(){
        String sql = "select name from student where sex=?";
        List<String> names = jdbcTemplate.queryForList(sql,String.class,"男");
        System.out.println(names);
    }

启动测试
在这里插入图片描述
可见queryForList(String sql , Class type,Object… arg)方法执行成功

4.3.2、查询复杂对象(封装为Map)

  • 获取一个
Map queryForMap(String sql) 
Map queryForMap(String sql , Object[] args) 
Map queryForMap(String sql , Object… arg) 
  • 获取多个
List<Map<String,Object>> queryForList(String sql) 
List<Map<String,Object>> queryForList(String sql , Object[] args) 
List<Map<String,Object>> queryForList(String sql , Object… arg)
4.3.2.1、演示Map queryForMap(String sql,Object… arg)方法

在test>java文件夹下>Test类中>新增testQueryMap1方法

    @org.junit.Test
    //演示Map queryForMap(String sql , Object… arg) 方法
    public void testQueryMap1(){
        String sql = "select * from student where id = ?";
        Map<String,Object> stu = jdbcTemplate.queryForMap(sql,1);
        System.out.println(stu);
    }

启动测试
在这里插入图片描述
可见queryForMap(String sql , Object… arg) 执行成功

4.3.2.2、演示List<Map<String,Object>> queryForList(String sql) 方法

在test>java文件夹下>Test类中>新增testQueryMap2方法

    @org.junit.Test
    //演示M演示List<Map<String,Object>> queryForList(String sql)方法方法
    public void testQueryMap2(){
        String sql = "select * from student";
        List<Map<String,Object>> stus = jdbcTemplate.queryForList(sql);
        System.out.println(stus);
    }

启动测试
在这里插入图片描述
可见List<Map<String,Object>> queryForList(String sql)执行成功

4.3.3、查询复杂对象(查询出来的结果封装为实体对象)

RowMapper接口
Spring JdbcTemplate中关于RowMapper的使用实例
项目中数据库存在student表,表中每一条记录代理一个学生;而在Java代码中,定义一个Student类,一个Student类对象表示一个学生。我们希望持久化操作时,从student表中查询出来的记录,能够方便的转化成Student类对象。但是在转化过程中,student表中字段怎么与Student类属性一一对应?JDBC Template在持久化操作时,就是通过RowMapper接口,实现表中字段与类属性一一对应的映射关系

我们可以通过new RowMapper<对象>()将数据库中获取的数据封装成我们需要的对象类型。实现接口中的mapRow(ResultSet r, int i)方法,在这个方法体中,通过结果集ResultSet获取数据库各字段的数据,然后在封装就可以了。

  • 获取一个
T queryForObject(String sql,RowMapper<T> mapper) 
T queryForObject(String sql,Object[] args,RowMapper<T> mapper) 
T queryForObject(String sql,RowMapper<T> mapper,Object… arg) 
  • 获取多个
List<T> query(String sql,RowMapper<T> mapper) 
List<T> query(String sql,Object[] args,RowMapper<T> mapper) 
List<T> query(String sql,RowMapper<T> mapper,Object… arg)

使用前的准备

  1. 以上queryForObject方法与query方法中,需要RowMapper接口实例化对象作为参数,那我们在Test类中声明私有的StudentRowMapper 类来实现RowMapper接口
    private class StudentRowMapper implements RowMapper<Student> {
        public Student mapRow(ResultSet resultSet, int i) throws SQLException {
            Student stu = new Student();
            stu.setId(resultSet.getInt("id"));
            stu.setName(resultSet.getString("name"));
            stu.setSex(resultSet.getString("sex"));
            stu.setBorn(resultSet.getDate("born"));
            return stu;
        }
    }
  1. 在Java>新建com.imooc.sc.entity包>新建Student 类(学生实体类)
package com.imooc.sc.entity;

import java.util.Date;

public class Student {
    private int id;
    private String name;
    private String sex;
    private Date born;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBorn() {
        return born;
    }

    public void setBorn(Date born) {
        this.born = born;
    }

    public String toString(){
        return "Student{"+id+","+name+","+sex+","+born+"}";
    }
}
4.3.3.1、演示T queryForObject(String sql,RowMapper<T> mapper,Object… arg)方法

在test>java文件夹下>Test类中>新增testQueryEntity1方法

    @org.junit.Test
    //演示T queryForObject(String sql,RowMapper<T> mapper,Object… arg)
    public void testQueryEntity1(){
        String sql = "select * from student where id = ?";
        Student stu = jdbcTemplate.queryForObject(sql, new StudentRowMapper(), 1);
        System.out.println(stu);
    }

启动测试
在这里插入图片描述
可见T queryForObject(String sql,RowMapper<T> mapper,Object… arg)执行成功

4.3.3.2、演示List<T> query(String sql,RowMapper<T> mapper)方法

在test>java文件夹下>Test类中>新增testQueryEntity2方法

    @org.junit.Test
    //演示List<T> query(String sql,RowMapper<T> mapper)
    public void testQueryEntity2(){
        String sql = "select * from student";
        List<Student> stus = jdbcTemplate.query(sql,new StudentRowMapper());
        System.out.println(stus);
    }

启动测试
在这里插入图片描述
可见List<T> query(String sql,RowMapper<T> mapper)方法执行成功

5、在项目中,使用JDBC Template封装持久层,结构上应该怎么设计?

在第3节中,我们已知示例为学生选课系统,包含三张表(学生表、课程表、选课表),在4.3.3中我们在com.imooc.sc.entity包中创捷了Student类,下面我们继续完善实体类

  1. 在Java>com.imooc.sc.entity包>新建Course类(课程实体类)
package com.imooc.sc.entity;

public class Course {
    private int id;
    private String name;
    private int score;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }
}

  1. 在Java>com.imooc.sc.entity包>新建Selection类(选课记录实体类)
package com.imooc.sc.entity;

import java.util.Date;

public class Selection {
    private int sid;
    private int cid;
    private Date selTime;
    private int score;

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public int getCid() {
        return cid;
    }

    public void setCid(int cid) {
        this.cid = cid;
    }

    public Date getSelTime() {
        return selTime;
    }

    public void setSelTime(Date selTime) {
        this.selTime = selTime;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }
}

接下来完善持久层接口与持久层接口实现类(在该项目示例中没有业务层去调用持久层,故不需要接口进行关联,但是为了严谨起见,还是创建持久层接口)

在持久层接口实现类中需要

  • 注入jdbcTemplate
  • 声明RowMapper(不需要查询返回实体类时,则不需声明)
  1. 在Java>新建com.imooc.sc.dao包>新建StudentDao 接口(StudentDao 持久层接口)
package com.imooc.sc.dao;

import com.imooc.sc.entity.Student;

import java.util.List;

public interface StudentDao {
    void insert(Student stu);
    void update(Student stu);
    void delete(int id);
    Student select(int id);
    List<Student> selectAll();
}

  1. 在Java>新建com.imooc.sc.dao.impl包>新建StudentDaoImpl 类(StudentDao 持久层接口实现类)
package com.imooc.sc.dao.impl;

import com.imooc.sc.dao.StudentDao;
import com.imooc.sc.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

@Repository
public class StudentDaoImpl implements StudentDao {
    //注入jdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insert(Student stu) {
        String sql = "insert into student(name,sex,born) values(?,?,?)";//born需要的参数类型是sql.DATE
        jdbcTemplate.update(sql,stu.getName(),stu.getSex(),stu.getBorn());//stu.getBorn()返回的参数类型是util.Date,按理说“stu.getBorn()”需要进行类型转换,但是jdbcTemplate已经帮我们自动转换了
    }

    public void update(Student stu) {
        String sql = "update student set name=?,sex=?,born=? where id=?";
        jdbcTemplate.update(sql,stu.getName(),stu.getSex(),stu.getBorn(),stu.getId());
    }

    public void delete(int id) {
        String sql = "delete from student where id=?";
        jdbcTemplate.update(sql,id);
    }

    public Student select(int id) {
        String sql = "select * from student where id=?";
        return jdbcTemplate.queryForObject(sql,new StudentRowMapper(),id);
    }

    public List<Student> selectAll() {
        String sql = "select * from student";
        return jdbcTemplate.query(sql,new StudentRowMapper());
    }
    
    //声明私有的StudentRowMapper 类来实现RowMapper接口
    private class StudentRowMapper implements RowMapper<Student> {
        public Student mapRow(ResultSet resultSet, int i) throws SQLException {
            Student stu = new Student();
            stu.setId(resultSet.getInt("id"));
            stu.setName(resultSet.getString("name"));
            stu.setSex(resultSet.getString("sex"));
            stu.setBorn(resultSet.getDate("born"));
            return stu;
        }
    }
}

  1. 在Java>com.imooc.sc.dao包>新建CourseDao 接口(CourseDao 持久层接口)
package com.imooc.sc.dao;

import com.imooc.sc.entity.Course;


import java.util.List;

public interface CourseDao {
    void insert(Course course);
    void update(Course course);
    void delete(int id);
    Course select(int id);
    List<Course> selectAll();
}

  1. 在Java>com.imooc.sc.dao.impl包>新建CourseDaoImpl类(CourseDao 持久层接口实现类)
package com.imooc.sc.dao.impl;

import com.imooc.sc.dao.CourseDao;
import com.imooc.sc.entity.Course;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@Repository
public class CourseDaoImpl implements CourseDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insert(Course course) {
        String sql = "insert into course(name,score) values(?,?)";
        jdbcTemplate.update(sql,course.getName(),course.getScore());
    }

    public void update(Course course) {
        String sql = "update course set name=?,score=? where id=?";
        jdbcTemplate.update(sql,course.getName(),course.getScore(),course.getId());
    }

    public void delete(int id) {
        String sql = "delete from course where id=?";
        jdbcTemplate.update(sql,id);
    }

    public Course select(int id) {
        String sql = "select * from course where id=?";
        return jdbcTemplate.queryForObject(sql,new CourseRowMapper(),id);
    }

    public List<Course> selectAll() {
        String sql = "select * from course";
        return jdbcTemplate.query(sql,new CourseRowMapper());
    }
    private class CourseRowMapper implements RowMapper<Course> {
        public Course mapRow(ResultSet resultSet, int i) throws SQLException {
            Course course = new Course();
            course.setId(resultSet.getInt("id"));
            course.setName(resultSet.getString("name"));
            course.setScore(resultSet.getInt("score"));
            return course;
        }
    }
}

  1. 在Java>com.imooc.sc.dao包>新建SelectionDao 接口(SelectionDao 持久层接口)
package com.imooc.sc.dao;

import com.imooc.sc.entity.Selection;

import java.util.List;
import java.util.Map;

public interface SelectionDao {
    void insert(List<Selection> seles);
    void delete(int sid,int cid);
    List<Map<String,Object>> selectByStudent(int sid);
    List<Map<String,Object>> selectByCourse(int cid);
}

  1. 在Java>com.imooc.sc.dao.impl包>新建SelectionDaoImpl 类(SelectionDao 持久层接口实现类)
package com.imooc.sc.dao.impl;

import com.imooc.sc.dao.SelectionDao;
import com.imooc.sc.entity.Selection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Repository
public class SelectionDaoImpl implements SelectionDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insert(List<Selection> seles) {
        String sql = "insert into selection values(?,?,?,?)";
        List<Object[]> list = new ArrayList<Object[]>();
        for(Selection sel:seles){
            Object[] args = new Object[4];
            args[0] = sel.getSid();
            args[1]=sel.getCid();
            args[2] = sel.getSelTime();
            args[3] =sel.getScore();
            list.add(args);
        }
        jdbcTemplate.batchUpdate(sql,list);
    }

    public void delete(int sid,int cid) {
        String sql = "delete from selection where student=? and course=?";
        jdbcTemplate.update(sql,sid,cid);
    }

    public List<Map<String, Object>> selectByStudent(int sid) {
        String sql = "select se.*,stu.name sname,cou.name cname from selection se " +
                "left join student stu on se.student=stu.id " +
                "left join course cou on se.course=cou.id" +
                "where student=?";
        return jdbcTemplate.queryForList(sql,sid);
    }

    public List<Map<String, Object>> selectByCourse(int cid) {
        String sql = "select se.*,stu.name sname,cou.name cname from selection se " +
                "left join student stu on se.student=stu.id " +
                "left join course cou on se.course=cou.id" +
                "where course=?";
        return jdbcTemplate.queryForList(sql,cid);
    }
}

  1. 在Spring配置文件applicationContext中配置注解自动扫面
    <context:component-scan base-package="com.imooc.sc"/>

6、JDBC Template优缺点分析

优点:

  • 简单
  • 灵活

缺点:

  • SQL与Java代码参杂
  • 功能不丰富

7、课程总结

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

翘脚猴子耍把戏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值