程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

尚硅谷实战项目-谷粒学院-part one

发布于2021-05-29 20:54     阅读(1968)     评论(0)     点赞(10)     收藏(0)


实战项目

mybatis-plus的使用

查询操作

application.properties文件

#springboot2.1后加cj,不会报警告
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#springboot2.1之后的版本需要加时区
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8 
spring.datasource.data-username=root
spring.activemq.password=root123

实体类:

//@Data注解生成getset空参等方法
@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

Mapper类:

//继承BaseMapper里的方法,使用User,增删改查
@Repository
public interface UserMapper extends BaseMapper<User> {
}

主启动类:

@SpringBootApplication
//扫描mapper接口
@MapperScan("com.yutou.mpdemo1010.mapper")
public class Mpdemo1010Application {

    public static void main(String[] args) {
        SpringApplication.run(Mpdemo1010Application.class, args);
    }

}

测试类:

@SpringBootTest
public class Mpdemo1010ApplicationTests {
	//注入usermapper
    @Autowired
    private UserMapper userMapper;

    //查询user表所有数据
    @Test
    public void findAll() {
        List<User> users = userMapper.selectList(null);
        System.out.println(users);
    }

}

测试中遇到错误:

Access denied for user ‘’@‘localhost’ to database ‘mybatis_plus’

MySQL中没有权限,把User表添加相关的权限即可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-he94E6zB-1622016021336)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210513122108532.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5whLAJwG-1622016021365)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210513122141839.png)]

增加操作

添加以下配置:

#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

运行测试类时,会展现mybatis具体日志

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w4uuhaqy-1622016021366)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210513133800327.png)]

//添加操作
@Test
public void addUser(){
    User user = new User();
    user.setName("lucy");
    user.setAge(30);
    user.setEmail("lucy@qq.com");

    int insert = userMapper.insert(user);
    System.out.println("insert"+ insert);
}

mp自动生成id值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sr3kXfUU-1622016021369)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210513135028586.png)]

分布式系统唯一ID生成方案汇总

  1. 数据库自增长序列或字段
  2. UUID:每次生成随机唯一的值 排序不方便
  3. Redis生成ID 当使用数据库来生成ID性能不够要求的时候,可以用Redis来生成ID,这主要依赖于Redis是单线程的,所以也可以用生成全局唯一的ID
  4. mp自带策略 其核心思想是在使用41bit作为毫秒数,10bit作为机器的ID,12bit作为毫秒内的流水号,最后还有一个符号位,永远是0

设置主键生成策略:

@TableId(type = IdType.AUTO) //给id设置自动增长
private Long id;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfoIfbah-1622016021371)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210513142405172.png)]

修改操作

@Test
public void updateUser(){
    User user = new User();
    user.setId(2L);
    user.setAge(120);

    int row = userMapper.updateById(user);
    System.out.println(row);
}

实现自动填充

在表中增加create_time和update_time列

具体实现过程:

1、在实体类里面进行自动填充属性添加注解:

@TableField(fill = FieldFill.INSERT)
private Date createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;

2、创建类,实现接口MetaObjectHandler实现接口里面的方法

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    //使用mp实现添加操作,这个方法执行
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }

    //使用mp实现修改操作,这个方法执行
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}

乐观锁

主要解决 丢失更新

多个人同时修改一条记录,但是只能有一个人修改成功,就是最先修改的那个人

具体实现步骤:

1、表添加字段,作为乐观锁版本号

2、对应实体类添加版本号属性

@Version
private Integer version; //版本号

3、乐观锁插件

@Configuration
//扫描mapper接口
@MapperScan("com.yutou.mpdemo1010.mapper")//将主启动类的注解移动到这里
public class MpConfig {

    //乐观锁插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor(){
        return new OptimisticLockerInterceptor();
    }
}

4、测试乐观锁

//测试乐观锁
@Test
public void testOptimisticLocker(){
    //先根据id查询数据 是为了先获取版本号
    User user = userMapper.selectById(1392802232843370498L);
    //进行修改
    user.setAge(200);
    userMapper.updateById(user);
}

mp简单查询:

1、根据id查询

2、多个id批量查询

//多个id批量查询
@Test
public void testSelectBatchIds(){
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1392802232843370498L,1392803855737053186L));
    System.out.println(users);
}

3、简单的条件查询

实现分页

1、配置分页插件

//分页插件
@Bean
public PaginationInterceptor paginationInterceptor(){
    return new PaginationInterceptor();
}

2、编写分页代码

直接new page对象,传入两个参数:当前页和每页显示记录数

//分页查询
@Test
public void testPage(){
    //1.创建Page对象
    //传入两个参数:当前页 和 每页显示记录数
    Page<User> page = new Page<>(1,3);
    //调用mp分页查询的方法
    //调用mp分页查询过程中,底层封装
    //把分页所有数据封装到page对象里面
    userMapper.selectPage(page,null);

    //通过page对象获取分页数据
    System.out.println(page.getCurrent());//当前页
    System.out.println(page.getRecords());//每页数据List集合
    System.out.println(page.getSize());//每页显示记录数
    System.out.println(page.getTotal());//总记录数
    System.out.println(page.getPages());//总页数

    System.out.println(page.hasNext());//是否有下一页
    System.out.println(page.hasPrevious());//是否有上一页
}

如果报错zero date value prohibited

在配置文件中的jdbc.url后加上

zeroDateTimeBehavior=CONVERT_TO_NULL

实现删除

物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据

1、根据id删除记录

@Test
public void testDeleteById(){
    int result = userMapper.deleteById(1392718513407320065L);
    System.out.println(result);
}

2、批量删除:假删除,将对应数据中代表是否被删除字段状态修改为"被删除状态",之后在数据库中仍旧能看到此条数据记录

逻辑删除:

1、数据库中添加deleted字段

ALTER TABLE `user` ADD COLUMN `deleted` boolean //并在设计表中更改默认为0

2、实体类添加deleted字段

@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;

3、配置逻辑删除插件

//逻辑删除插件
@Bean
public ISqlInjector sqlInjector(){
    return new LogicSqlInjector();
}

4、配置默认0是删除,1是不删除

mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

5、测试删除

使用之前的物理删除测试代码,最终效果是deleted值变成1

性能分析

dev:开发环境 test:测试环境 pro:生产环境

//性能分析插件
//开发环境使用,线上不推荐,maxTime 指的是sql 最大执行时长
@Profile({"dev","test"})//设置dev test 环境开启
public PerformanceInterceptor performanceInterceptor(){
    PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    performanceInterceptor.setMaxTime(100); //单位为ms,超过此处设置的ms则sql不执行
    performanceInterceptor.setFormat(true);
    return performanceInterceptor;
}

环境设置:

#环境设置
spring.profiles.active=dev

实现复杂条件查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QBpfHbVS-1622016021373)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210513210850972.png)]

测试复杂查询

    //mp实现复杂查询操作
    @Test
    public void testSelectQuery(){
        //创建QueryWrapper对象
        QueryWrapper<User> wrapper = new QueryWrapper<>();

        //通过QueryWrapper设置条件
        //ge、gt、le、lt
        //查询age>=30的记录
        //第一个参数字段名称,第二个参数设置值
//        wrapper.ge("age",30);

        //eq、ne  搜索name=龚俊的数据
//        wrapper.eq("name","龚俊");

        //between
//        wrapper.between("age",20,30);

        //like
//        wrapper.like("name","张");

        //orderByDesc
//        wrapper.orderByDesc("id");

        //last 直接拼接到sql的最后
//        wrapper.last("limit 1");

        //指定要查询的列
        wrapper.select("id","name");

        List<User> users = userMapper.selectList(wrapper);
        System.out.println(users);


    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jva0BFuU-1622016021373)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210513213702177.png)]

前后端分离

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w4LosGn0-1622016021374)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210513215025582.png)]

搭建项目环境

数据库设计

数据库设计规约:

1、库名与应用名称尽量一致

2、表名、字段名必须使用小写字母或数字,禁止出现数字开头

3、表名不使用复数名词

4、表的命名最好加上"业务名称_表的作用"

5、表必备三字段:id;gmt_create,gmt_modified

