java实战2:利用Spring boot开发Restful 接口

文章目录

1:Spring boot介绍

1.1:SpringBoot框架

SpringBoot框架一般分为View层、Controller层、Service层、Mapper层、pojo层。

  • 1、View层:视图层,根据接到的数据展示页面给用户

  • 2、Controller层:响应用户需求,决定用什么视图,需要准备什么数据来显示。Controller层负责前后端交互,接收前端请求,调用Service层,接收Service层返回的数据,最后返回具体的数据和页面到客户端

  • 3、Service层:Service层也可以分为三个方面

    (1)接口:用来声明方法
    
    (2)继承实现接口
    
    (3)impl:接口的实现(将mapper和service进行整合的文件)
     Service层存放业务逻辑处理,有一些关于数据库处理的操作,但是不是直接和数据库打交道,有接口,
     也有接口的实现方法,在impl实现接口类中需要导入mapper类,mapper层是直接与数据库进行操作的。
    
    1. Mapper层:也可以称为DAO层,是数据库CRUD的接口,只有方法名,具体实现在mapper.xml文件中,对数据库进行数据持久化操作(把数据放到持久化的介质中,同时提供CRUD操作)
  1. src/main/resource文件夹中的mapper.xml文件,里面存储的是真正的数据库CRUD语句

  2. Pojo层:存放实体类,与数据库中的属性基本保持一致,一般包括getter、setter、toString方法(未使用插件lombok的情况下)

1.2:框架间联系

controller层(处理前台发送的请求)--->service定义接口(业务逻辑)--->serviceImpl(对接口函数进行实现)
--->mapper(Mapper接口,方法名与Mapper.xml中定义的statement的id相同)--->mapper.xml(写sql语句查询数据库)

在这里插入图片描述
由此可见,Service层在Mapper层之上,在Controller层之下,既调用Mapper接口,又提供接口给Controller层用。

分层后,访问数据库和进行Service之间分工明确,对Service的需求修改,无需修改Mapper层,如果有访问数据库的新需求,也只需要在Mapper层修改。

1.3:各层框架的使用

1:mapper层

mapper层一般单独的为一个包

  • 扫描mapper第一种方式
    每个mapper接口上添加@Mapper注解 这个注解会通过spring boot启动注解自动扫描

  • 扫描mapper第二种方式
    在main函数中使用@MapperScan(basePackages = “com.demo.mapper”)标注dao所在的包名,这种方式如果引入会有红色波浪线问题 再加上@Repository这个注解就没有了

  • 方式三
    在properties或者yml的配置文件中配置mybatis时按照正则匹配扫描相关的包

2:Controller层

@RestController注解表示每个方法返回的数据将直接写入响应体
我们有每个操作的路由(@GetMapping、@PostMapping、@PutMapping、@DeleteMapping。对应于 HTTP GET、POST、PUT和DELETE调用)

接口如何处理传参,我们可以使用@RequestParam注解实现获取前端URL中的参数。具体使用见例子

url请求:("/basic/user?name=小红")      此时会将name=小红传递给方法的name参数,多个参数使用&连接。

@GetMapping(value = "/user_info")
    public List<user> getUser(@RequestParam(value = "name") String name) {
    //此时我们可以通过定义的name属性获取url中属性为name的值。
    }

1.4:应用输出运行基本信息

spring boot可以使用spring.banner.location属性更改程序启动时打印的横幅。比如默认的spring boot图形,我们在resources目录下新建banner.txt将需要打印的内容写在其中即可

  
//                          _ooOoo_                               //  
//                         o8888888o                              //  
//                         88" . "88                              //  
//                         (| ^_^ |)                              //  
//                         O\  =  /O                              //  
//                      ____/`---'\____                           //  
//                    .'  \\|     |//  `.                         //  
//                   /  \\|||  :  |||//  \                        //  
//                  /  _||||| -:- |||||-  \                       //  
//                  |   | \\\  -  /// |   |                       //  
//                  | \_|  ''\---/''  |   |                       //  
//                  \  .-\__  `-`  ___/-. /                       //  
//                ___`. .'  /--.--\  `. . ___                     //  
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //  
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //  
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //  
//      ========`-.____`-.___\_____/___.-`____.-'========         //  
//                           `=---='                              //  
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //  
//            佛祖保佑       永不宕机      永无BUG                  //
 
**************************************************************************************
    ${application.title} ${application.version} 
    Base on Spring Boot ${spring-boot.version}
**************************************************************************************
System.properties:
--------------------------------------------------------------------------------------
    java.specification.version                = ${java.specification.version}
    java.specification.vendor                 = ${java.specification.vendor}
    java.specification.name                   = ${java.specification.name}
    java.vm.specification.version             = ${java.vm.specification.version}
    java.vm.specification.vendor              = ${java.vm.specification.vendor}
    java.vm.specification.name                = ${java.vm.specification.name}
    java.home                                 = ${java.home}
    java.version                              = ${java.version}
    java.vendor                               = ${java.vendor}
    java.vendor.url                           = ${java.vendor.url}
    java.vm.version                           = ${java.vm.version}
    java.vm.vendor                            = ${java.vm.vendor}
    java.vm.name                              = ${java.vm.name}
    java.class.version                        = ${java.class.version}
    java.class.path                           = ${java.class.path}
    java.library.path                         = ${java.library.path}
    java.io.tmpdir                            = ${java.io.tmpdir}
    java.ext.dirs                             = ${java.ext.dirs}
    os.name                                   = ${os.name}
    os.arch                                   = ${os.arch}
    os.version                                = ${os.version}
    user.name                                 = ${user.name}
    user.home                                 = ${user.home}
    user.dir                                  = ${user.dir}

使用时在application.proerties中配置banner.txt文件的位置。不配置也会默认加载。

spring.banner.location=classpath:banner.txt

运行查看效果

1.5各个服务模块间通信

同一个项目可能存在多个模块、或者多个spring项目间可能需要互相访问接口。

1.5.1:多模块间互相访问

1、使用FeignClient完成服务间调用、
使用示例:定义接口并使用注解,接口是的url地址就是自己的controller的组成的地址

# value的值是自己模块的名称:值一般在yml或者properties的spring.application.name指定
@FeignClient(contextId = "TestClient", value = "TestService")
public interface TestClient {
    @GetMapping({"test/list"})
    List<String> list(@RequestParam("name") String name);
}

2、restTemplate:详见1.5.2步骤

1.5.2:多项目间访问

可以使用restTemplate完成多个项目间调用,也可以完成多模块间的调用。
使用示例:其中的url就是自己要访问的地址

RestTemplate resttemplate=new RestTemplate();
ResponseEntity<String> entity=resttemplate.getForEntity(url,Striing.class)
//拿到响应的数据
String body=entity.getBody();

即可调用

2:Restful 介绍

  • http是在应用层的​超文本传输协议。
  • Restful 基于HTTP、URI、XML、JSON等标准和协议,支持轻量级、跨平台、跨语言的架构设计。是Web服务的一种新的架构风格(一种思想)。

