MyBatis 框架的配置及使用详解

前言

MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。

在这里,通过写个几例子来说明。

配置文件

核心配置文件:SqlMapConfig.xml

<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--导入外部配置文件-->
    <properties resource="db.properties"/>

    <!--延迟加载-->
    <settings>
        <!--开启延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--关闭积极加载-->
        <setting name="aggressiveLazyLoading" value="false"/>

        <!--开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <!--别名配置-->
    <typeAliases>
        <package name="com.abc.entity"/>
    </typeAliases>

    <!--配置类型处理器-->
    <typeHandlers>
        <!--枚举处理器-->
        <!--注意:mybatis提供了两种不同的枚举处理器,默认情况下使用的是EnumTypeHandler(字符串)-->
        <!--此处我们修改默认枚举处理器,使用EnumOrdinalTypeHandler来操作,使用的是枚举下标-->
        <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.abc.entity.Gender"/>
    </typeHandlers>

    <!--mybatis插件配置-->
    <plugins>
        <!--pagehelper分页插件-->
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库-->
            <property name="helperDialect" value="mysql"/>
        </plugin>
    </plugins>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!-- 数据库连接池-->
            <dataSource type="POOLED">
                <!--使用p6spy探测SQL语句,替换原有驱动-->
                <!--<property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
                <property name="driver" value="${driver}"/>
                <!--并且修改URL-->
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--扫描实体包中所有的接口和xml映射文件-->
    <mappers>
        <package name="com.abc.mapper"/>
    </mappers>
</configuration>

其它用到的或要用的配置文件:
db.properties

driver=com.p6spy.engine.spy.P6SpyDriver
url=jdbc:p6spy:mysql:///study?serverTimezone=GMT%2B8
username=root
password=123456

ehcache.xml

<ehcache>
    <!--二级缓存存储路径-->
    <diskStore path="D:\java\ehcache"/>

    <!--
        maxElementsInMemory         内存中存储缓存对象的最大值
        maxElementsOnDisk           磁盘中存储缓存对象的最大值
        eternal                     缓存对象是否是永久的
        overflowToDisk              缓存对象是否允许被写到磁盘中
        timeToIdleSeconds           缓存对象最大空闲时间,超时将被销毁
        timeToLiveSeconds           缓存对象最长存活时间,超时将被销毁
    -->
    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="true"
            timeToIdleSeconds="120"
            timeToLiveSeconds="600"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>

    <!--可以配置多套缓存配置-->
    <!-- <cache name="myCache1"
         maxElementsInMemory="1000"
         eternal="false"
         overflowToDisk="false"
         timeToIdleSeconds="120"
         timeToLiveSeconds="600">
     </cache>-->
</ehcache>

log4j.properties

# 全局日志配置
log4j.rootLogger=WARN, stdout
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
# MyBatis 日志配置
log4j.logger.com.gem.mapper=DEBUG

spy.properties

driverlist=com.mysql.cj.jdbc.Driver
logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat
databaseDialectDateFormat=yyyy-MM-dd HH:mm:ss
appender=com.p6spy.engine.spy.appender.StdoutLogger

用到的依赖 jar包

<dependencies>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.5</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>

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

        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
            <version>3.9.1</version>
        </dependency>

        <dependency>
            <scope>compile</scope>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>

        <dependency>
            <scope>compile</scope>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.2.0</version>
        </dependency>

        <!--通用mapper-->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper</artifactId>
            <version>4.1.5</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

单表操作

mybatis_student 学生表

对应的实体类:
student 学生类

@Data
public class Student implements Serializable {
  private static final long serialVersionUID = 7557762079548382677L;
  private Long id;
  private String name;
  private LocalDate birthday;
  private Gender gender;
}

Gender 性别枚举类

public enum Gender {
  MALE,
  FEMALE;
}

持久层Mapper接口及XML实现

StudentMapper 接口

public interface StudentMapper {

  int insertStudent(Student student);

  int deleteStudent(Long id);

