使用MyBatisPlus的逆向工程自动生成Mapper接口,xml文件,service接口,service实现以及controller并整合Spring框架完成单表CUID和分页操作

使用Idea+MyBatisPlus+SpringMVC完成单表操作及分页功能

1. MyBatisPlus简介

Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

1.1. 特性

  • 无侵入: Mybatis-Plus 在 Mybatis 的基础上进行扩展,只做增强不做改变,引入 Mybatis-Plus 不会对您现有的 Mybatis 构架产生任何影响,而且 MP 支持所有 Mybatis 原生的特性
  • **依赖少:**仅仅依赖 Mybatis 以及 Mybatis-Spring
  • **损耗小:**启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • **预防Sql注入:**内置 Sql 注入剥离器,有效预防Sql注入攻击
  • **通用CRUD操作:**内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • **多种主键策略:**支持多达4种主键策略(内含分布式唯一ID生成器),可自由配置,完美解决主键问题
  • **支持热加载:**Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动
  • **支持ActiveRecord:**支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可实现基本 CRUD 操作
  • **支持代码生成:**采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用(P.S. 比 Mybatis 官方的 Generator 更加强大!)
    支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • **支持关键词自动转义:**支持数据库关键词(order、key…)自动转义,还可自定义关键词
  • **内置分页插件:**基于 Mybatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通List查询
  • **内置性能分析插件:**可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能有效解决慢查询
  • **内置全局拦截插件:**提供全表 delete 、 update 操作智能分析阻断,预防误操作

1.2. 架构原理

这里写图片描述

2.入门案例

2.1 使用MyBatis-Plus,需要加入两个jar包,如下:

<!--mybatisplus-->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus</artifactId>
  <version>${mybatisplus.version}</version>
</dependency>

<!--模板引擎-->
<dependency>
  <groupId>org.apache.velocity</groupId>
  <artifactId>velocity-engine-core</artifactId>
  <version>${velocity.version}</version>
</dependency>

需要注意的地方是,使用Maven加入MyBatis-Plus的jar包,则不需要在配置MyBatis和MyBatis-Spring整合包了,这些MyBatis-Plus会自动维护,避免出现jar冲突。

2.2 整合Spring框架的核心配置

<!--sqlSessionFacotry-->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
    <!-- MP 全局配置注入 -->
    <property name="globalConfig" ref="globalConfig"/>
    <property name="plugins">
        <array>
            <!-- 分页插件配置 -->
            <bean id="paginationInterceptor" class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"/>
            <!-- 乐观锁插件 -->
            <bean id="optimisticLockerInterceptor" class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor">
            </bean>
            <!-- 性能拦截器,兼打印sql,不建议生产环境配置-->
            <bean id="performanceInterceptor" class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor"/>
        </array>
    </property>
</bean>

<!-- 定义 MP 全局策略 -->
<bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
    <!-- 全局ID类型: 0, "数据库ID自增", 1, "用户输入ID", 2, "全局唯一ID", 3, "全局唯一ID"-->
    <property name="idType" value="3"/>
    <!--主键Sequence-->
    <property name="keyGenerator" ref="keyGenerator"/>
    <!-- 全局表为下划线命名设置 true -->
    <property name="dbColumnUnderline" value="true" />
</bean>

<!-- 配置oracle主键Sequence, 其他类型数据库,请配置相应的类型-->
<bean id="keyGenerator" class="com.baomidou.mybatisplus.incrementer.OracleKeyGenerator"/>


<!-- 配置mybatis 扫描mapper接口的路径, 相当于注解@MapperScan,@MapperScan("com.baomidou.mybatisplus.test.h2.entity.mapper")-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.zt.mapper"/>
</bean>

#3. 使用MyBatisPlus完成对单张表(Oracle自带的Dept表)的操作
##3.1使用Idea创建一个简单的MavenWeb项目
创建Maven项目,jdk选择1.8版本
输入项目名称