其客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:
GET用来获取资源,
POST用来新建资源(也可以用于更新资源),
PUT用来更新资源,
DELETE用来删除资源;

其利用URL进行资源请求,根据http响应码,判断请求状态,进而做出提醒。
HTTTP响应码

1:restFul设计规范

URI = scheme “😕/” host “:” port “/” path [ “?” query ][ “#” fragment ]

scheme: 指底层用的协议,如http、https、ftp
host: 服务器的IP地址或者域名
port: 端口,http默认为80端口
path: 访问资源的路径,就是各种web 框架中定义的route路由
	通常一个RESTful API的path组成如下:
	/{version}/{resources}/{resource_id}
	version:API版本号,有些版本号放置在头信息中也可以,通过控制版本号有利于应用迭代。
	resources:资源,RESTful API推荐用小写英文单词的复数形式。
	resource_id:资源的id,访问或操作该资源。

query: 对于需要传递的参数可以使用?分隔后加到后面,比如key=value等
		查询字符串,为发送给服务器的参数,在这里更多发送数据分页、排序等参数。
fragment: 锚点,定位到页面的资源

1:url命名规范

  • 1:不用大写字母,所有单词使用英文且小写。
  • 2:连字符用中杠"-“而不用下杠”_"
  • 3:正确使用 "/"表示层级关系,URL的层级不要过深,并且越靠前的层级应该相对越稳定
  • 4:结尾不要包含正斜杠分隔符"/"
  • 5:URL中不出现动词,用请求方式表示动作
    *:6:资源表示用复数不要用单数
  • 7:不要使用文件扩展名

2:接口传参

接口传参可以使用@RequestParam 或者@RequestBody 或者@pathvariable

  • @RequestParam 传参可以是单独的参数;在请求的parma中,根据url的?后,多个值用?key=value,key=value方式拼接
  • @RequestBody则是多把参数都封装为了一个bean对象进行传参,放在请求体的body中
  • @pathvariable是将请求参数放置在url后,?前,多个值用’,'拼接。

接口传参可以使用@RequestParam注解实现,@RequestParam将查询字符串参数的值绑定属性名到方法的参数属性中。此查询字符串参数不是required。如果请求中不存在,则使用defaultValue

1、以@RequestParam举例

  @PostMapping (value = "/insert")
    public String insert(@RequestParam (value = "host") String host,@RequestParam (value = "port") String port,@RequestParam (value = "email") String email,@RequestParam (value = "report_name") String report_name,@RequestParam (value = "password") String password) {
        String sql = String.format("insert into insert_email_getaway_config_info(host,port,email,password,report_name)  values ('%s','%s','%s','%s','%s')", host, port, email, password, report_name);
        System.out.println(sql);
        hsqlDB.insertData(sql);
        return "insert success";
    }

接口调用,可以使用postman测试接口
url格式如下,后面参数和path用?分隔,各个参数间用&分隔

/insert?host=127.0.0.1&port=8888&email=1&password=1&report_name=小红

一般大于3个的多个参数建议用@RequestBody封装对象来传参,利于维护!!!

 @PostMapping(value = "/insert_email_getaway_config")
    public String insertEmailGetawayConfig(@RequestBody   EmailGetawayBean email) {
    //获取值 email.getEmail();
 }

@Data
public class EmailGetawayBean {
    String host, port, email, password, report_name;
}
1:按接口类型传参

get,post等不同接口类型传参一样没有区别

//接口使用举例 http://localhost:9999/view/get_info?type=sms
 @GetMapping(value = "/get_system_config")
  public List<systemConfig> getSystemConfigInfo(@RequestParam(required = false) String type) {//使用type}
2:按照参数类型传参

1、字符串和其他8种基本数据类型:可以直接传参在@RequestParam 或者@RequestBody 或者@pathvariable中。
2、批量删除等多使用@pathvariable+数组类型。传参为/v1,v2
3、数组[]:@RequestParam+数组时,同样使用param的特征?k=v,k=v格式,多个k一样的会自动合并到数组中。

2.1:数组array传参
 //数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
    @RequestMapping("/arrayParam")
    @ResponseBody
    public String arrayParam(String[] likes){
        System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));
        return "{'module':'array param'}";
    }
    
url请求传参:/arrayParam?likes=xm,likes=dm
2.2:集合list传参

4、集合List:集合和数组有点区别,list必须明确用@RequestParam否则报错

//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
    System.out.println("集合参数传递 likes ==> "+ likes);
    return "{'module':'list param'}";
}
传参:/listParam?likes=xm,likes=dm
对象多级嵌套的话是/listParam?likes.age=1,likes.name=vm格式
2.3:集合传参

3、list<对象>:这种多使用@RequestBody 注解在body中传参,对象为{"k":"v"},list为[]表示。则list<对象>=[{“k”:“v”},{“k”:“v”}]
4、单独pojo对象。可以使用@RequestParam 或者@RequestBody。
pojo如:

public class Address {
    private String province;
    private String city;
    //setter...getter...略
}
public class User {
    private String name;
    private int age;
    private Address address;
    //setter...getter...略
}

@RequestParam传参
在这里插入图片描述
请求参数key的名称要和POJO中属性的名称一致,否则无法封装

2.4:图片文件传参

上传完成后返回存储的路径

2.4.1:图片文件传参多使用MultipartFile file接受前端参数。

如图片上传String imageManager为图片对象json结构利用string接受后再使用json转换类型,直接使用对象类型会报错。MultipartFile file接受传参的文件图片

   @PostMapping
    public AjaxResult add(String imageManager, MultipartFile file) {
        ImageManager image = JSON.parseObject(imageManager, ImageManager.class);
        try {
            //存储绝对路径
            String upload = FileUploadUtils.upload(file);
            image.setAvatar(upload);
        } catch (IOException e) {
            throw new RuntimeException("图片上传失败");
        }
        image.setCreateTime(DateUtils.getNowDate());
        image.setUpdateTime(DateUtils.getNowDate());
        return toAjax(imageManagerService.insertImageManager(image));
    }
2.4.2:图片使用base64传参

前端传输将图片转换为base64格式的字符串,后端接受base64格式的字符串将其转换为二进制再通过文件流写入到服务器上。

/**
 * 将接收的base64转换成图片保存
 *
 * @param imgByte
 *            base64数据
 * @return 成功返回图片保存路径,失败返回false
 */