其中id为主键,类型为bigint unsigned,单表时自增,步长为1。(如果使用分库分表集群部署,则id类型为verchar,非自增,业务中使用分布式id生成器),gmt_create,gmt_modified 的类型均为datetime类型,前者现在时表示主动创建,后者过去分词表示被动更新

6、单表行数超过500万行或者单表容量超过2GB,才推荐分库建表

7、表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是unsigned tinyint(1表示是,0表示否)

搭建父工程

新建guli_parent项目,pom.xml中加入以下代码,统一声明包的版本号

 <groupId>com.yutou</groupId>
 <artifactId>guli_parent</artifactId>
 <packaging>pom</packaging>
 <version>0.0.1-SNAPSHOT</version>
 <name>guli_parent</name>
 <description>Demo project for Spring Boot</description>
 <properties>
        <java.version>1.8</java.version>
        <guli.version>0.0.1-SNAPSHOT</guli.version>
        <mybatis-plus.version>3.0.5</mybatis-plus.version>
        <velocity.version>2.0</velocity.version>
        <swagger.version>2.7.0</swagger.version>
        <aliyun.oss.verision>2.8.3</aliyun.oss.verision>
        <jodatime.verision>2.10.1</jodatime.verision>
        <poi.verision>3.17</poi.verision>
        <commons-fileupload.verision>1.3.1</commons-fileupload.verision>
        <commons-io.verision>2.6</commons-io.verision>
        <httpclient.verision>4.5.1</httpclient.verision>
        <jwt.verision>0.7.0</jwt.verision>
        <aliyun-java-sdk-core.verision>4.3.3</aliyun-java-sdk-core.verision>
        <aliyun-sdk-oss.verision>3.1.0</aliyun-sdk-oss.verision>
        <aliyun-java-sdk-vod.verision>2.15.2</aliyun-java-sdk-vod.verision>
        <aliyun-java-vod-upload.verision>1.4.11</aliyun-java-vod-upload.verision>
        <aliyun-sdk-vod-upload.verision>1.4.11</aliyun-sdk-vod-upload.verision>
        <fastjson.version>1.2.28</fastjson.version>
        <gson.verision>2.8.2</gson.verision>
        <json.verision>20170516</json.verision>
        <commons-dbutils.verision>1.7</commons-dbutils.verision>
        <canal.vlient.verision>1.1.0</canal.vlient.verision>
        <docker.image.prefix>zx</docker.image.prefix>
        <cloud-alibaba.verision>0.2.2.RELEASE</cloud-alibaba.verision>
    </properties>

导入依赖时,发现pom文件的引入版本号报红

是因为标签内是引入本地依赖,先导入下载完本地依赖再使用这个标签。

<aliyun-java-vod-upload.version> 报红

去官网下载jar包后,复制到本地的maven中的lb文件夹下打开cmd运行,官网现在是1.4.14版

mvn install:install-file -DgroupId=com.aliyun -DartifactId=aliyun-sdk-vod-upload -Dversion=1.4.14 -Dpackaging=jar -Dfile=aliyun-java-vod-upload-1.4.14.jar

搭建子工程

新建service模块,在其下新建service_edu模块

…/…/pom.xml

在Pom文件中此文件报红,作用是子工程继承了父工程,默认的没有配置,需要指出父工程pom文件的相对位置

配置文件内容:

#服务端口
server.port=8001
#服务名
spring.application.name=service-edu

#环境设置:dev、test、prod
spring.profiles.active=dev

#Mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.activemq.password=root123

#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

使用代码生成器生成controller、mapper、service等内容:

更改代码生成器中的下面内容

		gc.setOutputDir("F:\\My eclipse\\guli_parent\\service\\service_edu" + "/src/main/java");//改成绝对路径
		gc.setIdType(IdType.ID_WORKER_STR); //更改主键策略
        // 3、数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root123");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 4、包配置
        PackageConfig pc = new PackageConfig();
        //包的名字 com.yutou.eduservice
        pc.setParent("com.yutou");
        pc.setModuleName("eduservice"); //模块名

        //com.yutou.eduservice.controller/entity/service/mapper
        pc.setController("controller");
        pc.setEntity("entity");
        pc.setService("service");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

讲师列表

创建controller类

//里面的@Controller表示spring管理的对象,@ResponseBody还表示里面的内容需要返回数据
@RestController
//访问路径
@RequestMapping("/eduservice/edu-teacher")
public class EduTeacherController {
    //把service注入
    @Autowired
    private EduTeacherService teacherService;

    //查询讲师所有数据
    //rest风格
    @GetMapping("findAll")//findall前可以加"/"也可以不加
    public List<EduTeacher> findAll(){
        //调用service的方法实现查询所有的操作
        List<EduTeacher> list = teacherService.list(null);
        return list;
    }

}

创建SpringBoot启动类

@SpringBootApplication
public class EduApplication {

    public static void main(String[] args) {
        SpringApplication.run(EduApplication.class,args);
    }
}

创建Config类,配置mapper扫描和其他

@Configuration
@MapperScan("com.yutou.eduservice.mapper")
public class MybatisPlusConfig {
}

运行主启动类

报错Failed to configure a DataSource: ‘url’ attribute is not specified and no embedd

把pom文件中的pom去掉,可以成功运行,但是…/…/pom.xml报红

更改返回的Json时间格式

#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

讲师删除

在config中配置逻辑删除插件:

//逻辑删除插件
@Bean
public ISqlInjector sqlInjector() {
    return new LogicSqlInjector();
}

逻辑删除上添加注解

@TableLogic
private Boolean isDeleted;

逻辑删除方法:

//逻辑删除讲师方法
@DeleteMapping("{id}")//id值需要通过路径进行传递
//@PathVariable 获取路径中的id值
public boolean removeTeacher(@PathVariable String id){
    boolean flag = teacherService.removeById(id);
    return flag;
}

如何测试:因为使用delete提交

借助一些工具进行测试:swagger或Postman

整合swagger

前后端分离开发模式中,api文档是最好的沟通方式。

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。

  1. 及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)
  2. 规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)
  3. 一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)
  4. 可测性 (直接在接口文档上进行测试,以方便理解业务

创建公共模块,整合swagger,为了所有模块都能进行使用:

1、guli_parent创建子模块common,在common创建子模块 service_base

配置类如下:

@Configuration //配置类
@EnableSwagger2 //swagger注解
public class SwaggerConfig {
    @Bean
    public Docket webApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                .paths(Predicates.not(PathSelectors.regex("/admin/.*")))
                .paths(Predicates.not(PathSelectors.regex("/error.*")))
                .build();
    }

    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("网站-课程中心API文档")
                .description("本文档描述了课程中心微服务接口定义")
                .version("1.0")
                .contact(new Contact("Helen", "http://atguigu.com",
                        "55317332@qq.com"))
                .build();
    }
}

2、在service_edu引入service_base依赖

<dependency>
    <groupId>com.yutou</groupId>
    <artifactId>service_base</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

3、在service_edu启动类添加注解,设置包扫描规则

//为了加载swagger的配置类
@ComponentScan(basePackages = {"com.yutou"})
public class EduApplication {..}

4、访问swaggerSwagger UI

报错unable to infer base url. This is common when using dynamic servlet registration or when the API is behind an API Gateway

把@ComponentScan(basePackages = {“com.yutou”})注释掉换成@EnableSwagger2

5、定义接口说明和参数说明

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e9FGvLjt-1622016021375)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210517110443702.png)]

定义在类上:@Api

定义在方法上:@ApiOperation

定义在参数上:@ApiParam

统一结果返回

统一返回数据格式:

项目中我们会将响应封装成json返回,一般我们会将所有的接口的数据格式统一,使得前端对数据的操作更一致轻松

一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容

json数据格式2种:

对象、数组两种格式混合使用

{
	"success":布尔  //响应是否成功
	"code":数字 //响应码
	"message": 字符串 //返回消息
	"data": HashMap //返回数据,放在键值对中
}

1、在common模块中创建子模块common-utils

2、创建interface,定义数据返回状态码

public interface ResultCode {