具体步骤可以参考我之前的博客JAVA开发使用SSM框架入门案例
##3.2 配置Maven 的pom.xml 所需的jar

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.zt</groupId>
  <artifactId>mybatisplus-springmvc</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>mybatisplus-springmvc Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <properties>
    <spring.version>5.0.4.RELEASE</spring.version>
    <aspectJ.version>1.8.13</aspectJ.version>
    <mybatisplus.version>2.2.0</mybatisplus.version>
    <oracle.version>11.2.0.1.0</oracle.version>
    <log4j.version>1.2.17</log4j.version>
    <slf4j.version>1.7.25</slf4j.version>
    <c3p0.version>0.9.5.2</c3p0.version>
    <servlet.version>3.1.0</servlet.version>
    <jstl.version>1.2.5</jstl.version>
    <taglibs.version>1.2.5</taglibs.version>
    <jackson.version>2.9.4</jackson.version>
    <commons.lang3.version>3.7</commons.lang3.version>
    <velocity.version>2.0</velocity.version>
  </properties>

  <dependencies>
    <!--spring 基础包-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!--spring 事务包  jdbc,tx-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring.version}</version>
    </dependency>


    <!--spring web mvc 模块-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>


    <!--spring整合的测试包-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>

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

    <!--AespectJ -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>${aspectJ.version}</version>
    </dependency>

    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>${aspectJ.version}</version>
    </dependency>

    <!--mybatisplus,加入该jar后,就不再需要mybatis-xxx.jar,mybatis-spring-xxx.jar了,它们会以依赖包的形式被自动维护-->
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus</artifactId>
      <version>${mybatisplus.version}</version>
    </dependency>

    <!--模板引擎-->
    <dependency>
      <groupId>org.apache.velocity</groupId>
      <artifactId>velocity-engine-core</artifactId>
      <version>${velocity.version}</version>
    </dependency>

    <!--oracle-->
    <dependency>
      <groupId>com.oracle</groupId>
      <artifactId>ojdbc6</artifactId>
      <version>${oracle.version}</version>
    </dependency>

    <!--c3p0数据源-->
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>${c3p0.version}</version>
    </dependency>

    <!--日志-->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>${log4j.version}</version>
    </dependency>
    
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>${slf4j.version}</version>
    </dependency>

    <!--servlet-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>${servlet.version}</version>
      <scope>provided</scope>
    </dependency>

    <!--jstl-->
    <dependency>
      <groupId>org.apache.taglibs</groupId>
      <artifactId>taglibs-standard-jstlel</artifactId>
      <version>${jstl.version}</version>
    </dependency>

    <!--json数据转换包-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
    </dependency>

    <!--cocommons-lang3-->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>${commons.lang3.version}</version>
    </dependency>

  </dependencies>
  <build>
    <finalName>mybatisplus-springmvc</finalName>
    <plugins>
    <!--配置maven的编译环境,不然mavn模版默认会使用jdk1.5版本-->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.5.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

项目jar包部门示例图:

pom.xml文件配置生成的jar目录

2.3 逆向工程的执行文件完成包路径和数据库的连接配置

在项目工程中
● 创建一个src/main/java/genterator目录,并设置java目录为源码目录, ● 创建一个src/test/resources目录,并设置resources目录为测试源码目录, 在genterator目录下新建一个GenteratorCode.java文件

项目目录架构

GenteratorCode.java文件配置如下:

package genterator;

import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DbType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

import java.util.*;

/**
 * Created by CDHong on 2018/4/6.
 */
public class GenteratorCode {

    public static void main(String[] args) throws InterruptedException {
        //用来获取mybatis-plus.properties文件的配置信息
        final ResourceBundle rb = ResourceBundle.getBundle("mybatis-plus");
        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(rb.getString("OutputDir"));
        gc.setFileOverride(true);
        gc.setActiveRecord(true);
        gc.setEnableCache(false);// XML 二级缓存
        gc.setBaseResultMap(true);// XML ResultMap
        gc.setBaseColumnList(true);// XML columList
        gc.setAuthor(rb.getString("author"));
        // 自定义文件命名,注意 %s 会自动填充表实体属性!
        gc.setMapperName("%sMapper");
        gc.setXmlName("%sMapper");
        gc.setServiceName("I%sService");
        gc.setServiceImplName("%sServiceImpl");
        gc.setControllerName("%sController");
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.ORACLE);
        /*dsc.setTypeConvert(new MySqlTypeConvert(){
            // 自定义数据库表字段类型转换【可选】
            @Override
            public DbColumnType processTypeConvert(String fieldType) {
                System.out.println("转换类型:" + fieldType);
                return super.processTypeConvert(fieldType);
            }
        });*/
        dsc.setDriverName(rb.getString("oracle.driver"));
        dsc.setUrl(rb.getString("oracle.url"));
        dsc.setUsername(rb.getString("oracle.user"));
        dsc.setPassword(rb.getString("oracle.pwd"));
        mpg.setDataSource(dsc);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        // strategy.setCapitalMode(true);// 全局大写命名 ORACLE 注意
        //strategy.setTablePrefix(new String[] { "SYS_" });// 此处可以修改为您的表前缀
        strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
        strategy.setInclude(new String[] {"EMP","DEPT"}); // 需要生成的表
        //strategy.setExclude(new String[]{"test"}); // 排除生成的表
        mpg.setStrategy(strategy);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent(rb.getString("parent"));
        // pc.setModuleName("tbldept");//模块名称,单独生成模块时使用!!!!!!!!!!!
        pc.setController("controller");
        pc.setService("service");
        pc.setServiceImpl("service.impl");
        pc.setEntity("entity");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