  int updateStudent(Student student);

  List<Student> selectStudentsByIds(@Param("ids") List<Long> ids);

  List<Student> selectStudentsByBirthday(
      @Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate);

  List<Student> selectStudentsByConditions(
      @Param("name") String name, @Param("gender") Gender gender);

  Student selectStudentById(Long id);

  List<Student> selectStudents();

  List<Student> selectStudentsByName(String name);
}

StudentMapper.xml 映射XML文件

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

<!--映射mapper接口-->
<!--namespace命名空间,要求值与对应的接口保持一致-->
<mapper namespace="com.abc.mapper.StudentMapper">

    <!--指定当前mapper开启二级缓存-->
    <!-- <cache/>-->

    <!--
        默认内置二级缓存参数配置:
        eviction        表示缓存策略,LRU,FIFO
        flushInterval   表示刷新间隔(毫秒)
        size            表示缓存容量(默认1024)
        readOnly        表示缓存只读
    -->
    <!-- <cache  eviction="FIFO"  flushInterval="60000"  size="512"  readOnly="true"/>-->

    <!--使用ehcache缓存-->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache">
        <!--<property name="" value=""/>-->
    </cache>

    <!--
        查询
        id              对应接口中的方法名
        resultType      对应查询结果类型
                        a.查询结果为集合时,它指向的是集合元素类型
                        b.默认情况下需要指定类的全限定名
                          可以在SqlMapConfig.xml中配置别名定义来简化
        parameterType   指向参数类型
    -->
    <select id="selectStudents" resultType="student">
        select * from mybatis_student
    </select>

    <!--parameterType参数类型可以省略-->
    <select id="selectStudentById" parameterType="long" resultType="student">
        select * from mybatis_student where id = #{id}
    </select>

    <select id="selectStudentsByBirthday" parameterType="java.time.LocalDate" resultType="student">
        select * from mybatis_student where birthday between #{startDate} and #{endDate}
    </select>

    <!--
        注意:
        通过$的方式拼接的sql语句,不再是以占位符的形式生成sql,而是以拼接字符串的方式生成sql,
        这样做带来的问题是:会引发sql注入的问题!
    -->
    <select id="selectStudentsByName" resultType="student">
        select * from mybatis_student where name like concat('%',#{name},'%')
    </select>

    <!--添加操作-->
    <insert id="insertStudent">
        insert into mybatis_student(name, birthday, gender)
        values (#{name}, #{birthday}, #{gender})
    </insert>

    <!--删除-->
    <delete id="deleteStudent">
        delete from mybatis_student where id = #{id}
    </delete>

    <!--更新-->
    <!--动态SQL语句-->
    <update id="updateStudent">
        update mybatis_student set id=#{id}
        <if test="name != null and name != ''">
            ,name=#{name}
        </if>
        <if test="birthday !=null">
            ,birthday=#{birthday}
        </if>
        <if test="gender !=null">
            ,gender=#{gender}
        </if>
        <where>
            id=#{id}
        </where>
    </update>

    <!--定义SQL片段-->
    <sql id="query_student_condition">
        <where>
            <if test="name!=null and name!=''">
                and name=concat('%',#{name},'%')
            </if>
            <if test="gender!=null">
                and gender=#{gender}
            </if>
        </where>
    </sql>

    <!--where标签可以提供where关键字,并且把条件第一个and自动去除-->
    <select id="selectStudentsByConditions" resultType="student">
        select * from mybatis_student
        <!--引用SQL片段-->
        <!--注意:如果想要引用其他xml文件中的SQL片段,必须指定全名(命名空间+sqlid)-->
        <include refid="query_student_condition"></include>
    </select>

    <!--
        动态SQL之循环语句foreach
        collection              表示迭代的集合或者数组
        open                    表示迭代开始的内容
        close                   表示迭代结束的内容
        item                    表示迭代的每一个项
        separator               表示迭代间隔符
    -->
    <select id="selectStudentsByIds" resultType="student">
        select * from mybatis_student
        <where>
            <if test="ids!=null and ids.size>0">
                <foreach collection="ids" open="id in(" close=")" item="id" separator=",">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>
</mapper>

把测试的基本步骤封装成 MybatisUtil工具类

/** Mybatis工具类 */
public class MybatisUtil {
  private static SqlSessionFactory sqlSessionFactory = null;
  // 本地线程变量,可以保证线程安全
  private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();

  static {
    try {
      // 加载mybatis核心配置文件,获取SqlSessionFactory连接工厂
      Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /** 返回SqlSessionFactory对象 */
  public static SqlSessionFactory getSqlSessionFactory() {
    return sqlSessionFactory;
  }

  /** 新建一个SqlSession对象,并且把它放入到线程变量中 */
  private static void newSqlSession() {
    // 通过连接工厂获取SqlSession连接对象
    // 注意:openSession参数为true表示该连接对象开启自动提交模式
    SqlSession sqlSession = sqlSessionFactory.openSession(true);
    threadLocal.set(sqlSession);
  }

  /** 获取SqlSession对象 */
  public static SqlSession getSqlSession() {
    // 优先从线程变量中获取SqlSession对象
    SqlSession sqlSession = threadLocal.get();
    // 判断,如果线程变量中没有SqlSession对象,则创建并且添加
    if (sqlSession == null) {
      newSqlSession();
      sqlSession = threadLocal.get();
    }
    return sqlSession;
  }

  /** 关闭SqlSession对象,并且从线程变量中删除 */
  public static void closeSqlSession() {
    // 获取线程变量中的SqlSession对象
    SqlSession sqlSession = threadLocal.get();
    // 判断
    if (sqlSession != null) {
      sqlSession.close();
      threadLocal.set(null);
    }
  }
}

StudentMapperTest 测试类

public class StudentMapperTest {

  SqlSession sqlSession;
  StudentMapper mapper;

  // 任何测试方法运行之前会优先执行该方法
  @Before
  public void setUp() {
    sqlSession = MybatisUtil.getSqlSession();
    // 获取Mapper接口的动态代理对象
    mapper = sqlSession.getMapper(StudentMapper.class);
  }

  // 任何测试方法运行之后会继续执行该方法
  @After
  public void tearDown() {
    MybatisUtil.closeSqlSession();
  }

  @Test
  public void insertStudent() {
    Student student = new Student();
    student.setName("jack");
    student.setBirthday(LocalDate.now());
    student.setGender(Gender.FEMALE);
    int result = mapper.insertStudent(student);
    System.out.println("添加成功,一共影响了: " + result + "行记录.");
  }
  @Test
  public void deleteStudent() {
    int result = mapper.deleteStudent(4L);
    System.out.println("删除成功,一共影响了: " + result + "行记录.");
  }
  @Test
  public void updateStudent() {
    Student student = mapper.selectStudentById(1L);
    student.setName("张三");
    int result = mapper.updateStudent(student);
    System.out.println("更新成功,一共影响了: " + result + "行记录.");
  }
  @Test
  public void selectStudentsByBirthday() {
    mapper
        .selectStudentsByBirthday(LocalDate.of(2020, 9, 10), LocalDate.of(2020, 9, 20))
        .forEach(System.out::println);
  }
  @Test
  public void selectStudentById() {
    System.out.println(mapper.selectStudentById(1L));
  }
  @Test
  public void selectStudents() {
    mapper.selectStudents().forEach(System.out::println);
  }
  @Test
  public void selectStudentsByName() {
    mapper.selectStudentsByName("o").forEach(System.out::println);
  }
  @Test
  public void selectStudentsByConditions() {
    mapper.selectStudentsByConditions(null, Gender.MALE).forEach(System.out::println);
  }
  @Test
  public void selectStudentsByIds() {
    mapper.selectStudentsByIds(List.of(1L, 3L, 2L)).forEach(System.out::println);
  }
}

多表级联操作(一对一)

一对一关系:丈夫和妻子

mybatis_husband 丈夫表
mybatis_wife 妻子表

对应的实体类:
Husband

@Data
public class Husband {
  private Long id;
  private String name;

  // 体现一对一关系
  private Wife wife;
}

Wife

@Data
public class Wife {
  private Long id;
  private String name;
}

持久层Mapper接口及XML实现

HusbandMapper 接口

public interface HusbandMapper {
  /** 根据姓名查询丈夫信息,如果该丈夫有妻子,则积极加载妻子的信息 */
  Husband selectHusbandAndWife(String name);

  /** 根据姓名查询丈夫信息,如果该丈夫有妻子,则延迟加载妻子的信息 */
  Husband selectHusbandAndWifeLazy(String name);

  Wife selectWifeById(Long id);
}

HusbandMapper.xml

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

<mapper namespace="com.abc.mapper.HusbandMapper">

    <!--定义resultmap-->
    <resultMap id="husbandAndWifeMap" type="Husband">
        <!--id封装-->
        <id property="id" column="id"/>
        <!--其他属性封装-->
        <result property="name" column="name"/>
        <!--关联绑定(一对一)-->
        <association property="wife" javaType="Wife">
            <!--绑定关联对象的属性-->
            <id property="id" column="wid"/>
            <result property="name" column="wname"/>
        </association>
    </resultMap>

    <select id="selectHusbandAndWife" resultMap="husbandAndWifeMap">
        select h.id, h.name, w.id wid, w.name wname
        from mybatis_husband h
                 left join mybatis_wife w on h.id = w.husband_id
        where h.name = #{name}
    </select>

    <!--延迟加载-->
    <resultMap id="husbandAndWifeLazyMap" type="Husband">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="wife" javaType="Wife" select="selectWifeById" column="id"/>
    </resultMap>

    <select id="selectHusbandAndWifeLazy" resultMap="husbandAndWifeLazyMap">
        select *
        from mybatis_husband
        where name = #{name}
    </select>

    <select id="selectWifeById" resultType="Wife">
        select *
        from mybatis_wife
        where husband_id = #{id}
    </select>
</mapper>

这里就不写测试类了。

多表级联操作(一对多)

一对多关系:顾客和订单

mybatis_customer 顾客表
mybatis_order 订单表

对应的实体类:
Customer 顾客类

@Data
public class Customer {
  private Long id;
  private String name;
  
  // 体现一对多关系
  private List<Order> orders;
}

Order 订单类

@Data
public class Order {
  private Long id;
  private String orderno;
  private Double price;
}

持久层Mapper接口及XML实现

CustomerMapper 接口

public interface CustomerMapper {

  /** 根据客户名查询客户,如果该客户有订单,则级联查询该客户的订单信息 */
  Customer selectCustomerAndOrders(String name);

  /** 添加客户,如果该客户有订单,则级联添加订单信息 */
  int insertCustomerAndOrders(Customer customer);
}

CustomerMapper.xml

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

<mapper namespace="com.abc.mapper.CustomerMapper">

    <resultMap id="customerAndOrdersMap" type="Customer">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <!--一对多关系绑定-->
        <collection property="orders" ofType="Order">
            <id property="id" column="oid"/>
            <result property="orderno" column="orderno"/>
            <result property="price" column="price"/>
        </collection>
    </resultMap>

    <select id="selectCustomerAndOrders" resultMap="customerAndOrdersMap">
        select c.id, c.name, o.id oid, o.orderno, o.price
        from mybatis_customer c
                 left join hbm_order o on c.id = o.customer_id
        where c.name = #{name}
    </select>

    <!--
        useGeneratedKeys用来把添加自动生成的id值赋值给keyProperty
   -->
    <insert id="insertCustomerAndOrders" useGeneratedKeys="true" keyProperty="id">
        insert into mybatis_customer (name)
        values (#{name})
    </insert>
</mapper>

CustomerMapperTest 测试类

public class CustomerMapperTest {

  SqlSession sqlSession;
  CustomerMapper mapper;

  @Before
  public void setUp() {
    sqlSession = MybatisUtil.getSqlSession();
    mapper = sqlSession.getMapper(CustomerMapper.class);
  }

  @After
  public void tearDown() {
    MybatisUtil.closeSqlSession();
  }

  @Test
  public void selectCustomerAndOrders() {
    System.out.println(mapper.selectCustomerAndOrders("张三"));
  }

  @Test
  public void insertCustomerAndOrders() {
    Customer customer = new Customer();
    customer.setName("jack");
    mapper.insertCustomerAndOrders(customer);
  }
}

补充:缓存测试

TestMybatisCache 测试类

public class TestMybatisCache {
    /** 测试Mybatis一级缓存
     * 一级缓存默认开启,无需配置
     * 它的作用域为单独的某一个SqlSession
     */
    @Test
    public void testFirstLevelCache() {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student s1 = mapper.selectStudentById(1L);
        System.out.println(s1);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        Student s2 = mapper.selectStudentById(1L);
        System.out.println(s2);
        System.out.println(s1 == s2);
        MybatisUtil.closeSqlSession();
    }

    /**
     * 测试Mybatis二级缓存
     * 二级缓存默认关闭,需要手动配置打开
     * 它的作用域是跨多个SqlSession的,即多个连接可以共享
     * 打开方式:
     * 1.全局配置文件开启总开关
     * 2.开启二级缓存局部小开关(哪个操作是需要缓存的)
     * 3.需要二级缓存的实体实现序列化接口
     *
     * 4.如果使用第三方缓存框架,则需要额外配置:
     *    4-1.导入第三方缓存jar包(此处我们使用ehcache),mybatis-ehcache
     *    4-2.导入缓存配置文件
     *    4-3.在映射文件中指定二级缓存的type类型
     */
    @Test
    public void testSecondLevelCache() {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student s1 = mapper.selectStudentById(1L);
        System.out.println(s1);
        MybatisUtil.closeSqlSession();

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        sqlSession = MybatisUtil.getSqlSession();
        mapper = sqlSession.getMapper(StudentMapper.class);
        Student s2 = mapper.selectStudentById(1L);
        System.out.println(s2);
        System.out.println(s1 == s2);
        MybatisUtil.closeSqlSession();
    }
}

一级缓存测试结果:

DEBUG [main] - Cache Hit Ratio [com.abc.mapper.StudentMapper]: 0.0
DEBUG [main] - ==>  Preparing: select * from mybatis_student where id = ?
DEBUG [main] - ==> Parameters: 1(Long)
1600950867935|128|statement|connection 0|url jdbc:p6spy:mysql:///study?serverTimezone=GMT%2B8|select * from mybatis_student where id = ?|select * from mybatis_student where id = 1
DEBUG [main] - <==      Total: 1
Student(id=1, name=张三, birthday=2020-09-16, gender=MALE)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DEBUG [main] - Cache Hit Ratio [com.abc.mapper.StudentMapper]: 0.0
Student(id=1, name=张三, birthday=2020-09-16, gender=MALE)
true

二级缓存测试结果:

DEBUG [main] - Cache Hit Ratio [com.abc.mapper.StudentMapper]: 0.0
DEBUG [main] - ==>  Preparing: select * from mybatis_student where id = ?
DEBUG [main] - ==> Parameters: 1(Long)
1600951052911|6|statement|connection 0|url jdbc:p6spy:mysql:///study?serverTimezone=GMT%2B8|select * from mybatis_student where id = ?|select * from mybatis_student where id = 1
DEBUG [main] - <==      Total: 1
Student(id=1, name=张三, birthday=2020-09-16, gender=MALE)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DEBUG [main] - Cache Hit Ratio [com.abc.mapper.StudentMapper]: 0.5
Student(id=1, name=张三, birthday=2020-09-16, gender=MALE)
true
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

toollong

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

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

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

打赏作者

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

抵扣说明:

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

余额充值