    public static Integer SUCCESS = 20000;//成功
    public static Integer ERROR = 20001;//失败
}

3、创建结果类

链式编程

形如以下这种的叫做链式编程,每个类都返回this即可实现下面的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KmyRQ4p6-1622016021376)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210517115221600.png)]

4、 在service中引入common-utils

<dependency>
    <groupId>com.yutou</groupId>
    <artifactId>common-utils</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>
public R findAll(){
    //调用service的方法实现查询所有的操作
    List<EduTeacher> list = teacherService.list(null);
    return R.ok().data("items",list);
}
public R removeTeacher(@ApiParam(name = "id", value = "讲师ID", required = true)
                                 @PathVariable String id){
    boolean flag = teacherService.removeById(id);
    if (flag){
        return R.ok();
    }else {
        return R.error();
    }
}

测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G19vYAxT-1622016021377)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210517125417272.png)]

分页查询

基本分页功能

1、MybatisPlusConfig中配置分页插件

/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}

2、编写讲师分页查询接口的方法

  //分页查询讲师方法
    //current 当前页
    //limit 每页记录数
    @GetMapping("pageTeacher/{current}/{limit}")
    public R pageListTeacher(@PathVariable long current,
                             @PathVariable long limit){

        //创建page对象
        Page<EduTeacher> pageTeacher = new Page<>(current,limit);
        //调用方法实现分页
        //调用方法的时候,底层封装,把分页所有数据封装到pageTeacher对象里面
        teacherService.page(pageTeacher,null);

        long total = pageTeacher.getTotal(); //总记录数
        List<EduTeacher> records = pageTeacher.getRecords(); //数据list集合

        //等价于下面那种
//        Map map = new HashMap();
//        map.put("total",total);
//        map.put("row",records);
//        return R.ok().data(map);


        return R.ok().data("total",total).data("rows",records);
    }

条件分页查询

1、把条件值传递到接口里面

把条件值封装到对象里面,把对象传递到接口里面

@ApiModel(value = "Teacher查询对象" ,description = "讲师查询对象封装")
@Data
public class TeacherQuery implements Serializable {
    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "教师名称,模糊查询")
    private String name;

    @ApiModelProperty(value = "头衔 1高级讲师 2首席讲师")
    private Integer level;

    @ApiModelProperty(value = "查询开始时间", example = "2019-01-01 10:10:10")
    private String begin;//注意,这里使用的是String类型,前端传过来的数据无需进行类型转换

    @ApiModelProperty(value = "查询结束时间", example = "2019-12-01 10:10:10")
    private String end;
}

2、根据条件值进行判断,拼接条件

@RequestBody:使用json传递数据,把json数据封装到对应的对象里面 (需要使用Post提交)

@ResponsBody:返回数据,返回Json数据

 //条件查询带分页的方法
    @PostMapping("pageTeacherCondition/{current}/{limit}")
    public R pageTeacherCondition(@PathVariable long current,
                                  @PathVariable long limit,
                                  //参数值可以为空
                                  @RequestBody(required = false) TeacherQuery teacherQuery){
        //创建page对象
        Page<EduTeacher> teacherPage = new Page<>(current,limit);
        //构建条件
        QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>();
        //多条件组合拆线呢
        //mybatis 学过 动态sql
        String name = teacherQuery.getName();
        Integer level = teacherQuery.getLevel();
        String begin = teacherQuery.getBegin();
        String end = teacherQuery.getEnd();
        //判断条件值是否为空,如果不为空拼接条件
        if(StringUtils.isEmpty(name)){
            //构建条件
            wrapper.like("name",name);
        }
        if (StringUtils.isEmpty(level)){
            wrapper.eq("level",level);
        }
        //begin大于 end小于 是一个范围
        if (StringUtils.isEmpty(begin)){
            wrapper.ge("gmt_create",begin);
        }
        if (StringUtils.isEmpty(end)){
            wrapper.le("gmt_create",end);
        }

        //调用方法实现条件查询分页
        teacherService.page(teacherPage,wrapper);
        long total = teacherPage.getTotal(); //总记录数
        List<EduTeacher> records = teacherPage.getRecords(); //数据list集合

        return R.ok().data("total",total).data("rows",records);
    }

添加讲师

1、创建包handler,创建自动填充类 MyMetaObjectHandler

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
 @Override
 public void insertFill(MetaObject metaObject) {
 this.setFieldValByName("gmtCreate", new Date(), metaObject);
 this.setFieldValByName("gmtModified", new Date(), metaObject);
 }
 @Override
 public void updateFill(MetaObject metaObject) {
 this.setFieldValByName("gmtModified", new Date(), metaObject);
 }
}

2、在实体类添加自动填充注解

@ApiModelProperty(value = "创建时间")
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;

