发布于2021-05-29 20:54 阅读(2128) 评论(0) 点赞(10) 收藏(0)
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生成方案汇总
设置主键生成策略:
@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表示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;
}
}
@SpringBootApplication
public class EduApplication {
public static void main(String[] args) {
SpringApplication.run(EduApplication.class,args);
}
}
@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
前后端分离开发模式中,api文档是最好的沟通方式。
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
创建公共模块,整合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
复制代码好像有问题。。略过
安装
ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现
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 也完全能够为复杂的单页应用提供驱动。
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>
局部组件
<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是独立的项目,不是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 就是运行在服务端的 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
类似maven,用在前端中,管理前端js依赖,联网下载js依赖,比如jquery
1、npm项目初始化操作
使用命令
npm init -y
2、npm下载js依赖
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,在后端中,类与类之间的调用称为后端模块化操作
前端模块化
新建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))
新建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的.bin路径加入环境变量
使用webpack -v查看是否安装成功
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>
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']
}
]
}
}
# 解压压缩包
# 进入目录
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
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能大大减少占用内存的主要原因是在解析Excel时没有将文件数量一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析
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;
}
}
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能大大减少占用内存的主要原因是在解析Excel时没有将文件数量一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析
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;
}
}
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黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!