@RequestMapping("/saveToImgByStr")
@ResponseBody
public Object saveToImgByStr(String imgByte,String cardNum,HttpServletRequest request,HttpServletResponse response) {
	//存储路径
    String destDir = "/upload/image";
   //获取图片内容。Base64码的组成大概是这样的:图片格式;base64,图片内容
    imgByte=imgByte.replaceAll("data:image/png;base64,","");
//base64内容转为二进制
    BASE64Decoder decoder =  new BASE64Decoder();
    byte[] imageByte = null;
    try{
        imageByte = decoder.decodeBuffer(imgByte);
        for (int i = 0; i < imageByte.length; ++i) {
            if (imageByte[i] < 0) {// 调整异常数据
                imageByte[i] += 256;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    if (imageByte.length>0) {
        try {
            //获取文件上传的真实路径
            String uploadPath = request.getSession().getServletContext().getRealPath("/");
            //保存文件的路径
            String filepath = destDir + File.separator + createNewDir();
            File destfile = new File(uploadPath + filepath);
            if (!destfile.exists()) {
                destfile.mkdirs();
            }
            //文件新名称,为避免重复可使用uuid替代UUID.fastUUID().toString()
            String fileNameNew = getFileNameNew() + ".png";
            File f = new File(destfile.getAbsoluteFile() + File.separator + fileNameNew);
            /*将字符串转换成二进制,用于显示图片。用文件流写到服务器上,此处还可以使用//使用apache提供的工具类操作流,现成的更方便
          	 	   OutputStream out = new FileOutputStream(f.getPath());
         	 	 /  out.write(imageByte);
       	 	  	    out.flush();
        	     / out.close(); */
        	     
	  // 将上面生成的图片格式字符串 imgStr,还原成图片显示
            InputStream in = new ByteArrayInputStream(imageByte);
            FileOutputStream fos = new FileOutputStream(f);
           // BufferedOutputStream bos = new BufferedOutputStream(fos);
            byte[] buf = new byte[1024];
            int length;
            length = in.read(buf, 0, buf.length);

            while (length != -1) {
                fos.write(buf,0,length);
                length = in.read(buf);
            }
            fos.flush();
            fos.close();
            in.close();
            String lastpath = filepath + File.separator + fileNameNew;
            System.out.println("返回图片路径:" + lastpath);  
            return lastpath;

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }
    return false;
}
2.4.3:后端存储富文本(文字加图片)

方案1:富文本中的图片直接使用base64存储,后端使用String 字符串字段(数据库可使用LongTxt)接受整个富文本内容存储即可。

@ApiOperation("发表新闻")
    @PostMapping("/news")
    public Result updateNews(@RequestParam Long userId,@RequestParam Integer categoryId,@RequestParam String title,@RequestParam String context){
        System.out.println("发表新闻"+context);
        Result result = new Result();
        News news = new News(categoryId,userId,title,context);
        boolean flag = newsService.save(news);
        if (!flag){
            result.setFlag(false);
            return result;
        }
        result.setFlag(true);
        return result;
    }

方案2:富文本中的图片单独接受,接受后上传到服务器的指定路径,后端将副文本中的图片地址转为上传服务器后的地址。

2.5:Map传参
方法有一个map类型的参数或者传递的对象实体内部有一个map参数
/**
     * 请求参数
     */
    private Map<String, Object> params = new HashMap<>();
url请求传参:key值方到中括号里面即可。params[startTime]=value
比如http://localhost/system/interviewInfo/list?params[startTime]=2024-02-28
3:参数校验

依赖引用:
实现参数校验,程序必须引⼊ spring-boot-starter-validation 依赖。只是在引⼊ spring-boot-starter-web 依赖时,该模块会⾃动依赖 spring-boot-starter-validation,所以程序中引⼊ spring-boot-starter-web 会⼀并依赖spring-boot-starter-validation 到项⽬中。
使用:参数校验使用在pojo(domain)层或者接口传参里面。
@Size 、@NotBlank、@NotNull这几个注解是java中的

注解功能
@AssertFalse可以为null,如果不为null的话必须为false
@AssertTrue可以为null,如果不为null的话必须为true
@DecimalMax设置不能超过最⼤值
@DecimalMin设置不能超过最⼩值
@Digits设置必须是数字且数字整数的位数和⼩数的位数必须在指定范围内
@Future⽇期必须在当前⽇期的未来
@Past⽇期必须在当前⽇期的过去
@Max最⼤不得超过此最⼤值
@Min最⼤不得⼩于此最⼩值
@NotNull不能为null,可以是空
@Min最⼤不得⼩于此最⼩值
@Pattern必须满⾜指定的正则表达式
@Size集合、数组、map等的size()值必须在指定范围内
@Email必须是email格式
@Length⻓度必须在指定范围内
@NotBlank字符串不能为null,字符串trim()后也不能等于“”
@NotEmpty不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于“”
@Range值必须在指定范围内
@URL必须是⼀个URL

在具体的类中

public class User implements Serializable {
 
	 private Integer id;
	 
	 @NotBlank(message = "⽤户名不能为空!")
	 private String userName;
	 @NotBlank(message = "⽤户密码不能为空!")
	 @Length(min = 6, max = 10,message = "密码⻓度⾄少6位但不超过10位!")
	 private String userPwd;
	 
	 @Email
	 private String email;
	 
	 /*
	 省略get set ⽅法 
	 */
}

在传参中

   @ApiOperation("单参数校验")
    @PostMapping(value = "/valid/test3")
    public String test3(@Email String email){
        log.info("email is {}", email);
        return "email valid success";
    }

3:利用Spring boot开发restFul接口

1:创建pojo层

用于对应数据库对应的属性,必须提供对应的getter,setter,toString方法。如果使用lombok使用@data注解即可实现。

映射方式1:
Spring boot查询数据库底层使用mybatis进行交互,如果没有明确写明实体类和表的字段映射,就是同名对应,匹配成功则数据交互成功,匹配失败则数据交互失败,导致接收空值。

@Data
public class user{
String name,age,like,addr;
}

映射方式2:使用@data+@TableField
如:把表中的id映射到bean的user_id字段

@TableField(value="id")
private string user_id

多表关联查询存在字段重名或者不一致问题可以使用这种方式解决,不想使用@TableField也可以在mapper.xml配置中使用resultMap来完成映射。详见第四节

2:创建mapper层

底层利用mybatis和数据库进行交互,提供CRUD增删改查操作。
单独创建mapper的包放mapper类,使用注解@Mapper

利用@Select(“${sqlStr}”)创造一个查询语句,将查询结果映射为user
@Param(“sqlStr”) 注解为传参映射作用

@Mapper
public interface QueryHsqlMapper {
//根据路由和传参的sql语句执行数据库操作
    @Select("${sqlStr}")
    List<user> getData(@Param("sqlStr") String sqlStr);
}

利用类上的@Mapper注解可实现springboot启动时自动扫描。

使用步骤如下

  • 1:在main函数中使用注解表明该mapper类所在的包位置或者mapper类中使用@mapper注解。选其一
@MapperScan(basePackages = "com.demo.mapper")
@SpringBootApplication()
public class Main {
    private static final Logger LOG = LoggerFactory.getLogger(Main.class.getName());

    public static void main(String[] args) throws IOException, InterruptedException {
        SpringApplication.run(Main.class, args);
   }
  • 2:在resources目录下创建mapper.xml配置文件
    写入内容
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.demo.mapper.QueryHsqlMapper">
</mapper>
  • 3:application.properties文件中使用mapper.xml配置mybatis
mybatis.mapper-locations=classpath*:/mapper/*Mapper.xml
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.cache-enabled=false
2.1:和mybatis映射
1:对象内容嵌套映射
1.1:association嵌套映射

可以使用或者标签实现对嵌套结构的查询,mybatis会对该映射字段进行二次查询完成数据映射。
比如现在有两张表:用户表和部门表,希望查询用户表时查询出其所在部门的信息。

希望实现如下的数据结构

 {
            "userId": 1,
            "deptId": 103,
            "userName": "admin",
            "nickName": "管理员",        
            "dept": {
                "deptId": 103,
                "deptName": "研发部门"      
            }

正常写join查询所有字段后需要新建pojo对象接受很多的映射字段,但是mybatis自带了嵌套映射。
1、下面建表:

create table sys_user (
  user_id           bigint(20)      not null                   comment '用户ID',
  dept_id           bigint(20)      default null               comment '部门ID',
  user_name         varchar(30)     not null                   comment '用户账号',
  nick_name         varchar(30)     not null                   comment '用户昵称' 
    primary key (user_id)
) engine=innodb comment = '用户信息表';

create table sys_dept (
  dept_id           bigint(20)      not null                   comment '部门id',
  parent_id         bigint(20)      default 0                  comment '父部门id',
  ancestors         varchar(500)    default ''                 comment '祖级列表',
  dept_name         varchar(30)     default ''                 comment '部门名称',
  primary key (dept_id)
) engine=innodb comment = '部门表';

2、可以在xml中使用association 完成嵌套结构的映射。
通过将column列的结果作为条件进行二次查询数据库将查到的字段映射到SysDept对象结构上,再赋值给SysUser的dept属性(类型必须是SysDept)。这样就会得到希望的数据结构

    <resultMap type="SysUser" id="SysUserResult">
        <id property="userId" column="user_id"/>
        <result property="deptId" column="dept_id"/>
        <result property="userName" column="user_name"/>
        <result property="nickName" column="nick_name"/>
        <association property="dept" column="dept_id" javaType="SysDept" resultMap="deptResult"/>
    </resultMap>

下面介绍association 的多种用法

用法1:直接写其嵌套对象的映射信息
  <!--<association property="tenure" column="se_id" javaType="DbSessionBo">
            <result property="seId" column="se_id"/>
            <result property="area" column="area"/>
            <result property="annual" column="annual"/>
            <result property="session" column="session"/>
            <result property="order" column="order"/>
            <result property="isSession" column="is_session"/>
            <result property="orderId" column="order_id"/>
            <result property="startTime" column="start_time"/>
            <result property="endTime" column="end_time"/>
            <result property="remark" column="remark"/>
            <result property="createBy" column="create_by"/>
            <result property="createTime" column="create_time"/>
            <result property="updateBy" column="update_by"/>
            <result property="updateTime" column="update_time"/>
        </association>-->
用法2:嵌套的对象结构单独定义,使用resultMap再将其引入
    <association property="dept" column="dept_id" javaType="SysDept" resultMap="deptResult"/>
        
    <resultMap id="deptResult" type="SysDept">
        <id property="deptId" column="dept_id"/>
        <result property="parentId" column="parent_id"/>
        <result property="deptName" column="dept_name"/>
    </resultMap>
用法3:association +select标签。select指定通过那条sql进行查询、实现对映射字段查询的更精确的管理。
<association property="tenure" column="se_id" javaType="DbSessionBo" select="getDepartmentById"></association>

  <select id="getDepartmentById" parameterType="int" resultMap="DbSessionResult">
        select * from sys_dept where dept_id =#{dept_id}
   </select>

我在使用用法1、2中出现了部门字段没有映射的问题,最后采用方法3去解决的。
也可以使用上面几种用法定义resutmap,嵌套的字段名称不同时select查询语句可以使用join多表一次都查询字段出来也会自动映射。

1.2:collection嵌套映射

collection嵌套映射可完成1-1,1-多的映射,比如映射到list
比如select后表1一条数据唯一id对应表2的两条数据。
希望将表2的两条数据嵌套到表1对象的一个list《表2》中。

  • id可以使用<id 列指定唯一的主键确定数据。从而将其他的列可以收敛到list中
    javaType:选择list,resultMap进行字段映射。
<collection property="fileManagers" javaType="java.util.List" resultMap="SysFileManagerResult"/>

sql中查到的sys_file_manager的相关列就会映射到SysFileManagerResult上,对于suggest_info 中主表同一id的表2数据进行合并到list

    select s.*,f.file_id,f.absolute_path from suggest_info s left join sys_file_manager  f on s.su_id=f.su_id

3:创建service层

1:创建接口
2:实现接口,使用注解@Slf4j @Service

public interface QueryHsqlDB {
    List<user> getData(String sqlStr);
}

@Slf4j
@Service
public class QueryHsqlDBImple implements QueryHsqlDB {
    @Resource
    QueryHsqlMapper   mapper;

    @Override
    public List<user> getData(String sqlStr) {
        List<user> data = mapper.getData(sqlStr);
        return data;
    }
}

4:创建controller层

定义请求的方式(get/put/post/delete),请求的路径等,使用注解
@RestController
@CrossOrigin
@RequestMapping({“/data”}) //定义url访问的路径

@RestController
@CrossOrigin
@RequestMapping({"/data"})
public class Controller {

    @Resource
    private QueryHsqlDB queryHsqlDB;
    
    @GetMapping(value = "/user")
    public List<user> getuser() {
    String sql="select * from  user";
    return queryHsqlDB.getData(sql)
}

访问时路径即可/data/user即会调用getuser方法,将返回结果传给客户端
此种方式查询的sql是写死的,存在硬编码的问题,后续进行优化。

5:配置application.properties

springboot采用properties文件作为总配置文件,默认application.properties放在src/main/resource文件,在、该配置文件中可以使用通配符*用来表示相同类型的文件。比如多个Mapper.xml
下面介绍一些常用配置

# tomcat启动也就是访问web的端口号
server.port=11080
#xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mybatis.mapper-locations=classpath*:/mapper/*Mapper.xml
# 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
mybatis.configuration.map-underscore-to-camel-case=true

#jdbc数据库相关配置
#指定数据库驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库jdbc连接url地址,serverTimezone设置数据库时区东八区
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/login?characterEncoding=UTF-8&serverTimezone=GMT%2B8
#数据库账号
spring.datasource.username=root
spring.datasource.password=root

数据库的创建表和插入数据
#建表,利用*实现模糊匹配
spring.datasource.schema=classpath:create*.sql
#插入数据
#进行该配置后,每次启动程序,程序都会运行resources/insert.sql文件,对数据库的数据操作,相当于往表中插入数据。
spring.datasource.data=classpath:insert*.sql

#前端的静态资源加载,一般为resoources路径下的文件,个人创建/resources/templates目录存放
spring.resources.static-locations=classpath:templates/
spring.mvc.view.suffix=.html

# 自定义属性用于配置启动时自动打开浏览器
openProject.isOpen=true
openProject.web.openUrl=http://localhost:${server.port}/#/

自定义属性配置

#自定义属性
com.name="111"
com.name1="222"    
 # 取值需要在类上使用@Configuration注解后再使用@Value(value="${com.name}")的方式获取赋值并使用
@Value(value="${com.name}")
private  String name;//既可以将name值赋予变量    
5.1:获取配置的值

方式1:使用@envriment注解引入全部的配置管理再通过get获取
方式2:创建配置注解类,类上使用@data、@compent注解,创建局部变量加@value(“KaTeX parse error: Expected 'EOF', got '#' at position 43: …用类名.变量获取值 方式3: #̲ 取值需要在类上使用@Conf…{com.name}”)的方式获取赋值并使用,和方式2就是注解不一样而已
@Value(value=“${com.name}”)
private String name;//既可以将name值赋予变量

6:springboot启动自动打开浏览器

windows和linux均可,使用@Configuration完成和配置文件的映射,用@Value(“${属性}”) 获取属性的值

先在application.properties中定义属性

server.port=9999
openProject.web.openUrl=http://localhost:${server.port}/#/
@Configuration
public class GetLinuxIp {
    @Value("${openProject.web.openUrl}")   //完成属性映射
    private static String url;
    private static final Logger LOG = LoggerFactory.getLogger(GetLinuxIp.class.getName());

    public static String getInet4Address() {   //获取运行服务器的ip
        Enumeration<NetworkInterface> nis;
        String ip = null;
        try {
            nis = NetworkInterface.getNetworkInterfaces();
            for (; nis.hasMoreElements();) {
                NetworkInterface ni = nis.nextElement();
                Enumeration<InetAddress> ias = ni.getInetAddresses();
                for (; ias.hasMoreElements();) {
                    InetAddress ia = ias.nextElement();
                    //ia instanceof Inet6Address && !ia.equals("")
                    if (ia instanceof Inet4Address && !ia.getHostAddress().equals("127.0.0.1")) {
                        ip = ia.getHostAddress();
                    }
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }
        return ip;
    }
    
    public static void AutoOpenUrl() throws InterruptedException, IOException {//打开服务器的逻辑实现
        Runtime run = Runtime.getRuntime();
        String os = System.getProperty("os.name").toLowerCase();
        if (os.indexOf("win") >= 0) { //判断操作系统
            try {
                run.exec("rundll32 url.dll,FileProtocolHandler " + url);

            } catch (Exception e) {
                e.printStackTrace();
                LOG.error(e.getMessage());
            }
        } else if (os.indexOf("nix") >= 0 || os.indexOf("nux") >= 0) {
            String currentIp = GetLinuxIp.getInet4Address();
            String[] browsers = {"firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape"};
            String browser = null;
            for (int count = 0; count < browsers.length && browser == null; count++) {
                if (Runtime.getRuntime().exec(
                        new String[]{"which", browsers[count]}).waitFor() == 0) {
                    browser = browsers[count];
                }
            }
            if (browser != null) {
                Runtime.getRuntime().exec(new String[]{browser, url.replaceAll("localhost", currentIp)});
            }
        }
        LOG.info("启动浏览器打开项目成功:" + url);
    }
}

7:创建表和插入表

一般用于测试或者demo中的数据模拟,比如结合hsqldb等内存数据库,表的加载可以在application.properties中进行使用,详见步骤5。
比如创建用户表:create.sql

CREATE TABLE  user_info(
user_id  varchar(255) NOT NULL PRIMARY KEY, // PRIMARY KEY设置主键
name  varchar(255) DEFAULT NULL,
age varchar(255) DEFAULT NULL,
like  varchar(255) DEFAULT NULL,
addr  varchar(255) DEFAULT NULL
);

进行数据插入:insert.sql

insert into user_info values("001","小红","18","篮球","西安");

7:主类启动服务

SpringApplication提供了一种方便的方法来引导从main()方法启动的 Spring 应用程序。在许多情况下,您可以委托给静态SpringApplication.run方法,如下例所示:

如果在mapper类中已经使用了@mapper注解此处就不需要@MapperScan(basePackages = “com.demo.mapper”)

@MapperScan(basePackages = "com.demo.mapper") //用于扫描mapper文件,一般指定到包路径,在mapper层使用@mapper注解同理两种均可
@SpringBootApplication()  //main函数的固定注解
public class Main {
    private static final Logger LOG = LoggerFactory.getLogger(Main.class.getName());

    public static void main(String[] args) throws IOException, InterruptedException {
        SpringApplication.run(Main.class, args); //指定启动的主类即可
        LOG.info("started......");
} }

8:单元测试

常用测试工具

JUnit :单元测试 Java 应用程序的事实标准。

Spring Test & Spring Boot Test:Spring Boot 应用程序的实用程序和集成测试支持。

AssertJ:一个流畅的断言库。

Hamcrest:匹配器对象库(也称为约束或谓词)。

Mockito:Java 模拟框架。

JSONassert : JSON 的断言库。

JsonPath:JSON 的 XPath。

@SpringBootTest 指定测试类在SpringBoot环境下运行

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Main.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ControllerTest {

    @Autowired
    private WebApplicationContext webApplicationContext;
    private MockMvc mockMvc;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();//建议使用这种
    }

    @Test   /*用于模拟接口请求查看返回的结果*/
    public void testGet() throws Exception {
        ResultActions perform = mockMvc.perform(MockMvcRequestBuilders.get("/data"));
        String contentAsString = perform.andReturn().getResponse().getContentAsString();
        System.out.println(contentAsString);
        Assert.assertTrue(contentAsString.contains("小红"));
    }

9:项目pom.xml依赖

连接mysql的话需要引入相关依赖。
springboot常用依赖说明
依赖说明:
1:mybatis和mybatis-plus只引入其中一个包就行了,不要重复
2:结合spring boot引入的必须是 **<artifactId>mybatis-plus-spring-boot-starter</artifactId>**不能是mybatis-plus-core。否则会导致加载mapper.xml配置文件失败,报**Invalid bound statement (not found)**

 <groupId>org.example</groupId>
    <artifactId>smhll_redis_restful</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>smhll_redis_restful</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>

        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <fastjson>1.2.41</fastjson>
        <springboot>1.5.15.RELEASE</springboot>
        <redis.version>4.2.0</redis.version>

        <lombok.version>1.18.20</lombok.version>
        <commons.io.version>2.5</commons.io.version>
        <mybatis.version>2.1.2</mybatis.version>
        <commons.version>3.11</commons.version>
    </properties>

    
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
                  <version>${springboot}</version>
            <scope>test</scope>
        </dependency>


<!--   -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${springboot}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
            <version>${springboot}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${springboot}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>${springboot}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson}</version>
        </dependency>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>
        <!-- 添加内存数据库 org.hsqldb用于测试 -->
        <dependency>
            <groupId>org.hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>

        <!--还是引入其中一个实现类-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>
    </dependencies>
    <!--项目打包依赖-->
    <build>
        <plugins>
<!-- 打包插件,默认版本为最新的,若在pom中指定了springboot版本则为其版本,必须和使用的springboot版本保持一致-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <!--保证打包时可以将依赖也直接到到jar包中,不配置此处打的只有class文件-->
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>     <!--指定运行测试类的路径-->
                            <mainClass>com.demo.Main</mainClass>
                        </manifest>
                    </archive>
                </configuration>
                <version>3.3.0</version>
            </plugin>
<-- 编辑代码的插件--!>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

4:实战优化

4.1:第三节优化

关于第三节中controller中的sql硬编码问题进行优化。
采用sql放在mapper.xml中进行维护,后期更新配置不用改代码

4.2:springboot多条sql事务

事务是为了保证每个用户的每个操作都是一致的,只有失败或者成功。
比如controller接口中插入数据需要同时插入A、B两表,不使用事务的话可能存在一个成功一个失败的情况造成数据错误。

springboot提供了@Transactional 注解用来实现事务,使用在类或者方法上。

比如下面的方法,接口中同时执行两次insert,只能同时失败或全成功

   @Transactional
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody DbUser bo) {

        SysUser sysUser = new SysUser();
		sysUser.set**;

        Long l = userService.insertUserByDB(sysUser);
        if (l>0){
            bo.setUserId(l);
        }else {
            return toAjax(0);
        }
        return toAjax(iDbUserService.insertByBo(bo));
    }

4.4:获取springboot中的application.properties等配置文件中的配置

获取方式可以通过注解解析配置文件等多种方式

方式1: Environment 注入

private Environment environment;

4.5:输出执行sql日志

1:引入依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${springboot}</version>
        </dependency>

2:在application.properties配置日志

# 日志配置
logging:
  level:
    com.yd: debug
    org.springframework: warn

3:配置logback.yaml
可以通过此配置配置不同包下的输出日志级别,还可配置日志输出的文件路径格式等。

<logger name="com.yd" level="debug" />

5:问题汇总

5.1:Invalid bound statement (not found) .mapper文件

用mapper.xml映射文件中的 namespace 和 select 标签的 id 去需要我们是mapper类中的方法
原因1:配置文件中的包名和方法名可能写错找不到方法
原因2:mapper.xml配置文件压根没有找到。

解决1:修改正确的配置文件完成映射
解决2:查看配置文件的路径,同时必须在application.properties中进行配置。pom.xml依赖包必须用**<artifactId>mybatis-plus-spring-boot-starter</artifactId>**不能是mybatis-plus-core。否则会导致加载mapper.xml配置不到,报这种问题

5.2:a compent requied a bean of “自己定义的mapper类”,配置加载不到spring容器中

使用的mybatis maven依赖不能是mybatis-plus-boot-starter,必须是**<artifactId>mybatis-plus-spring-boot-starter</artifactId>**。

5.3:对于所有的找到不到mapper类或者加载不到配置文件的问题

1:查看相关注解是否添加,以及是否正确
2:application.yaml配置中是否配置mybatis.mapper-locations或者mybatis-plus.mapper-locations
用哪个必须和自己引用的依赖包是一致,否则找不到
3:resource下定义的*.mapper.xml中配置namespace 和 select 标签的 id 必须正确
4:mybatis引入的maven依赖必须是两个之一,不需要其他mybatis依赖了

<artifactId>mybatis-plus-spring-boot-starter</artifactId>`
<artifactId>mybatis-spring-boot-starter</artifactId>`

5.4:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):

网上已经有很多文章说明可能导致这个报错的原因,无非是以下几种:
1.检查xml文件的namespace是否正确,id和方法名是否一致

2.Mapper.java的方法在Mapper.xml中没有,然后执行Mapper的方法会报此

3.xxxMapper.java的方法返回值是List,而select元素没有正确配置ResultMap,或者只配置ResultType

4.mapper.xml中配置是否正确,返回值传参和bean对象是否能映射上,映射失败会报问题。

我的问题是在mapper.xml中指定了databaseId=“mysql”,删除后就正常运行了

5.5:Parameter ‘xxx’ not found. Available parameters are [arg1, arg0, param1, param2].

传入的参数和sppring容器中的参数映射失败了,在mapper,service层的方法参数上都加上**(@Param(“value”) String value)**,对传参加入注解完成映射就可以了

5.6:连接mongo时报错:java.lang.ClassNotFoundException:com.mongodb.event.ConnectionPoolCreatedEvent

在mongo驱动程序页面上:http://mongodb.github.io/mongo-java-driver/3.0/driver/getting-started/installation-guide/#mongodb-driver

您将看到以下文字:

注意:mongodb-driver需要以下依赖项:bson和mongodb-driver-core

因此,您需要以下所有依赖来完成这项工作:

mongodb-driver-3.0.1.jar,mongodb-driver-core-3.0.1.jar,bson-3.0.1.jar
将其依赖的jar包导入即可正常运行

5.7:Unable to interpret the implicit parameter configuration with dataType: , dataTypeClass: class java.

这是因为Swagger中的注解@ApiImplicitParam有一个属性为dataTypeClass,该属性的默认值为Void.class,因为没有指定dataTypeClass属性的值,所以报该警告信息。
解决:在@ApiImplicitParam注解上加上dataTypeClass属性的值,例如:

@ApiImplicitParams({
        @ApiImplicitParam(name = "username", value = "用户名", dataTypeClass = String.class, required = true)
})

5.8:springboot中的时区问题

sprinbboot中的时间主要涉及new Date()获取时间,dateformat、和数据库的交互、服务器时间四大步骤。在四大步骤中进行时区获取、时间转换格式化(Jackson框架)、时间的赋值时可能存在时间差问题。默认时区为Utc时区会和中国时区相差8小时,导致我们获取的时间会有问题

各步骤解决如下:

  • 1:服务器时间(包括服务器和容器)
    1.1:保证自己服务器的时间是正确的:
    可执行date命令查看,不正确先进行服务器时间校验
    1.2:若服务或者依赖的数据库等部署在docker容器中也需要保证自己容器的时间是正确的。dockerId是服务的容器di或者容器名称
    docker cp /usr/share/zoneinfo/Asia/Shanghai dockerId:/etc/localtime
  • 2:时间格式化
    在代码对象的属性上使用@JsonFormat(pattern=“yyyy-MM-dd HH:mm:ss”)注解或者使用SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");Date stationTime = dateFormat.parse(dateFormat.format(PayEndTime())时存在时间格式化
    默认时区都是Utc时区。需要指定为东八区中国时区,
    方法1:全局时区配置:在配置文件中添加
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

方法2:局部时区设置,在使用格式化的地方指定时区

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date time;
  • 3:数据库时区配置,在url中加serverTimezone=GMT%2B8
    url: jdbc:mysql://xxx:xx/db?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8

5.9:文件导入时:Failed to perform cleanup of multipart itemsjava.io.UncheckedIOException: Cannot delete

文件导入使用MultipartFile传参转stream流时未及时关闭
解决1:在finally中关闭stream:inputStream.close()
解决2:在获取stream时可以使用 Lombok 框架 @Cleanup 注解,主动释放资源:

5.10:数据导出excel时:The maximum length of cell contents (text) is 32767 characters

原因:excel导出时单个单元格的最大字符默认是32767,当数据过长时就会报错,所以需要在创建workbook.createSheet()后调用此方法即可。

public static void resetCellMaxTextLength() {
        SpreadsheetVersion excel2007 = SpreadsheetVersion.EXCEL2007;
        if (Integer.MAX_VALUE != excel2007.getMaxTextLength()) {
            Field field;
            try {
                field = excel2007.getClass().getDeclaredField("_maxTextLength");
                field.setAccessible(true);
                field.set(excel2007,Integer.MAX_VALUE);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

5.11:Java后台接收前台请求参数(<>、#¥%&、空格等)被转义成&amp;lt;gt;&quot;等字符

后台做个处理:(利用org.apache.commons.lang包里的StringEscapeUtils解码方法)

String category = StringEscapeUtils.unescapeHtml(request.getParameter(“参数名”));

5.12:mybatis查询映射时间date类型时报错:java.sql.SQLException: HOUR_OF_DAY: 0 -> 1

问题描述:mysql数据库中字段类型为date,查询时pojo字段类型为date。JsonFormat格式化数据。

  @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private Date startTime;

查询mysql时间类型某条数据突然报错java.sql.SQLException: HOUR_OF_DAY: 0 -> 1。排查发现时间是1986-05-04。分析得到此时间是夏令时时间。

夏令时时间如下,也就是说字段使用date类型、东八区时区转换时候,下列的的时间转换可能会出现问题,

1992年起,夏令时暂停实行。
1935年至1951年,每年5月1日至9月30日
1952年3月1日至10月31日
1953年至1954年,每年4月1日至10月31日
1955年至1956年,每年5月1日至9月30日
1957年至1959年,每年4月1日至9月30日
1960年至1961年,每年6月1日至9月30日
1974年至1975年,每年4月1日至10月31日
1979年7月1日至9月30日
1986年至1991年,每年4月中旬的第一个星期日2时起至9月中旬的第一个星期日2时止。具体如下:
1986年5月4日至9月14日,
1987年4月12日至9月13日,
1988年4月10日至9月11日,
1989年4月16日至9月17日,
1990年4月15日至9月16日,
1991年4月14日至9月15日。

解决:
1、在 MySQL 的连接参数上加入useLegacyDatetimeCode=false和serverTimezone=Asia/Shanghai,此种方式实测数据库使用date类型时无效
2、将代码里面的生日类型从Date改为LocalDate,实测有效

  @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private LocalDate startTime;

3、方式3数据库定义时候字段使用dateTime类型,实测有效

上面三种方式都可以尝试一下。

  • GMT+8 和 Asia/Shanghai 的区别
    GMT+8 因为没有位置信息,所以无法使用夏令时
    Asia/Shanghai 使用夏令时

5.13:接口响应中文乱码问题

流程spriingboot-mybatis-数据库。
常见的中文乱码问题是因为spriingboot解析中文导致的,此时进行设置编码。
方法1:在application.yaml中配置 charset: UTF-8

server:
	servlet:
		encoding:
			charset: UTF-8

方法2:在api上指明响应编码

@GetMapping(value = "/api", produces = "application/json;charset=UTF-8")

设置后即可在响应的head中看到编码格式
在这里插入图片描述
若上述问题仍旧无法解决中文乱码进行数据库乱码排查。(我的修改后还是乱码是因为mysql容器默认编码不是utf-8导致的,参见下面方式2解决)
方法1:查看数据库中表的数据中文是否乱码。乱码的话设置表的编码格式。

建表和数据库的时候指定编码格式
CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

方式2:若使用的是docker mysql,则修改容器的编码格式为C.UTF-8即可

docker exec -it mysql env LANG=C.UTF-8 bash

此时若还是乱码则吧数据清除后重新入库即可正常。

5.14:前端传递参数中富文本字段中的标签样式丢失

现象:前端传递html样式参数如 "content": "<p>2222</p>"到后端后发现样式丢失为 “content”: “2222”

解决
配置文件application.yml配置了
xss拦截攻击,默认会把字符串中含有代码的标签过滤,把需要用的接口加上成功解决
在这里插入图片描述

6:mybatis介绍

mybatis-plus官网介绍:https://baomidou.com/
mybatis官网介绍:https://mybatis.net.cn/

6.1:介绍

6.2:mapper.xml详解

MyBatis的关注点在POJO与SQL之间的映射关系,mapper.xml就是实现这种关系的。里面还可以加if判断,foreach循环等操作。接口类型包括select查询,insert插入,delete删除等等对于sql语句。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.training.mapper.UserMapper">
    
    <!--定义接口方法对应的 SQL 语句,传参通过mapper类传入-->
     <select id="findByName" parameterType="String" resultType="com.training.entity.User">
        select * from user where name=#{name}
    </select>
    
    <!--多个传参时parameterType可以忽略,取值通过下标取值-->
	<select id="getByNameAge" resultType="com.xf.pojo.User">
       select * from t_user where name = #{0} and age = #{1}
</select>
</mapper>

namespace:配置我们mapper类的位置
select id:配置我们的mapper类中的查询方法名,
parameterType:定义传参的类型
resultType:sql查询结果映射到那个pojo类

对于pojo字段映射麻烦的关联查询可以使用resultMap 映射

<resultMap type="customer" id="customerMap">
       <id property="id" column="c_id"/>
       <result property="name" column="c_name"/>
</resultMap>

<!--type是pojo类名,id是定义resultMap 自己的id,用于在写sql模块时resultType属性进行配置-->

在这里插入图片描述

6.3:mybatis写入获取自增主键

springboot+mybatis+mysql
数据写入mysql,mysql表设置了自增主键,现在从spring写接口进行数据插入。
1:controller层传参使用封装的bean对象,不用传主键
2:mapper将bean对象传入
3:mapper.xml配置 <insert id=“id” useGeneratedKeys="true"后。mybatis会去查找最大的主键然后赋值给bean对象完成传参,此处我们只用#{id}就可获取值

 <insert id="id" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO t_user(id,username,password) VALUES(#{id}, #{username}, #{password});
   </insert>

6.4:@注解实现插入字段值自动填充

在pojo中可以使用 注解在对应的操作时自动更新某字段,比如创建时间,更新时间,而不用自己去处理。

  • 1、普通字段 @TableField(fill = FieldFill.INSERT_UPDATE)
    使用步骤
  • 自定义对拦截器进行实现,并在拦截器中对公共字段进行赋值,比如创建时间等
1、实现MetaObjectHandler 接口,对其中自动注入的参数进行统一赋值
2、pojo字段上使用注解 @TableField(fill = FieldFill.INSERT_UPDATE)

实现MetaObjectHandler 接口

@Slf4j
public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        try {
            if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity) {
                BaseEntity baseEntity = (BaseEntity) metaObject.getOriginalObject();
                Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime())
                    ? baseEntity.getCreateTime() : new Date();
                baseEntity.setCreateTime(current);
                baseEntity.setUpdateTime(current);
                String username = StringUtils.isNotBlank(baseEntity.getCreateBy())
                    ? baseEntity.getCreateBy() : getLoginUsername();
                // 当前已登录 且 创建人为空 则填充
                baseEntity.setCreateBy(username);
                // 当前已登录 且 更新人为空 则填充
                baseEntity.setUpdateBy(username);
            }
        } catch (Exception e) {
            throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
        }
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        try {
            if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity) {
                BaseEntity baseEntity = (BaseEntity) metaObject.getOriginalObject();
                Date current = new Date();
                // 更新时间填充(不管为不为空)
                baseEntity.setUpdateTime(current);
                String username = getLoginUsername();
                // 当前已登录 更新人填充(不管为不为空)
                if (StringUtils.isNotBlank(username)) {
                    baseEntity.setUpdateBy(username);
                }
            }
        } catch (Exception e) {
            throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
        }
    }

    /**
     * 获取登录用户名
     */
    private String getLoginUsername() {
        LoginUser loginUser;
        try {
            loginUser = LoginHelper.getLoginUser();
        } catch (Exception e) {
            log.warn("自动注入警告 => 用户未登录");
            return null;
        }
        return ObjectUtil.isNotNull(loginUser) ? loginUser.getUsername() : null;
    }

}
  • 2、主键字段:对于主键自动注入,在主键字段上使用注解 @TableId(value = “user_id”,type = IdType.AUTO)

示例

 /**
     * 用户id
     */
    @TableId(value = "user_id",type = IdType.AUTO)
    private Long userId;

6.4:resutMap复杂映射

可以使用和实现复杂对象、list、map的嵌套
可以实现<id 定义唯一结果列从而实现其他列数据的汇聚。
比如表1学生表,表2分数表。希望实现每个学生所有的学科分数在一个结果中

resutMap和查询sql

<resultMap type="com.mobile.ipc.domain.LzCount" id="LzCountResult">
        <id property="userId" column="db_id"/>
        <result property="userName" column="user_name"/>
        <result property="sum" column="sum"/>
        <collection property="rules" ofType="com.mobile.ipc.domain.DbRule"  javaType="java.util.ArrayList">
            <id property="id" column="rule_id"/>
            <result property="rule" column="rule"/>
            <result property="score" column="score"/>
        </collection>

    </resultMap>
    <select id="queryCount" parameterType="String" resultMap="LzCountResult">
        select e.db_id,e.user_name,e.rule_id,e.rule,e.score from (
        select a.db_id,d.user_name,a.rule_id,r.rule,sum(r.score) as score from lz_activity a
        inner join db_user d on a.db_id=d.user_id
        inner join db_rule r on a.rule_id=r.id
        <where>
            <if test="userName != null and userName != '' ">d.user_name like CONCAT(CONCAT('%', #{userName}), '%')</if>
        </where>
        GROUP BY a.db_id,a.rule_id
        ) e
    </select>

实体类

@Data
public class LzCount {
    /**
     * 用户id
     */
    private Long userId;
    /**
     * 用户名称
     */
    //@NotBlank(message = "用户名称不能为空", groups = { AddGroup.class, EditGroup.class })
    @Schema(name = "userName")
    private String userName;

    @Schema(name = "sum")
    private Long sum;

    private List<DbRule> rules;
}

结果

{
            "userId": 6,
            "userName": "测试1",
            "sum": 222,
            "rules": [
                {
                    "id": 100,
                    "rule": "数学",
                    "score": 10
                },
                {      
                    "id": 101,
                    "rule": "语文",
                    "score": 212
                }
            ]
        }```

  • 12
    点赞
  • 115
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: Spring Boot是一个基于Java的开源框架,它提供了一系列工具和配置来帮助开发人员快速搭建新应用。 Spring Boot能够将应用程序部署到任何Java应用服务器,并提供了许多方便的功能,如自动配置、日志记录和应用监控。 如果你想学习使用Spring Boot开发应用程序,你可以从以下文档开始: 1. Spring Boot官方文档: https://docs.spring.io/spring-boot/docs/current/reference/html/ 2. Spring Boot入门指南: https://spring.io/guides/gs/spring-boot/ 3. Spring Boot教程: https://www.tutorialspoint.com/spring_boot/index.htm 这些文档将为你提供Spring Boot的基础知识,并帮助你开始使用Spring Boot开发应用程序。 ### 回答2: Spring Boot是一个用于简化Java开发的框架,是基于Spring框架的一个子项目。Spring框架是一个非常强大和流行的Java开发框架,但是在配置方面可能较复杂。而Spring Boot的出现就是为了解决这个问题,它通过自动化配置和约定大于配置的原则,极大地简化了Spring应用的开发和部署过程。 Spring Boot的学习文档主要包括以下内容: 1. 简介和入门:文档首先会介绍Spring Boot的基本概念和背景,为什么要使用Spring Boot以及它的优势。然后,会帮助读者快速搭建一个简单的Spring Boot项目,并编写第一个Hello World程序。 2. 核心特性:文档将详细介绍Spring Boot的核心特性和功能,如自动配置、起步依赖、外部化配置和监控等。读者会了解到这些特性如何帮助开发人员更高效地开发应用程序。 3. Web开发Spring Boot在Web开发方面提供了很多方便的功能,如快速创建RESTful API、集成持久层框架和模板引擎等。学习文档将详细介绍如何使用Spring Boot进行Web开发,并提供一些常见场景的示例。 4. 数据访问:文档将介绍如何使用Spring Boot集成各种数据库和持久层框架,如JPA、MyBatis等,并提供最佳实践和示例代码。 5. 测试和部署:文档将介绍如何使用Spring Boot进行单元测试和集成测试,并提供一些常用的测试框架和工具。同时,也会介绍如何将Spring Boot应用部署到不同的环境中。 6. 扩展和配置:Spring Boot支持通过自定义配置文件、自定义Starter和自定义注解等方式进行扩展。文档将详细介绍如何使用这些扩展机制进行定制开发。 总体而言,Spring Boot学习文档会从入门到深入地介绍Spring Boot的各个方面,帮助读者快速掌握和应用Spring Boot框架。同时,文档也会提供丰富的示例代码和最佳实践,便于读者进行实践和理解。 ### 回答3: Spring Boot是一个基于Java的开源框架,用于快速构建独立的、生产级别的Spring应用程序。它的学习文档包含了以下几个方面: 1. 安装和配置:学习文档会介绍如何安装和配置Spring Boot框架,包括下载安装包、设置环境变量等等。 2. 快速入门:学习文档会提供一个简单的示例,帮助初学者快速了解Spring Boot的基本概念和用法。通过这个示例,学习者可以了解如何创建一个简单的Spring Boot应用,并通过内置的嵌入式服务器来运行应用。 3. 核心概念:学习文档会介绍Spring Boot框架的核心概念,如自动配置、起步依赖、外部化配置等。通过学习这些核心概念,学习者可以更好地理解和应用Spring Boot框架。 4. 高级特性:学习文档还会介绍一些Spring Boot的高级特性,如Spring Boot Actuator、Spring Boot Data等。这些特性可以帮助开发者更加方便地进行应用程序的监控和管理,以及数据库访问等操作。 5. 实战项目:学习文档可能会提供一些实战项目,供学习者根据文档步骤实践,并通过实践来深入理解Spring Boot框架的应用。 综上所述,Spring Boot的学习文档提供了从安装配置到核心概念再到高级特性的全面介绍,通过学习文档,可以帮助初学者快速上手Spring Boot框架,并深入理解和应用其功能特性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值