@ApiModelProperty(value = "更新时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;

3、controller类

@PostMapping("addTeacher")
public R addTeacher(@RequestBody EduTeacher eduTeacher){
    boolean save = teacherService.save(eduTeacher);
    if (save){
        return R.ok();
    }else {
        return R.error();
    }
}

4、swagger测试

把数据中时间和id删去,,会自动填充

修改讲师

1、根据讲师id进行查询

//根据讲师id进行查询
@GetMapping("getTeacher/{id}")
public R getTeacher(@PathVariable String id){
    EduTeacher eduTeacher = teacherService.getById(id);
    return R.ok().data("teacher",eduTeacher);

}

2、讲师修改

//讲师修改功能
@PostMapping("updateTeacher")
public R updateTeacher(@RequestBody EduTeacher eduTeacher){
    boolean flag = teacherService.updateById(eduTeacher);
    if (flag){
        return R.ok();
    }else {
        return R.error();
    }
}

统一异常处理

全局异常处理:

@ControllerAdvice
public class GlobalExceptionHandler {

    //指定出现什么异常都会执行这个方法
    @ExceptionHandler(Exception.class)
    @ResponseBody //为了能够返回数据
    public R error(Exception e){
        e.printStackTrace();
        return R.error().message("执行了全局异常处理");
    }
}

特定异常处理:

//指定异常处理
@ExceptionHandler(ArithmeticException.class)
@ResponseBody //为了能够返回数据
public R error(ArithmeticException a){
    a.printStackTrace();
    return R.error().message("执行了ArithmeticException异常处理");
}

自定义异常处理:

1、创建自定义异常类

@Data
@AllArgsConstructor  //生成有参数的构造方法
@NoArgsConstructor  //生成无参构造方法
public class GuliException extends RuntimeException{

    private Integer code;// 状态码

    private String meg; //异常信息
}

2、在统一异常类添加规则

//自定义异常
@ExceptionHandler(GuliException.class)
@ResponseBody //为了能够返回数据
public R error(GuliException e){
    e.printStackTrace();
    return R.error().code(e.getCode()).message(e.getMsg());
}

3、执行自定义异常

try{
    int i=10/0;
}catch (Exception e){
    //执行自定义异常
    throw new GuliException(20001,"执行了自定义异常处理");
}

统一日志处理

日志记录器的行为是分等级的。

分为:OFF、FATAL、ERROR、WARN、INFO、DUBUG、ALL

越往右级别越高,右边包含左边

默认情况下,spring boot 从控制台打印出来的日志级别只有INFO及以上的级别,可以配置日志级别

#设置日志级别
logging.level.root=WARN

把日志不仅输出到控制台,也可以输出到文件中,使用日志工具

1、删除application.properties日志配置

2、resource中创建logback-spring.xml

复制代码好像有问题。。略过

前端

vscode

安装

es6

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现

es6语法

let的作用范围

<script>
    //es6如何定义变量,定义变量特点
    //js定义:var a = "1";
    //es6写法定义变量: 使用关键字 let let a = 10;
    //创建代码块,定义变量
    {
        var a = 10
        let b = 20
    }
    //在代码块外面输出变量
    console.log(a)
    console.log(b)//Uncaught ReferenceError : b is not defined 
</script>

let只能定义一次

const定义常量,值不能改变,一旦声明必须初始化,否则会报错

解构赋值

//2、对象解构
let user = {name: 'Helen', age: 18}
// 传统
let name1 = user.name
let age1 = user.age
console.log(name1, age1)
// ES6
let { name, age } = user//注意:结构的变量必须是user中的属性
console.log(name, age)

模板字符串

模板字符串相当于加强版的字符串,用反引号 `,除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式。

// 1、多行字符串
let string1 = `Hey,
can you stop angry now?`
console.log(string1)
// Hey,
// can you stop angry now?
// 2、字符串插入变量和表达式。变量名写在 ${} 中,${} 中可以放入 JavaScript 表达式。
let name = "Mike"
let age = 27
let info = `My Name is ${name},I am ${age+1} years old next year.`
console.log(info)
// My Name is Mike,I am 28 years old next year.
// 3、字符串中调用函数
function f(){
 return "have fun!"
}
let string2 = `Game start,${f()}`
console.log(string2); // Game start,have fun!

声明对象简写:

const age = 12
const name = "Amy"
// 传统
const person1 = {age: age, name: name}
console.log(person1)
// ES6
const person2 = {age, name}
console.log(person2) //{age: 12, name: "Amy"}

定义方法简写:

// 传统
const person1 = {
 sayHi:function(){
 console.log("Hi")
 }
}
person1.sayHi();//"Hi"
// ES6
const person2 = {
 sayHi(){
 console.log("Hi")
 }
}
person2.sayHi() //"Hi"

对象扩展运算符:

// 1、拷贝对象
let person1 = {name: "Amy", age: 15}
let someone = { ...person1 }
console.log(someone) //{name: "Amy", age: 15}
// 2、合并对象
let age = {age: 15}
let name = {name: "Amy"}
let person2 = {...age, ...name}
console.log(person2) //{age: 15, name: "Amy"}

箭头函数:

// 传统
var f1 = function(a){
 return a }
console.log(f1(1))
// ES6
var f2 = a => a
console.log(f2(1))
// 当箭头函数没有参数或者有多个参数,要用 () 括起来。
// 当箭头函数函数体有多行语句,用 {} 包裹起来,表示代码块,
// 当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。
var f3 = (a,b) => {
 let result = a+b
 return result
}
console.log(f3(6,2)) // 8
// 前面代码相当于:
var f4 = (a,b) => a+b

vue

Vue 是一套用于构建用户界面的渐进式框架。

Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

vue入门

1、创建Html页面,使用vscode快捷键生成html代码 !

2、引入vue的js文件,类似于jquery

<script src="vue.min.js"></script>

3、在html页面创建div标签,div添加id属性

4、编写vue代码

<script>
        new Vue({
            el:'#app',//绑定vue作用的范围
            data:{//定义页面中显示的模型数据
                message:'Hello Vue'
            }
        })
    </script>

5、使用插值表达式,获取data里面定义值

 <div id="app">
        {{message}}
    </div>

基本语法

1、基本数据渲染和指令

单向数据绑定

 <div id="app">
        <!-- 
            v-bind指令
            单向数据绑定
            这个指令一般用在标签属性里面,获取值
        -->
        <h1 v-bind:title="message">
            {{content}}
        </h1>

        <!-- 简写方式 -->
        <h2 :title="message">
            {{content}}
        </h2>

    </div>
    <script src="vue.min.js"></script>
    <script>
        new Vue({
             el: '#app',
             data: {
                 content:'我是标题',
                 message:'页面加载于' + new Date().toLocaleString()
             }
         })
     </script>

双向数据绑定:

<body>
 <div id="app">
    <input type="text" v-bind:value="searchMap.keyWord"/>
    <input type="text" v-model="searchMap.keyWord"/>

    <p>{{searchMap.keyWord}}</p>
    </div>
    <script src="vue.min.js"></script>
    <script>
        new Vue({
             el: '#app',
             data: {
                 searchMap:{
                     keyWord: '鱼头'
                 }
             }
         })
     </script>
</body>

vue绑定事件

<body>
 <div id="app">
        <!-- vue绑定事件 -->
        <button v-on:click="search()">查询</button>
        <!-- vue绑定事件简写 -->
        <button @click="f1()">查询1</button>
    </div>
    <script src="vue.min.js"></script>
    <script>
        new Vue({
             el: '#app',
             data: {
                 searchMap:{
                     keyWord: '鱼头'
                 },
                 //查询结果
                 result:{}
             },
             methods:{//定义多个方法
                search(){
                    console.log('search...')
                },
                f1(){
                    console.log('f1...')
                }
             }
         })
     </script>
</body>

修饰符

<div id="app">
    <!-- 修饰符用于指出一个指令应该以特殊方式绑定。
 这里的 .prevent 修饰符告诉 v-on 指令对于触发的事件调用js的
event.preventDefault():
 即阻止表单提交的默认行为 -->
    <form action="save" v-on:sumbit.prevent="onSubmit">
        <input type="text" id="name" v-model="user.username"/>
        <button type="submit">保存</button>
    </form>
    </div>
    <script src="vue.min.js"></script>
    <script>
        new Vue({
             el: '#app',
             data: {
                 user:{}
             },
             methods:{
                 onSubmit(){
                    if(this.user.username){
                        console.log('提交表单')
                    }else {
                        alert('请输入用户名')
                    }
                 }
             }
         })
     </script>

条件渲染

<body>
 <div id="app">
    <input type="checkbox" v-model="ok"/>是否同意
    <!-- 条件指令 v-if v-else -->
    <h1 v-if="ok"尚硅谷></h1>
    <h1 v-else>谷粒学院</h1>
    </div>
    <script src="vue.min.js"></script>
    <script>
        new Vue({
             el: '#app',
             data: {
                 ok:false
             }
         })
     </script>
</body>

循环指令

<body>
    <div id="app">
        <ul>
            <li v-for="n in 10">{{ n }} </li>
        </ul>

        <ol>
            <li v-for="(n,index) in 10">{{n}} -- {{index}}</li>
        </ol>

        <hr/>
        <table border="1">
            <tr v-for="user in userList">
                <td>{{user.id}}</td>
                <td>{{user.username}}</td>
                <td>{{user.age}}</td>
            </tr>
        </table>
    </div>
    <script src="vue.min.js"></script>
    <script>
        new Vue({
             el: '#app',
             data: {
                userList: [
                    { id: 1, username: 'helen', age: 18 },
                    { id: 2, username: 'peter', age: 28 },
                    { id: 3, username: 'andy', age: 38 }
                ] 
             }
         })
     </script>
</body>

vue组件

局部组件

<body>
 <div id="app">
    <Navbar></Navbar>
    </div>
    <script src="vue.min.js"></script>
    <script>
        new Vue({
             el: '#app',
             // 定义局部组件,这里可以定义多个局部组件
            components: {
            //组件的名字
                 'Navbar': {
            //组件的内容
                      template: '<ul><li>首页</li><li>学员管理</li></ul>'
            }
            }
         })
     </script>
</body>

全局组件:

 <script src="Navbar.js"></script>

vue生命周期:

<script>
        new Vue({
             el: '#app',
             data: {
                
             },
             created(){
                 debugger//断点
                //在页面渲染之前执行
                console.log('created..')
             },
             mounted(){
                 debugger
                 //在页面渲染之后执行
                 console.log('mounted..')
             }
         })
     </script>

vue路由

Vue.js 路由允许我们通过不同的 URL 访问不同的内容。

通过 Vue.js 可以实现多视图的单页Web应用(single page web application,SPA)。

Vue.js 路由需要载入 vue-router 库

 <div id="app">
        <h1>Hello App!</h1>
        <p>
        <!-- 使用 router-link 组件来导航. -->
        <!-- 通过传入 `to` 属性指定链接. -->
        <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
            <router-link to="/">首页</router-link>
            <router-link to="/student">会员管理</router-link>
            <router-link to="/teacher">讲师管理</router-link>
        </p>
        <!-- 路由出口 -->
        <!-- 路由匹配到的组件将渲染在这里 -->
        <router-view></router-view>
    </div>
    <!-- 有顺序关系 -->
    <script src="vue.min.js"></script>
    <script src="vue-router.min.js"></script>
    <script>
        // 1. 定义(路由)组件。
        // 可以从其他文件 import 进来
        const Welcome = { template: '<div>欢迎</div>' }
        const Student = { template: '<div>student list</div>' }
        const Teacher = { template: '<div>teacher list</div>' }
        // 2. 定义路由
        // 每个路由应该映射一个组件。
        const routes = [
            { path: '/', redirect: '/welcome' }, //设置默认指向的路径
            { path: '/welcome', component: Welcome },
            { path: '/student', component: Student },
            { path: '/teacher', component: Teacher }
        ]
        // 3. 创建 router 实例,然后传 `routes` 配置
        const router = new VueRouter({
            routes // (缩写)相当于 routes: routes
        })
        // 4. 创建和挂载根实例。
        // 从而让整个应用都有路由功能
        const app = new Vue({
            el: '#app',
            router
        })
        // 现在,应用已经启动了!
    </script>

axios

axios是独立的项目,不是vue里面的一部分,使用axios经常和vue一起使用,实现ajax操作

1、创建html页面,引入js文件,包含两个js文件,vue和axios

 <script src="vue.min.js"></script>
    <script src="axios.min.js"></script>

2、编写axios代码

创建json文件,数据创建

{
    "success":true,
    "code":20000,
    "message":"成功",
    "data":{
        "items":[
            {"name":"lucy","age":"20"},
            {"name":"zzh","age":"30"},
            {"name":"gj","age":"29"}
        ]
        
    }
}

使用axios发送ajax请求,请求文件,得到数据,在页面显示

<body>
 <div id="app">

    </div>
    <script src="vue.min.js"></script>
    <script src="axios.min.js"></script>
    <script>
        new Vue({
             el: '#app',
             //固定的结构
             data: { //在data定义变量和初始值
                 //定义变量:值空数组
                 userList:[]
             },
             created() {//在页面渲染之前执行
                //调用定义的方法
                this.getUserList()
             },
             methods:{//编写具体的方法
                //创建方法 查询所有用户数据
                getUserList() {
                    //使用axios发送ajax请求
                    //axios:提交方式().then(箭头函数).catch(箭头函数)
                    axios.get("data.json")
                        .then(response => {
                            //response就是请求之后返回数据
                            // console.log('***'+response)
                            //通过response获取具体数据,赋值给定义空数组
                            this.userList = response.data.data.items
                            console.log(this.userList)
                        }) //请求成功执行then方法
                        .catch(error =>{
                        }) //请求失败执行catch方法
                }
             }
         })
     </script>
</body>

node js

简单的说 Node.js 就是运行在服务端的 JavaScript。

Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。

编写js文件

const http = require('http');
http.createServer(function (request, response){
    //发送HTTP头部
    //HTTP 状态值: 200 : OK
    //内容类型:text/plain
    response.writeHead(200,{'Content-Type':'text/plain'});
    //发送响应数据 "Hello World"
    response.end("Hello Server");
}).listen(8888);
//终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');

在js文件所在文件夹运行cmd

输入node 01.js即可以运行。 在浏览器输入http://127.0.0.1:8888/可以访问到hello Sever

npm管理工具

类似maven,用在前端中,管理前端js依赖,联网下载js依赖,比如jquery

1、npm项目初始化操作

使用命令

npm init -y

2、npm下载js依赖

babel

babel是转码器,把es6代码换成es5代码,因为写的代码es6代码,但是es6代码浏览器兼容性很差,如果使用es5代码浏览器兼容性好

1、安装babel工具,使用命令

npm install --save-dev babel-cli

如果babel --version显示不了版本号

如果安装后已经产生了node-module文件夹,就把里面的.bin文件加入到环境变量Path中,重启vscode,即可看到版本号

2、创建js文件,编写cs6代码

3、创建babel配置文件

{
    "presets":["es2015"],
    "plugins":[]
}

4、安装转码器

npm install --save-dev babel-preset-es2015

5、使用命令进行转码

babel es6/demo.js -o dist/01.js

如果package.json里面的babel版本号是6.x的,而babel --version的版本是7.x则出现错误

原因是版本不兼容,把之前安装的卸载之后

按以下地址的教程安装:

babel7.0 安装及使用_Yu_xiaoji的博客-CSDN博客_安装babel7

但是安装之后转换不了。。。

模块化

开发后端接口时候,开发controller service mapper,controller注入service,service注入mapper,在后端中,类与类之间的调用称为后端模块化操作

前端模块化

es5写法

新建01.js

//创建js方法
// 定义成员:
const sum = function(a,b){
    return parseInt(a) + parseInt(b) }
const subtract = function(a,b){
    return parseInt(a) - parseInt(b) }

//设置哪些方法可以被其他js调用
module.exports = {
    sum: sum,
    subtract: subtract,
   }

新建02.js

//1 引入01.js文件
const m = require('./01.js')

//2.调用
console.log(m.sum(1,2))
console.log(m.subtract(3,1))

es6写法

新建01.js

//定义方法,设置哪些方法可以被其他js调用
export function getList(){
    console.log('getList...')
}

export function save(){
    console.log('save...')
}

新建02.js

//调用01.js的方法,引入01.js文件,进行调用
import {getList,save} from './01.js'

//调用方法
getList()
save()

webpack

webpack是一个前端资源加载/打包工具,它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的的规则生成对应的静态资源

把多种静态资源转换成一个静态文件,减少了页面的请求

安装webpack

安装webpack,把webpack的.bin路径加入环境变量

使用webpack -v查看是否安装成功

打包js文件

1、创建src/common.js文件

exports.info = function (str) {
 document.write(str);
}

2、创建src/utils.js文件

exports.add = function (a, b) {
 return a + b; }

3、src下创建main.js

const common = require('./common');
const utils = require('./utils');
common.info('Hello world!' + utils.add(100, 200));

4、创建配置文件webpack.config,js

const path = require("path"); //Node.js内置模块
module.exports = {
 entry: './src/main.js', //配置入口文件
 output: {
 path: path.resolve(__dirname, './dist'), //输出路径,__dirname:当前文件所在路
径
 filename: 'bundle.js' //输出文件
 }
}

5、命令行执行编译命令

webpack #有黄色警告
webpack --mode=development #没有警告
#执行后查看bundle.js 里面包含了上面两个js文件的内容并惊醒了代码压缩

6、测试

webpack目录下创建index.html,引用bundle.js

<body>
 <script src="dist/bundle.js"></script>
</body>

打包css文件

1、创建src/css文件

2、在main.js中引入css文件

//css文件引入
require('./style.css')

3、安装css-loader和style-loader

webpack本身只能处理javaScript模块,如果要处理其他类型的文件,就需要使用loader进行转换。

npm install --save-dev style-loader css-loader

4、修改webpackconfig.js

const path = require("path"); //Node.js内置模块
module.exports = {
 entry: './src/main.js', //配置入口文件
 output: {
    path: path.resolve(__dirname, './dist'), //输出路径,__dirname:当前文件所在路径
    filename: 'bundle.js' //输出文件
 },//新增部分
 module: {
   rules: [ 
   { 
   test: /\.css$/, //打包规则应用到以css结尾的文件上
   use: ['style-loader', 'css-loader']
   }
   ]
}
}

项目前端搭建

vue-elemnt-admin

安装

# 解压压缩包
# 进入目录
cd vue-element-admin-master
# 安装依赖
npm install
# 启动。执行后,浏览器自动弹出并访问http://localhost:9527/
npm run dev

入口

前端框架入口

index.html 和 main.js

目录介绍

build:放项目构建的脚本文件

config:配置信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dHhd5UQv-1622016021379)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210521150216718.png)]

可以更改端口号

修改为false:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uipQfGuV-1622016021380)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210521150255688.png)]

不安装Eslint插件,因为语法要求过于严格

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b7tKznQh-1622016021381)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210521151306952.png)]

src目录

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qBvqjmMF-1622016021382)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210521151723112.png)]

后台登录功能改造

1、更改dev.env.js里面的访问地址

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OyQRTahU-1622016021383)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210521153117143.png)]

2、进行登录调用两个方法, Login登录操作方法和info登录之后获取用户信息的方法,所以,创建接口两个方法实现登录

(1)login 返回 token值

(2)info 返回 roles name avatar

@RestController
@RequestMapping("/eduservice/user")
public class EduLoginController {
    //login
    @PostMapping("login")
    public R login(){
        return R.ok().data("token","admin");
    }
    //info
    @GetMapping("info")
    public R info(){
        return R.ok().data("roles","[admin]").data("name","admin").data("avatar","https://img1.doubanio.com/view/richtext/large/public/p155631507.jpg");
    }
}

3、更改前端中的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AwVBxP3K-1622016021384)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210521160226598.png)]

跨域问题:

通过一个地址去访问另外一个地址,这个过程中如果有三个地方任何一个不一样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c3FnwfqO-1622016021386)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210521161341648.png)]

在类上增加注解:

@CrossOrigin //解决跨域问题

讲师管理

讲师列表

1、添加路由

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b4O3bukO-1622016021387)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210521170513225.png)]

2、创建路由对应的页面

新建以下路径下的vue文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NyV3rOin-1622016021388)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210521171530940.png)]

3、创建api/teacher.js定义访问路由的接口地址

import request from '@/utils/request'

export default {
    //1 讲师(条件查询分页)
    //current当前页 limit每页记录数 teacherQuery 
    getTeacherListPage(current, limit, teacherQuery){
        return request({
            url: `/eduservice/teacher/pageteacherCondition/${current}/${limit}`,
            method: 'post',
            //teachQuery条件对象,后端使用ReqeustBody获取数据
            //data表示把对象转换json进行传递到接口里面
            data: teacherQuery
        })
    }
}

4、在讲师列表页面 list.vue页面调用定义的接口方法,得到接口返回数据

<template>
    <div class="app-container">
        讲师列表
    </div>
</template>
<script>
//引入调用teacher.js文件
import teacher from '@/api/teacher/teacher.js'


export default{
    //写核心代码位置
    data(){
      return {
          list:null, //查询之后接口返回集合
          page:1, //当前页
          limit:10, // 每页记录数
          total:0, //总记录数
          teacherQuery:{} //条件封装对象

      }
    },
    created(){ //页面渲染具体的方法,调用teacher.js定义的方法
        //调用
        this.getList()

    },
    methods:{
        //讲师列表的方法
        getList(){
            teacher.getTeacherListPage(this.page,this.limit,this.teacherQuery)
                .then(response => { //请求成功
                    //response接口返回的数据
                    // console.log(response)
                    this.list=response.data.rows
                    console.log(this.list)
                    console.log(this.total)
                })
                .catch(error => {
                    //请求失败
                    console.log(error)
                })
        }
    }
}
</script>

5、把请求接口获取参数在页面进行显示

<template>
    <div class="app-container">
        讲师列表
     <!-- 表格 -->
    <el-table
    v-loading="listLoading"
    :data="list"
    element-loading-text="数据加载中"
    border
    fit
    highlight-current-row> 
    <el-table-column
     label="序号"
     width="70"
     align="center"> 
    <template slot-scope="scope">
     {{ (page - 1) * limit + scope.$index + 1 }}
    </template>
    </el-table-column>
     <el-table-column prop="name" label="名称" width="80" />

    <el-table-column label="头衔" width="80">

    <template slot-scope="scope">
    {{ scope.row.level===1?'高级讲师':'首席讲师' }}
    </template>
    </el-table-column> 
    <el-table-column prop="intro" label="资历" />
    <el-table-column prop="gmtCreate" label="添加时间" width="160"/>
    <el-table-column prop="sort" label="排序" width="60" />
    <el-table-column label="操作" width="200" align="center"> 
    <template slot-scope="scope"> <router-link :to="'/edu/teacher/edit/'+scope.row.id"> 
    <el-button type="primary" size="mini" icon="el-icon-edit">修改</el-button>
    </router-link> <el-button type="danger" size="mini" icon="el-icon-delete"
    @click="removeDataById(scope.row.id)">删除</el-button>
    </template>
    </el-table-column>
    </el-table>
  
        </div>
    </template>
<script>
//引入调用teacher.js文件

import teacher from '@/api/teacher/teacher.js'


export default{
    //写核心代码位置
    data(){
      return {
          list:null, //查询之后接口返回集合
          page:1, //当前页
          limit:10, // 每页记录数
          total:0, //总记录数
          teacherQuery:{} //条件封装对象

      }
    },
    created(){ //页面渲染具体的方法,调用teacher.js定义的方法
        //调用
        this.getList()

    },
    methods:{
        //讲师列表的方法
        getList(){
            teacher.getTeacherListPage(this.page,this.limit,this.teacherQuery)
                .then(response => { //请求成功
                    //response接口返回的数据
                    // console.log(response)
                    this.list=response.data.rows
                	this.total=response.data.total
                    console.log(this.list)
                    console.log(this.total)
                })
                .catch(error => {
                    //请求失败
                    console.log(error)
                })
        }
    }
}
</script>

讲师列表添加分页实现

增加分页条

  <!-- 分页 -->
    <el-pagination
        :current-page="page" 
        :page-size="limit"	
        :total="total"
        style="padding: 30px 0; text-align: center;"
        layout="total, prev, pager, next, jumper"
        @current-change="getList"
    />

分页的方法修改,添加页码参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S5wgx8G2-1622016021389)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210521204914993.png)]

测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mxaXiRlC-1622016021389)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210521205459836.png)]

条件查询

    <!--查询表单-->
    <el-form :inline="true" class="demo-form-inline"> 
    <el-form-item> 
    <el-input v-model="teacherQuery.name" placeholder="讲师名"/>
    </el-form-item> 
    <el-form-item> <el-select v-model="teacherQuery.level" clearable placeholder="讲师头衔"> 
    <el-option :value="1" label="高级讲师"/>
    <el-option :value="2" label="首席讲师"/>
    </el-select>
    </el-form-item>

     <el-form-item label="添加时间"> 
        <el-date-picker
        v-model="teacherQuery.begin"
    type="datetime"
    placeholder="选择开始时间"
    value-format="yyyy-MM-dd HH:mm:ss"
    default-time="00:00:00"
    />
    </el-form-item>
    <el-form-item> 
     <el-date-picker
    v-model="teacherQuery.end"
    type="datetime"
    placeholder="选择截止时间"
    value-format="yyyy-MM-dd HH:mm:ss"
    default-time="00:00:00"
    />
    </el-form-item> 
    <el-button type="primary" icon="el-icon-search" @click="getList()">查 询</el-button> 
    <el-button type="default" @click="resetData()">清空</el-button>
    </el-form>

实现清空功能:

resetData(){//清空的方法
        //表单输入项数据情况
        this.teacherQuery = {}
        //查询所有讲师数据
        this.getList()   
}

讲师删除

1、在每条记录后面添加删除按钮

2、在按钮绑定事件

3、在绑定事件的方法传递删除讲师的id值

  //删除讲师方法
        removeDataById(id){
            alert(id)
        }

4、在api文件夹teacher.js定义删除接口的地址

  //删除讲师
    deleteTeacherId(id){
        return request({
            url: `/eduservice/teacher/${id}`,
            method: 'delete'
        })
    }

5、页面调用方法实现删除

  //删除讲师方法
        removeDataById(id){
            this.$confirm('此操作将永久删除讲师记录, 是否继续?', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
            }).then(() => { //点击确定,执行then方法
                //调用删除方法
                teacher.deleteTeacherId(id)
                .then(response =>{
                    //提示信息
                    this.$message({
                        type: 'success',
                        message: '删除成功!'
                });
                    //回到列表页面
                    this.getList()
                }) 
            })  //点击取消,执行catch方法
        }

添加讲师

给save页面添加模板

<template>  
    <div class="app-container">  讲师添加
        <el-form label-width="120px"> 
            <el-form-item label="讲师名称"> 
                <el-input v-model="teacher.name"/>
            </el-form-item> 
        <el-form-item label="讲师排序">
             <el-input-number v-model="teacher.sort" controls-position="right" min="0"/>
        </el-form-item> 
        <el-form-item label="讲师头衔"> <el-select v-model="teacher.level" clearable placeholder="请选择">
         <el-option :value="1" label="高级讲师"/>
         <el-option :value="2" label="首席讲师"/>
        </el-select>
        </el-form-item> 
        <el-form-item label="讲师资历"> 
        <el-input v-model="teacher.career"/>
    </el-form-item>
    <el-form-item label="讲师简介"> 
     <el-input v-model="teacher.intro" :rows="10" type="textarea"/>
    </el-form-item>
    <el-form-item> 
        <el-button :disabled="saveBtnDisabled" type="primary" @click="saveOrUpdate">保存</el-button>
          </el-form-item>
        </el-form>
    </div>
</template>

<script>
export default {
    data(){
        return {
            teacher:{
                name:'',
                sort:0,
                level:1,
                career:'',
                intro:'',
                avator:''
            },
            saveBtnDisabled:false //保存按钮是否禁用
        }
    },
    created(){

    },
    methods:{

    }
}
</script>

在teacher.js中

 //添加讲师
    addTeacher(teacher){
        return request({
            url: `/eduservice/teacher/addTeacher`,
            method: 'post',
            data: teacher
        })
    }

2、在页面实现调用

 //添加讲师的方法
        saveTeacher(){
            teacherApi.addTeacher(this.teacher)
                .then(response =>{
                    //提示信息
                    this.$message({
                        type: 'success',
                        message:'添加成功'
                    });
                    //回到列表页面 路由跳转
                    this.$router.push({path:'/teacher/table'})
                })
        }

中文出现乱码,在数据库的连接后面加上

spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8

3、增加排序功能

//排序
queryWrapper.orderByDesc("gmt_create");

4、测试

排序没用

讲师修改

1、在每条记录后面添加 修改 按钮

2、点击修改按钮,进入表单页面,进行数据回显

3、通过路由跳转进入数据回显页面,在路由index页面添加路由

     {
        path:'edit/:id',
        name:'EduTeacherEdit',
        component:() => import('@/views/edu/teacher/save'),
        meta:{title:'编辑讲师',noCache:'true'},
        hidden:true
      }

更改

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RM0NPiNN-1622016021390)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210523162855813.png)]

4、在表单页面实现数据回显

(1)在teacher.js定义根据id查询接口

 getTeacherInfo(id){
        return request({
            url: `/eduservice/teacher/getTeacher/${id}`,
            method: 'get'     
        })
    }

(2)在页面调用接口实现数据回显

 //根据讲师id查询的方法
        getInfo(id){
            teacherApi.getTeacherInfo(id)
            .then(response => {
                this.teacher = response.data.teacher
            })
        },

(3)调用 根据id查询的方法

因为添加和修改使用save页面。区别添加和修改,只有修改时候查询数据回显

判断路径里面是否有讲师id值,如果有id值修改,没有id值直接添加

  created(){//页面渲染之前执行
        //判断路径是否有id值
        if(this.$route.params && this.$route.params.id){
            //从路径获取id值
            const id = this.$route.params.id
            //调用根据id查询的方式
            this.getInfo(id)
        }
        
    },

最终修改页面:

1、在api的teacher.js定义修改接口

 //修改讲师
    updateTeacher(){
        return request({
            url: `/eduservice/teacher/updateTeacher`,
            method: 'post',
            data:teacher     
        })
    }

2、在页面调用修改方法

   saveOrUpdate(){
            //判断修改还是添加
            //根据teacher里面是否有id
            if(this.teacher.id){
                //添加
                this.saveTeacher()
            }else{
                //修改
                this.updateTeacher()
            }
            //添加
            this.saveTeacher()
        },

增加修改讲师的方法

 //修改讲师的方法
        updateTeacher(){
            teacherApi.updateTeacherInfo(this.teacher)
                .then(response =>{
                      //提示信息
                    this.$message({
                        type: 'success',
                        message:'修改成功'
                    });
                    //回到列表页面 路由跳转
                    this.$router.push({path:'/teacher/table'})
                })
        },

遇到问题:

1、第一次点击修改,进行数据回显

第二次再去点击,添加讲师,进入表单页面,但是问题:表单页面还是显示修改回显数据,正确效果应该是表单数据清空

解决问题:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fgf5oh8a-1622016021391)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210523212002769.png)]

上面的代码没有解决问题,为什么?

多次路由跳转到同一个页面,在页面中created方法只会执行第一次,后面再进行调整就不会执行

最终解决,使用vue监听

 watch:{ //监听
      $route(to, from){ //路由变化方式,当路由发送变化,方法就会执行
          this.init()
      }  
    },

对象存储OSS

为了解决海量数据存储与弹性扩容,项目中我们采用云存储的解决方案-阿里云OSS

阿里云OSS管理控制台使用

1、使用oss,首先创建Bucket

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DdCRHW0J-1622016021392)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525111308881.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EJhSNuzW-1622016021393)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525111324634.png)]

2、准备工作,创建操作阿里云oss许可证(阿里云颁发id和密钥)

3、查看文档位置,引入依赖安装

<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.10.2</version>
</dependency>

上传讲师头像

环境搭建

1、新建service.service_oss模块

2、引入依赖

    <dependencies>
        <!-- 阿里云oss依赖 -->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
        </dependency>
        <!-- 日期工具栏依赖 -->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>
    </dependencies>

3、配置文件

#服务端口
server.port=8002
#服务名
spring.application.name=service-oss
#环境设置:dev、test、prod
spring.profiles.active=dev
 
#阿里云 OSS
#不同的服务器,地址不同
aliyun.oss.file.endpoint=your endpoint
aliyun.oss.file.keyid=your accessKeyId
aliyun.oss.file.keysecret=your accessKeySecret
#bucket可以在控制台创建,也可以使用java代码创建
aliyun.oss.file.bucketname=guli-file

4、启动主配置类

加上该注解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M7mFD5xn-1622016021393)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525141631965.png)]

后端接口实现

1、创建常量类,读取配置文件的内容

//当项目已启动,spring接口,spring卸载之后,执行接口一个方法
@Component
public class ConstantPropertiedUtils implements InitializingBean {

    //读取配置文件内容
    @Value("${aliyun.oss.file.endpoint}")
    private String endpoint;

    @Value("aliyun.oss.file.keyid")
    private String keyId;

    @Value("aliyun.oss.file.keysecret")
    private String keySecret;

    @Value("aliyun.oss.file.bucketname")
    private String bucketName;

    //定义公开静态常量
    public static String END_POINT;

    public static String ACCESS_KEY_ID;

    public static String ACCESS_KEY_SECRET;

    public static String BUCKET_NAME;


    @Override
    public void afterPropertiesSet() throws Exception {
        END_POINT = endpoint;
        ACCESS_KEY_ID = keyId;
        ACCESS_KEY_SECRET = keySecret;
        BUCKET_NAME = bucketName;
    }
}

2、创建controller,创建service

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wh3plx6R-1622016021395)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525145622138.png)]

@RestController
@RequestMapping("/eduoss/fileoss")
public class OssController {

    @Autowired
    private OssService ossService;

    //上传头像的方法
    @PostMapping
    public R uploadOssFile(MultipartFile file){
        //获取上传文件 MultipartFile
        //返回上传到oss的路径
        String url = ossService.uploadFileAvatar(file);
        return R.ok().data("url", url);
    }
}

3、在service实现上传文件到oss

@Service
public class OssServiceImpl implements OssService {

    //上传文件到oss
    @Override
    public String uploadFileAvatar(MultipartFile file) {
        //工具类获取值
        String endpoint = ConstantPropertiedUtils.END_POINT;

        String accessKeyId = ConstantPropertiedUtils.ACCESS_KEY_ID;
        String accessKeySecret = ConstantPropertiedUtils.ACCESS_KEY_SECRET;
        String bucketName = ConstantPropertiedUtils.BUCKET_NAME;

        try{
            // 创建OSSClient实例
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

            //获取上传文件的输入流
            InputStream inputStream = file.getInputStream();
            //获取文件名称
            String fileName = file.getOriginalFilename();

            // 调用oss方法实现上传
            //第一个参数 Bucket名称 第二个参数 上传到oss文件路径和文件名称
            //第三个参数 上传文件输入流
            ossClient.putObject(bucketName, fileName, inputStream);

            // 关闭OSSClient。
            ossClient.shutdown();

            //把上传之后的文件路径返回
            //需要把上传到阿里oss路径手动拼接出来
            String url = "http://"+bucketName+"."+endpoint+"/"+fileName;

            return url;

        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

4、测试 上传

在swagger中上传测试,注意文件不能太大

后端接口完善

在文件名称添加随机唯一值,让每个文件名称不同 把文件进行分类管理

实现年月日分类

     // 在文件名称里面添加随机唯一的值
            String uuid = UUID.randomUUID().toString().replaceAll("-","");
            //
            fileName = uuid + fileName;

            //把文件按照日期进行分类
            //获取当前日期
            String datePath = new DateTime().toString("yyyy/MM/dd");

            //拼接
            fileName = datePath+ "/"+ fileName;

下载nginx windows版

配置nginx:

在nginx.conf里面修改默认端口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PUaYf71m-1622016021396)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525200407231.png)]

配置nginx转发规则

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BsZmcsw3-1622016021397)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525203244643.png)]

前端中更改:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qSJ1tWR6-1622016021397)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525203507662.png)]

上传讲师头像

1、将组件复制到项目src/components里面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1RcUHkDO-1622016021398)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525210255188.png)]

2、添加讲师页面使用这个复制上传组件

	 <!-- 讲师头像 -->
    <el-form-item label="讲师头像">
    <!-- 头衔缩略图 -->
        <pan-thumb :image="teacher.avatar"/>
    <!-- 文件上传按钮 -->
        <el-button type="primary" icon="el-icon-upload"
            @click="imagecropperShow=true">更换头像
        </el-button>
    <!--
    v-show:是否显示上传组件
    :key:类似于id,如果一个页面多个图片上传控件,可以做区分
    :url:后台上传的url地址
    @close:关闭上传组件
    @crop-upload-success:上传成功后的回调 -->
        <image-cropper
        v-show="imagecropperShow"
        :width="300"
        :height="300"
        :key="imagecropperKey"
        :url="BASE_API+'/admin/oss/file/upload'"
        field="file"
        @close="close"
        @crop-upload-success="cropSuccess"/>
            </el-form-item>

3、使用组件

 //上传弹框组件是否显示
            imagecropperShow:false,
            imagecropperKey:0,//上传组件的key值
            BASE_API:process.env.BASE_API,//获取dev.env.js里面地址
            saveBtnDisabled:false //保存按钮是否禁用

4、引入组件和声明组件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J4E1HP0a-1622016021399)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525212506054.png)]

5、修改上传接口地址

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HahHmxKo-1622016021400)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525212622415.png)]

6、编写close方法和上传成功方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SLk31SNo-1622016021401)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525212842024.png)]

课程分类管理

EasyExcel

EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数量一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析

EasyExcel实现写操作

1、引入依赖

需要poi依赖配套,在guli-parent里已经引入

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.1</version>
        </dependency>

2、创建实体类,与excel中的数据对应

@Data
public class DemoData {

    //设置excel表头名称
    @ExcelProperty("学生编号")
    private Integer sno;

    @ExcelProperty("学生姓名")
    private String sname;
}

3、测试类

public class TestEasyExcel {

    public static void main(String[] args) {
        //实现excel写的操作
        //设置写入文件夹地址和excel文件名称
        String filename = "G:\\write.xlsx";

        //调用easyexcel里面的操作实现写操作
        //write方法里两个参数,第一个参数是文件路径名称,第二个参数是实体类的名称
        EasyExcel.write(filename,DemoData.class).sheet("学生列表").doWrite(getData());
    }

    //创建一个方法返回list集合
    private static List<DemoData> getData(){
        List<DemoData> list = new ArrayList<>();
        for (int i = 0; i < 10 ;i++){
            DemoData data = new DemoData();
            data.setSno(i);
            data.setSname("simon"+i);
            list.add(data);
        }
        return list;
    }
}

EasyExcel实现读操作

1、创建和excel对应实体类,标记对应列关系

@Data
public class DemoData {

    //设置excel表头名称
    @ExcelProperty(value = "学生编号",index = 0)
    private Integer sno;

    @ExcelProperty(value = "学生姓名", index = 1)
    private String sname;
}

2、创建监听进行excel文件读取

public class ExcelListener extends AnalysisEventListener<DemoData> {

    //一行一行的读取excel内容
    @Override
    public void invoke(DemoData demoData, AnalysisContext analysisContext) {
        System.out.println("***"+demoData);
    }

    //读取表头内容
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        System.out.println("表头"+headMap);
    }

    //读取完成之后
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
}

3、测试类

public static void main(String[] args) {
    //实现excel读的操作
    //设置读入文件夹地址和excel文件名称
    String filename = "G:\\write.xlsx";
    
    EasyExcel.read(filename,DemoData.class,new ExcelListener()).sheet().doRead();

}

结果为:

_API+’/admin/oss/file/upload’"
field=“file”
@close=“close”
@crop-upload-success=“cropSuccess”/>


3、使用组件

```vue
 //上传弹框组件是否显示
            imagecropperShow:false,
            imagecropperKey:0,//上传组件的key值
            BASE_API:process.env.BASE_API,//获取dev.env.js里面地址
            saveBtnDisabled:false //保存按钮是否禁用

4、引入组件和声明组件

[外链图片转存中…(img-J4E1HP0a-1622016021399)]

5、修改上传接口地址

[外链图片转存中…(img-HahHmxKo-1622016021400)]

6、编写close方法和上传成功方法

[外链图片转存中…(img-SLk31SNo-1622016021401)]

课程分类管理

EasyExcel

EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数量一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析

EasyExcel实现写操作

1、引入依赖

需要poi依赖配套,在guli-parent里已经引入

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.1</version>
        </dependency>

2、创建实体类,与excel中的数据对应

@Data
public class DemoData {

    //设置excel表头名称
    @ExcelProperty("学生编号")
    private Integer sno;

    @ExcelProperty("学生姓名")
    private String sname;
}

3、测试类

public class TestEasyExcel {

    public static void main(String[] args) {
        //实现excel写的操作
        //设置写入文件夹地址和excel文件名称
        String filename = "G:\\write.xlsx";

        //调用easyexcel里面的操作实现写操作
        //write方法里两个参数,第一个参数是文件路径名称,第二个参数是实体类的名称
        EasyExcel.write(filename,DemoData.class).sheet("学生列表").doWrite(getData());
    }

    //创建一个方法返回list集合
    private static List<DemoData> getData(){
        List<DemoData> list = new ArrayList<>();
        for (int i = 0; i < 10 ;i++){
            DemoData data = new DemoData();
            data.setSno(i);
            data.setSname("simon"+i);
            list.add(data);
        }
        return list;
    }
}

EasyExcel实现读操作

1、创建和excel对应实体类,标记对应列关系

@Data
public class DemoData {

    //设置excel表头名称
    @ExcelProperty(value = "学生编号",index = 0)
    private Integer sno;

    @ExcelProperty(value = "学生姓名", index = 1)
    private String sname;
}

2、创建监听进行excel文件读取

public class ExcelListener extends AnalysisEventListener<DemoData> {

    //一行一行的读取excel内容
    @Override
    public void invoke(DemoData demoData, AnalysisContext analysisContext) {
        System.out.println("***"+demoData);
    }

    //读取表头内容
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        System.out.println("表头"+headMap);
    }

    //读取完成之后
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
}

3、测试类

public static void main(String[] args) {
    //实现excel读的操作
    //设置读入文件夹地址和excel文件名称
    String filename = "G:\\write.xlsx";
    
    EasyExcel.read(filename,DemoData.class,new ExcelListener()).sheet().doRead();

}

结果为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rkMInjbB-1622016021403)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210525223120177.png)]

原文链接:https://blog.csdn.net/weixin_45720751/article/details/117294955



所属网站分类: 技术文章 > 博客

作者:黑暗之神

链接:http://www.javaheidong.com/blog/article/207482/188b92698744bec10a46/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

10 0
收藏该文
已收藏

评论内容:(最多支持255个字符)