        // 注入自定义配置,可以在 VM 中使用 cfg.abc 【可无】
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-rb");
                this.setMap(map);
            }
        };

        // 调整 xml 生成目录演示
        List<FileOutConfig> focList = new ArrayList<FileOutConfig>();
        focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return rb.getString("OutputDirXml")+ "/mybatis/mappers/" + tableInfo.getEntityName() + "Mapper.xml";
            }
        });
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 关闭默认 xml 生成,调整生成 至 根目录
        TemplateConfig tc = new TemplateConfig();
        tc.setXml(null);
        mpg.setTemplate(tc);

        // 执行生成
        mpg.execute();
    }
}

```xml

> 文件开始引入了一个外部属性配置文件:
>**mybatis-plus.properties:**该文件主要配置要生成的包文件所对应的路径,注释的作者说明,数据库连接等...
>**mybatis-plus.properties** 属性配置文件文件存放于src/test/resources目录下,文件信息如下:  

#此处为本项目src所在路径(代码生成器输出路径),注意一定是当前项目所在的目录哟
OutputDir=D:\IdeaProjects\mybatisplus-springmvc\src\main\java
#mapper.xml SQL映射文件目录
OutputDirXml=D:\IdeaProjects\mybatisplus-springmvc\src\main\resources
#设置作者
author=CDHong
#自定义包路径
parent=com.zt

#数据库连接信息
oracle.driver=oracle.jdbc.driver.OracleDriver
oracle.url=jdbc:oracle:thin:@localhost:1521:orcl
oracle.user=scott
oracle.pwd=tiger
``

2.4生成项目所需文件

到此逆向工程配置完毕,检查自己数据库是否开启, 对应的数据表是否存在,resouces配置文件信息是否正确,在GenteratorCode.java文件中的引用是否正确,确认无误后,就可以运行GerteratorCode.java文件中的main方法自动生成代码了,执行结果如下:

逆向工程生成目录

Controller文件信息

package com.zt.controller;


import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.stereotype.Controller;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author CDHong
 * @since 2018-04-06
 */
@Controller
@RequestMapping("/dept")
public class DeptController {

}

Entity实体文件信息,通过下面生成的代码我们也可以看出,最好数据库中表和字段的命名使用下划线的形式来完成,不然生成的代码不能实现骆驼命令方式。

package com.zt.entity;

import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.activerecord.Model;
import java.io.Serializable;

/**
 * <p>
 * 
 * </p>
 *
 * @author CDHong
 * @since 2018-04-06
 */
public class Dept extends Model<Dept> {

    private static final long serialVersionUID = 1L;

    @TableId("DEPTNO")
    private Integer deptno;
    @TableField("DNAME")
    private String dname;
    @TableField("LOC")
    private String loc;


    public Integer getDeptno() {
        return deptno;
    }

    public void setDeptno(Integer deptno) {
        this.deptno = deptno;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public String getLoc() {
        return loc;
    }

    public void setLoc(String loc) {
        this.loc = loc;
    }

    @Override
    protected Serializable pkVal() {
        return this.deptno;
    }

    @Override
    public String toString() {
        return "Dept{" +
        ", deptno=" + deptno +
        ", dname=" + dname +
        ", loc=" + loc +
        "}";
    }
}

Mapper接口文件信息

package com.zt.mapper;

import com.zt.entity.Dept;
import com.baomidou.mybatisplus.mapper.BaseMapper;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author CDHong
 * @since 2018-04-06
 */
public interface DeptMapper extends BaseMapper<Dept> {

}

Service接口文件信息

package com.zt.service;

import com.zt.entity.Dept;
import com.baomidou.mybatisplus.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author CDHong
 * @since 2018-04-06
 */
public interface IDeptService extends IService<Dept> {

}

Service实现类文件信息

package com.zt.service.impl;

import com.zt.entity.Dept;
import com.zt.mapper.DeptMapper;
import com.zt.service.IDeptService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author CDHong
 * @since 2018-04-06
 */
@Service
public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements IDeptService {

}

Maper.xml SQL映射文件信息

<?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.zt.mapper.DeptMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.zt.entity.Dept">
        <id column="DEPTNO" property="deptno" />
        <result column="DNAME" property="dname" />
        <result column="LOC" property="loc" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        DEPTNO AS deptno, DNAME AS dname, LOC AS loc
    </sql>

</mapper>

到此项目的逆向工程就介绍完毕,接下来我们就是用生成的这些文件来完成我们的单表操作和分页啦!

3.整合Spring+MyBatisPlus框架

整合Spring框架完成Bean管理,方便对象创建操作,当然MybatisPlus框架是需要Spring的,不然很多东西完成起来比较麻烦.框架整合,推荐使用xml配置的形式来完成,在这里只需要整合Spring和MyBatisPlus框架,web层使用Spring的Web模块来完成。
在src/main/resources目录下新建一个spring目录,用于存放Spring框架的配置文件。

3.1持久层(spring+mybatisplus)配置

在src/main/resouces/spring中新建两个spring的配置文件: ● spring-mybatis-plus.xml 用于持久层的配置(dao,service,数据源,事务...) ● spring-mvc.xml 用于视图显示和控制的配置(controller,exception,上传下载...)
先配置spring-mybatis-plus.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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--加载c3p0.properties-->
    <context:property-placeholder location="classpath:datasource.properties" />

    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
        <property name="driverClass" value="${oracle.driver}"/>
        <property name="jdbcUrl" value="${oracle.url}"/>
        <property name="user" value="${oracle.user}"/>
        <property name="password" value="${oracle.pwd}"/>

        <property name="initialPoolSize" value="${c3p0.initPoolSize}"/>
        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
        <property name="minPoolSize" value="${c3p0.minPoolSize}"/>
        <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>
        <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>
        <property name="checkoutTimeout" value="${c3p0.checkoutTimeout}"/>
    </bean>

    <!--sqlSessionFacotry-->
    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
        <!-- MP 全局配置注入 -->
        <property name="globalConfig" ref="globalConfig"/>
        <property name="plugins">
            <array>
                <!-- 分页插件配置 -->
                <bean id="paginationInterceptor" class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"/>
                <!-- 乐观锁插件 -->
                <bean id="optimisticLockerInterceptor" class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor">
                </bean>
                <!-- 性能拦截器,兼打印sql,不建议生产环境配置-->
                <bean id="performanceInterceptor" class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor"/>
            </array>
        </property>
    </bean>

    <!-- 定义 MP 全局策略 -->
    <bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
        <!-- 全局ID类型: 0, "数据库ID自增", 1, "用户输入ID", 2, "全局唯一ID", 3, "全局唯一ID"-->
        <property name="idType" value="3"/>
        <!--主键Sequence-->
        <property name="keyGenerator" ref="keyGenerator"/>
        <!-- 全局表为下划线命名设置 true -->
        <property name="dbColumnUnderline" value="true" />
    </bean>

    <!-- 配置oracle主键Sequence, 其他类型数据库,请配置相应的类型-->
    <bean id="keyGenerator" class="com.baomidou.mybatisplus.incrementer.OracleKeyGenerator"/>


    <!-- 配置mybatis 扫描mapper接口的路径, 相当于注解@MapperScan,@MapperScan("com.baomidou.mybatisplus.test.h2.entity.mapper")-->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.zt.mapper"/>
    </bean>

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

    <!--使用注解完成事务管理,注册-->
    <tx:annotation-driven />

    <!--把DAO交给Service-->
    <context:component-scan base-package="com.zt.service.impl" />

</beans>

在该配置文件中,需要引入两个属性配置文件:
● datasource.properties 配置文件:放置于src/main/resouces/目录下,主要用于数据库连接和C3P0数据源的常用配置。
● mybatis-config.xml 配置文件:放置于src/main/resouces/mybatis/目录下,主要用于mybatis的常用配置(别名,缓存,懒加载,骆驼命名等…),主要还是为了和spring的配置区分开,不要全部都在一个文件中去配置。
● 如果要查看项目的运行流程和数据库执行SQL,还需要加入一个配置文件。log4j.properties.注意这个文件需放在src/mian/resouces下且文件名也不能更改,当然想更改也可以,只是要加入一些配置去做修改,这里就不详细说了。

目录结构如下:
项目目录结构图

datasource.properties属性配置文件信息如下

oracle.driver=oracle.jdbc.driver.OracleDriver
oracle.url=jdbc:oracle:thin:@localhost:1521:orcl
oracle.user=scott
oracle.pwd=tiger

c3p0.initPoolSize = 5
c3p0.maxPoolSize =  500
c3p0.minPoolSize = 5
c3p0.acquireIncrement  = 10
c3p0.maxIdleTime  = 60000
c3p0.checkoutTimeout  = 60000

mybatis-config.xml属性配置文件信息如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <!--Mybaits的日志操作,使用log4j完成,这样避免sql语句无法打印-->
        <setting name="logImpl" value="log4j"/>
        <!--开启骆驼命名-->
        <setting name="mapUnderscoreToCamelCase" value="true" />
    </settings>

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

</configuration>

log4j.properties属性配置文件信息如下:

### Global logging configuration
log4j.rootLogger=Debug, stdout

### Uncomment for MyBatis logging
log4j.logger.org.apache.ibatis=ERROR

### Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

log4j.appender.lastEventSavedAppender=org.apache.ibatis.session.AutoMappingUnknownColumnBehaviorTest$LastEventSavedAppender

至此,持久层的所有配置都完毕了,接下来就是使用Junit来完成测试了。

3.2使用junit4完成单表CUID及分页测试

在src/test/java目录下新建一个新目录junit,在该目录下新建一个测试类DeptTest.java,用于完成测试操作。

package junit;

import com.zt.entity.Dept;
import com.zt.service.IDeptService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

/**
 * Created by CDHong on 2018/4/6.
 */
@ContextConfiguration("classpath:spring/spring-mybatis-plus.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class DeptTest {

    @Autowired private IDeptService deptService;

    /**
     * 在做保存的时候,可以自己手动添加主键,也可以使用序列来完成,
     * 相应的配置需要在spring-mybatis-plus.xml配置文件中找到以下两句配置
     * <property name="idType" value="2"/>
     *  <property name="keyGenerator" ref="keyGenerator"/>
     * 如果是手动添加主键,则idType的value值改为1,keyGenerator不需要配置
     * 如果是使用序列,则使用默认配置,除此之外需要在对应的实体类上添加如下注解:
     * @KeySequence(value = "seq_dept",clazz = Integer.class)
     * value:是要使用的序列名称  clazz是接受的值类型,默认是Long类型,这个结合自己实体字段类型来更改
     */
    @Test
    public void testSave(){
        Dept dept = new Dept();
        dept.setDname("科技部");
        dept.setLoc("重庆");
        boolean flg = deptService.insert(dept);
        System.out.println(flg);
    }

    @Test
    public void testUpdate(){
        //先查询部门,再根据需求修改
        Dept dept = deptService.selectById(63);
        //设置要修改的信息
        dept.setDname("市场部");
        dept.setLoc("渝北");
        boolean flg = deptService.updateById(dept);
        System.out.println(flg);
    }

    @Test
    public void testFindAll(){
        List<Dept> deptList = deptService.selectList(null);
        for(Dept dept:deptList){
            System.out.println(dept);
        }
    }

    @Test
    public void testDel(){
        boolean flg = deptService.deleteById(62);
        System.out.println(flg);
    }
    
     @Test
    public void testPageInfo(){
        //Page(pageIndex,pageSize)
        Page page = new Page(1,2);
        //查询条件实体
        EntityWrapper<Dept> ew = new EntityWrapper<>();
        ew.like("dname","A").orderBy("deptno");
        Page<Dept> deptPage = deptService.selectPage(page, ew);
        System.out.println("总页数:"+deptPage.getTotal());
        System.out.println("当前页码:"+deptPage.getCurrent());
        System.out.println("每页显示条数:"+deptPage.getSize());
        System.out.println("当前页数据:"+deptPage.getRecords());
    }

}

这里重点说一下保存操作,Oracle数据库的数据新增,没有自增操作,想要实现自增,需要借助序列来完成,所以在做添加操作的时候,需要指定是用序列还是手动输入,通过配置来指定,除此之外还需要在对应的实体上用注解指定要使用的序列名称。
实体注解,指定序列和类型:如下配置:
Dept实体指定保存使用的序列配置
执行保存操作打印的SQL信息:
执行保存操作
执行分页操作打印的SQL信息:
执行分页操作

3.3视图层(springmvc)配置

完成视图解析配置,静态资源处理以及字符编码设置.在src/main/resources/spring目录下,找到spring-mvc.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:mvc="http://www.springframework.org/schema/mvc"
       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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.zt.controller" />

    <!-- 编码配置 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                    <property name="supportedMediaTypes">
                        <list>
                            <value>text/plain;charset=UTF-8</value>
                            <value>text/html;charset=UTF-8</value>
                            <value>application/json;charset=UTF-8</value>
                        </list>
                    </property>
                </bean>
            </list>
        </property>
    </bean>

    <mvc:annotation-driven />

    <!---静态资源处理-->
    <mvc:resources mapping="/resources/**" location="/WEB-INF/resources/" />

    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/" />
        <property name="suffix" value=".jsp" />
    </bean>

</beans>

以上配置需要添加两个文件用于项目架构完善:resources(静态资源文件:css,js,image,前端框架等),pages(前端用于显示的静态页面:jsp,html,前端模版等),两个文件当存放与外界不可访问的安全目录WEB-INF目录下。

3.4配置web.xml文件启动配置

配置web.xml,因为maven模板自动生成的web.xml版本较低(2.3)。我们需要通过idea工具重新生成一个版本较高的文件(3.1),操作如图所示:
这里写图片描述
这里写图片描述
生成完毕后,在web.xml加入如下代码,完成请求字符编码处理(spring提供的过滤器),持久层配置监听启动和前端MVC中央处理器设置以及欢迎界面设置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!--字符编码过滤器-->
    <filter>
        <filter-name>EncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>EncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <!--加载Spring IOC容器-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/spring-mybatis-plus.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--加载spring mvc 模块-->
    <servlet>
        <servlet-name>spring-mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/spring-mvc.xml</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>spring-mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>/WEB-INF/pages/index.jsp</welcome-file>
    </welcome-file-list>

</web-app>

3.5jsp编写和数据显示

以上设置完毕后就是和浏览器做交互了,先在resources目录下引入我们需要的用到的前端框架layui,以及脚本文件js,并在js目录下创建一个dept.js备用,然后在pages目录下新建一个index.jsp做web.xml中配置的欢迎界面,再新建一个dept文件夹,用于存储dept操作所需的页面。并在其中新建一个list.jsp用于后端的响应页面。
在index.jsp中录入一个超链接用于访问后端控制器,测试是否可以正常启动访问

**index.jsp页面**
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<html>
<head>
    <base href="<%=basePath%>"/>
    <title>主界面</title>
</head>
<body>
    <a href="dept/index">显示部门信息</a>
</body>
</html>

list.jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<html>
<head>
    <base href="<%=basePath%>" />
    <title>部门管理</title>
</head>
<body>
     部门管理列表
</body>
</html>

接着在系统提供的DeptController.java文件中创建一个方法接收index.jsp的请求

package com.zt.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author CDHong
 * @since 2018-04-06
 */
@Controller
@RequestMapping("/dept")
public class DeptController {

    @RequestMapping(value = "/index",method = RequestMethod.GET)
    public String index(){
        return "dept/list";
    }

}

接下来配置我们的Web服务器Tomcat,部署项目,完成测试。配置Tomcat和项目部署可以参考:JAVA开发使用SSM框架入门案例
部署完毕后,启动服务器,打开浏览器输入请求地址:localhost,可以看到如下界面表示没有问题。
这里写图片描述当然点击连接也可以访问list.jsp页面。

3.6部门表CUID功能实现

剩下的就是部门表的添加,删除,修改以及分页查询,当然这些功能方法,Mybatis-Plus都帮我们完成了,我们只需要实现相应的逻辑和页面的编写即可,这里使用我们的页面是使用Layui框架来完成的,具体用法请参考经典模块化前端框架
逻辑就不说了,我直接贴一下控制器代码和jsp,js代码
目录架构

这里写图片描述

JSP页面:list.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<html>
<head>
    <base href="<%=basePath%>" />
    <title>部门管理</title>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <link rel="stylesheet" href="resources/layui/css/layui.css">
</head>
<body>
    <blockquote class="layui-elem-quote">
        <form action="" class="layui-form">
            <div class="layui-input-inline">
                <input type="text" name="search" required lay-verify="required" placeholder="请输入查询条件" autocomplete="off" class="layui-input"/>
            </div>
            <div class="layui-input-inline">
                <a href="javascript:;" class="layui-btn" lay-submit lay-filter="search-btn">查询</a>
                <a href="javascript:;" class="layui-btn layui-btn-normal add-btn">添加部门</a>
            </div>
        </form>
    </blockquote>

    <table id="deptList" lay-filter="dept-list"></table>

    <script type="text/javascript" src="resources/layui/layui.js"></script>
    <script type="text/javascript" src="resources/js/dept.js" ></script>
    <script type="text/html" id="dept-tool">
        <a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
        <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
    </script>
</body>
</html>

add-edit.jsp页面,这是添加和编辑公用页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<html>
<head>
    <base href="<%=basePath%>"/>
    <title>添加部门</title>
    <link rel="stylesheet" href="resources/layui/css/layui.css">
    <style>
        .sys-add-edit{ padding-top: 25px;text-align: center; }
        .sys-input-size{width: 380px}
    </style>
</head>
<body class="layui-container sys-add-edit">
    <form class="layui-form">
        <c:if test="${!empty dept.deptno}">
            <div class="layui-form-item">
                <div class="layui-form-label">部门编号:</div>
                <div class="layui-input-block">
                    <input class="layui-input layui-disabled sys-input-size" name="deptno" readonly value="${dept.deptno}" />
                </div>
            </div>
        </c:if>
        <div class="layui-form-item">
            <div class="layui-form-label">部门名称:</div>
            <div class="layui-input-block">
                 <input class="layui-input sys-input-size" placeholder="请输入部门名称" required lay-verify="required" name="dname" value="${dept.dname}" />
            </div>
        </div>
        <div class="layui-form-item">
            <div class="layui-form-label">部门地址:</div>
            <div class="layui-input-block">
                <input class="layui-input sys-input-size" placeholder="请输入部门地址" required lay-verify="required" name="loc" value="${dept.loc}" />
            </div>
        </div>
        <div class="layui-form-item">
            <c:choose>
                <c:when test="${!empty dept.deptno}">
                    <button class="layui-btn" lay-submit lay-filter="edit-submit">修改</button>
                </c:when>
                <c:otherwise>
                    <button class="layui-btn" lay-submit lay-filter="add-submit">添加</button>
                </c:otherwise>
            </c:choose>
            <input type="reset" class="layui-btn layui-btn-normal" value="重置" />
        </div>
    </form>

    <script type="text/javascript" src="resources/layui/layui.js"></script>
    <script type="text/javascript" src="resources/js/dept.js" ></script>
</body>
</html>

对应以上两个页面的js代码:js/dept.js

/**
 * Created by CDHong on 2018/4/7.
 */
layui.use(["form","layer","jquery","table"],function(){
    var form = layui.form,
        layer = layui.layer,
        $ = layui.jquery,
        table = layui.table;

    //设置全局默认参数
    table.set({
        elem:"#deptList",
        url:"dept/list",
        method:"post",
        //even:true,  //隔行变色
        limits:[10,15,20],
        height: 'full-150',
        page:{ limit:10}, //开启分页,并设置每页显示的条数
        cols:[[
            {checkbox: true},
            {field: 'deptno', title: '部门编号', width:180, sort: true, align: 'center'},
            {field: 'dname', title: '部门名称', sort: true},
            {field: 'loc', title: '部门地址'},
            {fixed: 'right',title: '操作', width:150, align:'center', toolbar: '#dept-tool'}
        ]],
        text:{
            none:'没有查询到符合条件的数据'
        }
    });

    //初始化表格数据
    var rootTable = table.render();

    //查询
    form.on("submit(search-btn)",function(data){
        table.render({
            where:data.field
        });
        return false;
    });

    //编辑和修改通用弹出层
    function layerOpen(title,param){
        layer.open({
            type:2,
            title:title,
            content:"dept/add-edit/"+param,
            skin:"layui-layer-molv",
            area: ['600px', '300px'],
            anim: 1,
            cancel: function(){
                table.render();
            }
        });
    }
    //添加部门弹出层
    $(".add-btn").click(function(){
        layerOpen("添加部门",-1);
    });

    //删除,添加和编辑的通用方法
    function sendAjax(url,info){
        $.ajax({
            url:url,
            method:"post",
            data:info,
            success:function(){
                layer.msg("执行成功!");
                //var index = parent.layer.getFrameIndex(window.name); //先得到当前iframe层的索引
                //parent.layer.close(index); //再执行关闭
            },
            error:function(){
                layer.msg("执行失败!");
            }
        });
    }
    //添加部门
    form.on('submit(add-submit)',function(data){
        sendAjax("dept/add-edit",data.field);
        return false;
    });

    //修改部门
    form.on("submit(edit-submit)",function(data){
        sendAjax("dept/add-edit",data.field);
        return false;

    });

    //监听工具条
    table.on('tool(dept-list)',function(obj){
        var lay_event = obj.event; //获得 lay-event 对应的值
        var data = obj.data;  //获得当前行数据
        //var tr = obj.tr; //获得当前行 tr 的DOM对象
        if(lay_event == 'edit'){
            layerOpen("编辑部门",data.deptno);
        }else if(lay_event == 'del'){
           layer.confirm('你确定要删除【'+data.dname+'】部门吗?',{icon:3,title:"删除提示",skin:"layui-layer-molv"},function(index){
               sendAjax("dept/del/"+data.deptno);
               layer.close(index);
               table.render();
            });
        }
    });

});

控制器代码:DeptController.java

package com.zt.controller;


import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zt.entity.Dept;
import com.zt.entity.vo.LayerJson;
import com.zt.service.IDeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author CDHong
 * @since 2018-04-06
 */
@Controller
@RequestMapping("/dept")
public class DeptController {

    @Autowired private IDeptService deptService;

    @RequestMapping(value = "/add-edit/{id}",method = RequestMethod.GET)
    public String addOrEdit(@PathVariable Integer id,Model model){
        if(id!=-1){
            Dept dept = deptService.selectById(id);
            model.addAttribute("dept",dept);
        }
        return "dept/add-edit";
    }

    @RequestMapping(value = "/index",method = RequestMethod.GET)
    public String index(){
        return "dept/list";
    }

    /**
     * 使用Layui table完成分页功能
     * @param pageIndex Layui table 默认提交当前页码的key 是page
     * @param pageSize Layui table 默认提交每页显示条数的key 是limit
     * @param search  查询条件
     * @return  返回自己组装符合Layui table格式的Json数据
     * @throws JsonProcessingException
     */
    @ResponseBody
    @RequestMapping(value = "/list",method = RequestMethod.POST)
    public String list(@RequestParam(value = "page",defaultValue = "1") Integer pageIndex, @RequestParam(value = "limit" , defaultValue = "10") Integer pageSize, String search) throws JsonProcessingException {
        //查询页码和每页显示的条数
        Page page = new Page(pageIndex,pageSize);
        //查询条件
        EntityWrapper<Dept> ew = new EntityWrapper<>();
        ew.orderBy("deptno").or().like("deptno",search).or().like("dname",search).or().like("loc",search);

        //根据查询条件查询符合的数据
        Page<Dept> mapPage = deptService.selectPage(page, ew);

        //这里需要自定义LayerJson类来组装JSON对象数据,主要是前端是用了Layui的table模块,该模块的请求数据需要对应的格式,
        //在这里我们建立一个符合格式的vo类来完成
        LayerJson layerJson = LayerJson.getInstance(mapPage.getRecords(),mapPage.getTotal());
        //转为JSON字符串
        return new ObjectMapper().writeValueAsString(layerJson);
    }

    @RequestMapping(value = "/add-edit",method = RequestMethod.POST)
    public ResponseEntity addOrEdit(Dept dept){
        boolean flg = deptService.insertOrUpdate(dept);
        if(flg){
            return new ResponseEntity(HttpStatus.OK);
        }
        return new ResponseEntity(HttpStatus.BAD_REQUEST);
    }

    @RequestMapping(value = "/del/{id}",method = RequestMethod.POST)
    public ResponseEntity del(@PathVariable Integer id){
        boolean flg = deptService.deleteById(id);
        if(flg){
            return new ResponseEntity(HttpStatus.OK);
        }
        return new ResponseEntity(HttpStatus.BAD_REQUEST);
    }

}

LayerJson类,用于转化和Layui table模块对接的数据格式,代码如下:

package com.zt.entity.vo;

import com.fasterxml.jackson.annotation.JsonInclude;
import org.springframework.http.HttpStatus;

/**
 * Created by CDHong on 2018/4/7.
 */
@JsonInclude(JsonInclude.Include.NON_NULL)
public class LayerJson {
    private Integer code;     //状态码
    private Object data;  //返回的数据
    private long count;    //总条数

    public LayerJson(Object data, long count) {
        this.code = 0; //layer table 默认正常返回状态码
        this.data = data;
        this.count = count;
    }

    public static LayerJson getInstance(Object data, long count){
        return new LayerJson(data,count);
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public long getCount() {
        return count;
    }

    public void setCount(long count) {
        this.count = count;
    }

}

对应的结构图:

这里写图片描述

项目截图:部门管理首页

这里写图片描述

项目截图:添加部门

这里写图片描述

项目截图:修改部门

这里写图片描述

项目截图:删除部门

这里写图片描述

项目截图:模糊查询

这里写图片描述

项目截图:查询没有输入条件提示

这里写图片描述

到此整个项目讲解完毕,如需源码可以到这里下载,因为CSDN博客不再支持附件上传,只能通过另外途径给大家提供源码项目源码

  • 13
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

CDHong.it

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

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

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

打赏作者

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

抵扣说明:

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

余额充值