# SpringBoot
# 一、SpringBoot最简项目构建
# 1. 环境要求
推荐环境:
1. JDK 1.8+ #验证方式 java -version
2. Maven 3.6.0+ #验证 mvn -v
3. IntelliJ IDEA、STS 使用IDEA应该更舒服
4. 推荐辅助工具: notepad++/vscode/typora/
2
3
4
5
# 2. maven项目结构
sb-demo
-src #代码源文件
-main
-java #java源代码
-resources #资源文件,配置文件 eg: 前端static页面文件/application.yml/mybatis的xml文件等
-test
-java #单元测试
-resources #单元测试资源文件
-pom.xml #maven项目核心文件,配置依赖等等
2
3
4
5
6
7
8
9
# 3. pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 本项目坐标 必要 -->
<groupId>com.bjq.demo</groupId>
<artifactId>sb-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 打包方式-jar,默认jar -->
<packaging>jar</packaging>
<!-- 本项目描述信息,可选 -->
<name>my springboot demo</name>
<description>first springboot demo</description>
<!-- 父级模块 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
</parent>
<!-- 项目属性设置 -->
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<!-- 项目依赖 -->
<dependencies>
<!-- SpringBoot-web启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!-- springboot maven打包插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 4. 导入IDEA或者其他IDE
# 5. 启动类
/**SpringBoot核心注解 必选**/
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
2
3
4
5
6
7
# 二、SpringBoot静态资源访问配置
# 1. SpringBoot默认静态资源路径
- SpringBoot默认将静态资源所有的访问映射到以下路径
1. classpath:/static
2. classpath:/public
3. classpath:/resources
4. classpath:/META-INF/resources
2
3
4
测试:在main/resources
下新建static
、public
、resources
等文件夹,分别放入a.png
、b.png
、c.png
三张图片,启动项目,访问:
http://localhost:8080/a.png
http://localhost:8080/b.png
http://localhost:8080/c.png
2
3
# 2.SpringBoot 自定义静态资源路径
# 1. 通过配置文件配置
通过application.yml或者application.properties配置静态资源加载路径
spring:
#静态资源访问路径
mvc.static-path-pattern: /demo/**
#静态资源映射路径,带上SpringBoot默认的路径
resources.static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/img/
2
3
4
5
启动,访问:http://localhost:8080/demo/test.png
# 2. 通过配置类配置
@Configuration
public class MyWebMvcConfigurerAdapter implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 将/static/**访问映射到classpath:/img/
registry.addResourceHandler("/demo/**").addResourceLocations("classpath:/img/");
}
}
2
3
4
5
6
7
8
9
启动,访问:http://localhost:8080/demo/test.png
# 三、封装统一返回数据格式
# 1. Result.java
package com.bjq.demo.common;
import java.io.Serializable;
public class Result<T> implements Serializable {
private static final long serialVersionUID = -7815237591909039836L;
private Integer code;
private String msg;
private T data;
public Result() {
}
public Result(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Result(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 2. ResultUtil.java
package com.bjq.demo.common;
public class ResultUtil {
private static final String MSG_SUCCESS = "success";
private static final String MSG_FAIL = "fail";
public static Result success() {
return success(null);
}
public static Result success(Object data) {
return new Result(0, MSG_SUCCESS, data);
}
public static Result fail(Integer code, String msg) {
return new Result(code, MSG_FAIL);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
以上两个类可以合并为一个,但是为了减少代码的冗余,拆了开来。
# 3. Rest接口返回参数 ResponseBody构造器 ajax/rest web服务返回 RestResponse.java
package com.biubiu.admin.util;
import java.util.HashMap;
/**
* RestResponse ResponseBody构造器 ajax/rest web服务返回
*
* @author biubiu
*/
public class RestResponse extends HashMap<String, Object> {
public static RestResponse success() {
return success("success");
}
public static RestResponse success(String message) {
RestResponse response = new RestResponse();
response.setSuccess(true);
response.setMessage(message);
response.setCode(0);
return response;
}
public static RestResponse failure(String message) {
RestResponse response = new RestResponse();
response.setSuccess(false);
response.setMessage(message);
response.setCode(-1);
return response;
}
public RestResponse setSuccess(Boolean success) {
if (success != null) {
put("success", success);
}
return this;
}
public RestResponse setMessage(String message) {
if (message != null) {
put("message", message);
}
return this;
}
public RestResponse setData(Object data) {
if (data != null) {
put("data", data);
}
return this;
}
public RestResponse setCode(Integer code) {
if (code != null) {
put("code", code);
}
return this;
}
public RestResponse setPage(Integer page) {
if (page != null) {
put("page", page);
}
return this;
}
public RestResponse setCurrentPage(Integer currentPage) {
if (currentPage != null) {
put("page", currentPage);
}
return this;
}
public RestResponse setLimit(Integer limit) {
if (limit != null) {
put("limit", limit);
}
return this;
}
public RestResponse setTotal(Integer total) {
if (total != null) {
put("total", total);
}
return this;
}
public RestResponse setAny(String key, Object value) {
if (key != null && value != null) {
put(key, value);
}
return this;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# 四、SpringBoot全局异常处理
/**
* 全局异常拦截器
*/
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
private static final String logExceptionFormat = "Capture Exception By GlobalExceptionHandler: Code: %s Detail: %s";
private static Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
//运行时异常
@ExceptionHandler(RuntimeException.class)
public String runtimeExceptionHandler(RuntimeException ex) {
return resultFormat(1, ex);
}
//空指针异常
@ExceptionHandler(NullPointerException.class)
public Map<String, Object> nullPointerExceptionHandler(NullPointerException e) {
System.err.println("NullPointerException:");
return resultFormat(2, ex);
}
//格式转化
private <T extends Throwable> String resultFormat(Integer code, T ex) {
ex.printStackTrace();
log.error(String.format(logExceptionFormat, code, ex.getMessage()));
Result result = ResultUtil.fail(code, ex.getMessage());
return JSON.toJSONString(result);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 五、SpringBoot配置文件
SpringBoot默认配置文件为application.properties
或者application.yml
# 1. YAML格式语法
大小写敏感,注意格式
对象、Map写法
- 写法一:
friends: lastName: zhangsan age: 20
1
2
3
4- 行内写法
friends: {lastName: zhangsan, age: 20}
1
2数组 List Set
- 写法一
pets: - cat - dog - pig
1
2
3
4
5- 行内写法
pets: [cat,dog,pig]
1
2随机数
random1: ${random.long} random2: ${random.int(10)} random3: ${random.int[1024, 65536]}
1
2
3
4
# 2. YAML举例
user:
lastName: hello
age: 18
boss: false
birth: 2019/12/12
maps: {qq: 10000, moblie: 13811002233}
lists:
- zhaoliu
- lisi
dog:
name: 张三
age: 4
2
3
4
5
6
7
8
9
10
11
12
13
整体映射:
//指定配置文件
@PropertySource(value="classpath:application.yml")
@Component
ConfigurationProperties(prefix = "user")
public class User {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String, Object> maps;
private List<Object> lists;
private Dog dog;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
只娶一个值
@Value("${demo.demo}")
private String 属性;
2
3
yml和properties同时存在,yml覆盖properties
配置文件加载路径
启动时扫描以下路径下的application.properties application.yml文件作为SpringBoot的默认配置文件
-file:./config/
-file:./
-classpath:/config/
-classpath:/
优先级由高到低
2
3
4
5
6
7
8
dev test pro环境配置文件切换 多profile application-dev.yml application-test.yml application-prod.yml
激活指定的profile
- 配置文件指定 spring.profiles.active = dev
- 命令行 Java -jar sb-demo.jar --spring.profiles.active = dev;
- 虚拟机参数 -Dspring.profiles.active=dev
Themeleaf
Model 数据 ModelAndView 数据视图
整合JPA
2jar--- msyql驱动 spring-boot-starter-data-jpa
RestFul设计
/addBook/{name}
jpa步骤 1.添加依赖 2.配置application 3.实体类和dao层接口 4.service接口 5.controller
整合MyBatis 1.依赖 mysql-connector-java mybatis-spring-boot-starter 2.配置文件 数据库 3.mapper接口 --注解形式 4.service接口 5.controller 6.配置扫描包 @MapperScan(basePackages="com.demo.dao")
整合Redis 1.依赖 springboot-satrter-data-redis 2.配置
# 六、SpringBoot集成Swagger
# 七、第三方jar在SpringBoot中的使用
# 1. 发布第三方jar到maven库,然后引入依赖(我还没试呢)
# 2. 放在项目中(重点说这个)
- 使用到的第三方jar放入项目中,位置随意。这里放在classpath:/jar/
在pom中添加依赖
这里project.basedir是项目路径
<dependency> <groupId>local-jar</groupId> <artifactId>QrCodeGenerator</artifactId> <version>1.0.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/jar/QrCodeGenerator.jar</systemPath> </dependency> <dependency> <groupId>local-jar</groupId> <artifactId>zxing-core</artifactId> <version>1.0.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/jar/zxing-core-2.2.jar</systemPath> </dependency> <dependency> <groupId>local-jar</groupId> <artifactId>zxing-javase</artifactId> <version>1.0.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/jar/zxing-javase-2.2.jar</systemPath> </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 八、SpringBoot打包部署
# 1. jar包部署
添加一个插件
<!-- 打包插件 --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
1
2
3
4
5
6
7
8
9这种使用SpringBoot内嵌的Tomcat进行部署。SpringBoot在不指定打包的时候默认jar包形式
<packaging>jar</packaging>
1使用命令行启动部署
java -jar xxx.jar
1
# 注意存在第三方jar的情况
在第七章的基础上需要添加如下配置,然后打包后会把第三方jar打包到jar包中。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources/jar</directory>
<targetPath>BOOT-INF/lib/</targetPath>
<includes>
<include>**/*.jar</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<targetPath>BOOT-INF/classes/</targetPath>
</resource>
</resources>
</build>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2. war包部署
添加插件如上
打包方式修改为war
<packaging>war</packaging>
1
2添加tomcat插件,spring boot本身有一个内嵌的tomcat,如果不做其他配置直接打包,就会生成一个jar包。 所以我们引入外部tomcat
<!-- 外部 tomcat --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency>
1
2
3
4
5
6在启动类中做如下配置,继承SpringBootServletInitializer,重写configure方法
@SpringBootApplication public class MyApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(MyApplication.class); } }
1
2
3
4
5
6
7
8
9
10
11
12
13打包(war)
- 切换到项目所在路径,使用命令
mvn clean package
- 使用IDE工具,例如IDEA自带打包工具
- 切换到项目所在路径,使用命令
部署,拷贝war包到tomcat的webapps路径下,启动tomcat
注意:这种war包方式端口号使用tomcat的,项目名使用war包的名字,对于application.yml配置文件里指定的端口号和项目名会覆盖掉。
# 注意第三方jar的情况
使用maven的打包插件,打包方式还是如上。
<build>
<!--设置maven-war-plugins插件,否则外部依赖无法打进war包 -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webResources>
<resource>
<directory>src/main/resources/jar/</directory>
<targetPath>WEB-INF/lib</targetPath>
<filtering>false</filtering>
<includes>
<include>**/*.jar</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>
</plugins>
</build>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 九、SpringBoot整合JPA
# 1. 需要的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
2
3
4
5
6
7
8
9
10
# 2. 准备数据库环境
create database springboot_jpadb;
grant all privileges on fpsdb.* to 'admin'@'%';
flush privileges;
2
3
4
5
6
# 3. 配置数据源
#通用数据源配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/fpsdb?charset=utf8mb4&useSSL=false&serverTimezone=UTC
spring.datasource.username=springboot
spring.datasource.password=springboot
# Hikari 数据源专用配置
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
# JPA 相关配置
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
2
3
4
5
6
7
8
9
10
11
12
13
- spring.jpa.show-sql=true 配置在日志中打印出执行的 SQL 语句信息。
- spring.jpa.hibernate.ddl-auto=create 配置指明在程序启动的时候要删除并且创建实体类对应的表。这个参数很危险,因为他会把对应的表删除掉然后重建。所以千万不要在生成环境中使用。只有在测试环境中,一开始初始化数据库结构的时候才能使用一次。
- spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect 。在 SrpingBoot 2.0 版本中,Hibernate 创建数据表的时候,默认的数据库存储引擎选择的是 MyISAM (之前好像是 InnoDB,这点比较奇怪)。这个参数是在建表的时候,将默认的存储引擎切换为 InnoDB 用的。
# 4. 实体类
@Entity
@Data
@Table(name = "TB_USER")
class User {
@Id
private Integer id;
@Column(name = "name")
private String name;
@Column(name = "trueName")
private String trueName;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- @Entity 是一个必选的注解,声明这个类对应了一个数据库表。
- @Table(name = "AUTH_USER") 是一个可选的注解。声明了数据库实体对应的表信息。包括表名称、索引信息等。这里声明这个实体类对应的表名是 AUTH_USER。如果没有指定,则表名和实体的名称保持一致。
- @Id 注解声明了实体唯一标识对应的属性。
- @Column(length = 32) 用来声明实体属性的表字段的定义。默认的实体每个属性都对应了表的一个字段。字段的名称默认和属性名称保持一致(并不一定相等)。字段的类型根据实体属性类型自动推断。这里主要是声明了字符字段的长度。如果不这么声明,则系统会采用 255 作为该字段的长度。
以上配置全部正确,则这个时候运行这个项目,我们就可以看到日志中如下的内容
Hibernate: drop table if exists t_fps_message
Hibernate: create table t_fps_message (id bigint not null, end_to_end_id varchar(255), fps_identifier varchar(255), message_id varchar(255), transaction_id varchar(255), txn_amt varchar(255), txn_cur varchar(255), txn_date_time varchar(255), primary key (id)) engine=InnoDB
2
3
系统自动将数据表给我们建好了,可以在数据库中查看表及表结构
# 5. Spring Data JPA
# 1. 实现一个持久层服务
在 Spring Data JPA 的世界里,实现一个持久层的服务是一个非常简单的事情。以上面的 UserDO 实体对象为例,我们要实现一个增加、删除、修改、查询功能的持久层服务,那么我只需要声明一个接口,这个接口继承 org.springframework.data.repository.Repository<T, ID> 接口或者他的子接口就行。这里为了功能的完备,我们继承了 org.springframework.data.jpa.repository.JpaRepository<T, ID> 接口。其中 T 是数据库实体类,ID 是数据库实体类的主键。 然后再简单的在这个接口上增加一个 @Repository 注解就结束了。
package com.demo.dao;
import com.demo.domain.FpsMessage;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* FpsMessageRepository
*
* @author baijq
*/
@Repository
public interface FpsMessageRepository extends JpaRepository<FpsMessage, Long> {
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
一行代码也不用写。那么针对 UserDO 这个实体类,我们已经拥有增删改查的功能
# 十、SpringBoot整合Mybatis
# 1. 依赖导入
<!--MyBatis框架-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!--MySQL驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
# 2. 配置文件
需要补全相关目录(mappers)
server.port=8888
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/ssodb?charset=utf8mb4&useSSL=false&serverTimezone=UTC
spring.datasource.username=admin
spring.datasource.password=admin
mybatis.mapper-locations=classpath:mappers/*Mapper.xml
logging.level.com.centanet.sso.mapper=debug
2
3
4
5
6
7
8
9
10
# 3. 扫描MyBatis的Mapper接口配置
@Mapper 注解,在接口类上添加了@Mapper,在编译之后会生成相应的接口实现类。
但是需要每一个接口都加该注解,比较麻烦
@Mapper public interface UserMapper { //todo }
1
2
3
4@MapperScan
指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
位置:可以在SpringBoot启动类上加,也可以写个配置类,如下。
//配置类形式 @Configuration @MapperScan("com.centanet.sso.mapper") // {"com.centanet.sso.mapper", "others"} public class MyBatisConfig { } //启动类形式 @SpringBootApplication public class MyApplication { //todo }
1
2
3
4
5
6
7
8
9
10
11
# 4. 接口 UserMapper
public interface UserMapper {
Integer insertUser(User user);
}
2
3
# 5. 接口对应的映射文件 UserMapper.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.centanet.sso.mapper.UserMapper">
<!--主键自动生成,返回id值-->
<insert id="insertUser" parameterType="com.centanet.sso.entity.User"
useGeneratedKeys="true" keyProperty="id">
insert into tb_user(username, password, name)
values(#{username}, #{password}, #{name})
</insert>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
# 十一、SpringBoot整合AOP
参考 https://www.lagou.com/lgeduarticle/17318.html (opens new window)
# 1. 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2
3
4
# 2. 配置切面类
package com.biubiu.admin.config.aop;
import com.alibaba.fastjson.JSON;
import com.biubiu.admin.entity.SysLog;
import com.biubiu.admin.service.SysLogService;
import com.biubiu.admin.util.ToolUtil;
import com.google.common.collect.Maps;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.Map;
/**
* AopAspect 切面类
*
* @author biubiu
*/
@Aspect
@Component
public class WebAspect {
@Resource
private SysLogService sysLogService;
private ThreadLocal<Long> startTime = new ThreadLocal<>();
/**
* 定义切入点,切入点为com.biubiu.admin.controller切下的所有函数
*/
@Pointcut("execution(public * com.biubiu.admin.controller..*.*(..))")
public void webLog() {
}
/**
* 前置通知:在连接点之前执行的通知
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) {
startTime.set(System.currentTimeMillis());
//目标方法参数信息
Object[] obj = joinPoint.getArgs();
HttpServletRequest request = ToolUtil.getCurrentRequest();
//获取请求参数
Enumeration<String> enumeration = request.getParameterNames();
Map<String, String> parameterMap = Maps.newHashMap();
while (enumeration.hasMoreElements()) {
String parameter = enumeration.nextElement();
parameterMap.put(parameter, request.getParameter(parameter));
}
String str = JSON.toJSONString(parameterMap);
if (obj.length > 0) {
System.out.println("请求参数信息");
}
//请求参数
SysLog log = SysLog.SysLogUtil.initSysLog(request);
log.setParams(str);
}
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfter(Object ret) {
SysLog log = SysLog.SysLogUtil.getSysLogInstant();
log.setUseTime(System.currentTimeMillis() - startTime.get());
sysLogService.saveSysLog(log);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# 3. 总结
@Pointcut("execution(public * com.biubiu.user.controller.*.*(..))")
public void webLog() {
}
@Before("webLog()")
public void before() {
log.error("已经记录下操作日志@Before 方法执行前");
}
@After("webLog()")
public void after() {
log.error("已经记录下操作日志@After 方法执行后");
}
@AfterReturning(pointcut = "webLog()", returning = "result")
public void doAfterReturning(Object result) {
log.error("已经记录下操作日志@AfterReturning 方法执行后");
}
@AfterThrowing(pointcut = "webLog()", throwing = "e")
public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
log.error("已经记录下操作日志@AfterThrowing 方法执行后");
}
@Around("webLog()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
log.error("已经记录下操作日志@Around 1 方法执行后");
Object result = proceedingJoinPoint.proceed();//执行切面方法,必须加
log.error("已经记录下操作日志@Around 2 方法执行后");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
日志结果,顺序排列
2020-10-09 10:33:02.009 ERROR 5992 --- [nio-8080-exec-1] com.biubiu.user.config.WebLogAspect : 已经记录下操作日志@Around 1 方法执行后
2020-10-09 10:33:02.010 ERROR 5992 --- [nio-8080-exec-1] com.biubiu.user.config.WebLogAspect : 已经记录下操作日志@Before 方法执行前
2020-10-09 10:33:02.596 ERROR 5992 --- [nio-8080-exec-1] com.biubiu.user.config.WebLogAspect : 已经记录下操作日志@AfterReturning 方法执行后
2020-10-09 10:33:02.596 ERROR 5992 --- [nio-8080-exec-1] com.biubiu.user.config.WebLogAspect : 已经记录下操作日志@After 方法执行后
2020-10-09 10:33:02.596 ERROR 5992 --- [nio-8080-exec-1] com.biubiu.user.config.WebLogAspect : 已经记录下操作日志@Around 2 方法执行后
2
3
4
5
# 日志记录
package com.biubiu.user.config;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.biubiu.user.domain.dto.RequestErrorInfo;
import com.biubiu.user.domain.dto.RequestInfo;
import com.biubiu.user.domain.entity.SysLog;
import com.biubiu.user.service.SysLogService;
import com.biubiu.util.core.HttpServletUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
/**
* 切面处理类,操作日志异常日志记录处理
*
* @author biubiu
*/
@Aspect
@Component
public class WebLogAspect {
@Value("${enableWriteLogToDb}")
private Boolean enableLog;
@Resource
private SysLogService sysLogService;
private static final Logger log = LoggerFactory.getLogger(WebLogAspect.class);
@Pointcut("execution(public * com.biubiu.user.controller.*.*(..))")
public void webLog() {
}
/**
* 打印进入控制层的入参
*
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("webLog()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long start = System.currentTimeMillis();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Object result = proceedingJoinPoint.proceed();
RequestInfo requestInfo = new RequestInfo();
requestInfo.setIp(request.getRemoteAddr());
requestInfo.setUrl(request.getRequestURL().toString());
requestInfo.setHttpMethod(request.getMethod());
requestInfo.setClassMethod(String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(),
proceedingJoinPoint.getSignature().getName()));
requestInfo.setRequestParams(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint));
requestInfo.setResult(result);
requestInfo.setTimeCost(System.currentTimeMillis() - start);
log.info("Request Info : {}", JSON.toJSONString(requestInfo));
//进去数据库
if (enableLog) {
sysLogService.saveSysLog(SysLog.builder()
.remoteAddr(requestInfo.getIp())
.method(MessageFormat.format("{0}.{1}", requestInfo.getClassMethod(), requestInfo.getHttpMethod()))
.params(requestInfo.getRequestParams().toString())
.requestUrl(requestInfo.getUrl())
.message(JSON.toJSONString(requestInfo))
.build());
}
return result;
}
@AfterThrowing(pointcut = "webLog()", throwing = "e")
public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
RequestErrorInfo requestErrorInfo = new RequestErrorInfo();
requestErrorInfo.setIp(request.getRemoteAddr());
requestErrorInfo.setUrl(request.getRequestURL().toString());
requestErrorInfo.setHttpMethod(request.getMethod());
requestErrorInfo.setClassMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName()));
requestErrorInfo.setRequestParams(getRequestParamsByJoinPoint((ProceedingJoinPoint) joinPoint));
requestErrorInfo.setException(e);
log.info("Error Request Info : {}", JSON.toJSONString(requestErrorInfo));
}
/**
* 获取入参
*
* @param proceedingJoinPoint
* @return
*/
private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
//参数名
String[] paramNames = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
//参数值
Object[] paramValues = proceedingJoinPoint.getArgs();
return buildRequestParam(paramNames, paramValues);
}
private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
//参数名
String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
//参数值
Object[] paramValues = joinPoint.getArgs();
return buildRequestParam(paramNames, paramValues);
}
private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
Map<String, Object> requestParams = new HashMap<>();
for (int i = 0; i < paramNames.length; i++) {
Object value = paramValues[i];
//如果是文件对象
if (value instanceof MultipartFile) {
MultipartFile file = (MultipartFile) value;
//获取文件名
value = file.getOriginalFilename();
}
requestParams.put(paramNames[i], value);
}
return requestParams;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# 十二、SpringBoot 整合Apollo配置中心
# 1. 导入jar包:
<!--apollo-->
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.1.0</version>
</dependency>
2
3
4
5
6
# 2. 配置apollo必要参数
SpringBoot的话最简单的一种是直接在application.properties里配置
必要参数
- appId
app.id=YOUR_APP_ID
1- meta Server
apollo.meta=http://config-server-url/
1其他参数(可选)
- 缓存地址(默认在c://opt/data//...)
- 环境 (DEV/PRO/UAT/TEST/LOCAL)
- 其他
也可以配置在启动参数里:
-Dapollo.meta=http://10.29.204.82:8080 -Denv=Local -Dspring.profile.active=dev -Dlogging.config=classpath:logback-spring-dev.xml
1
2
3
4
# 3. 获取配置
调用API方式,最简单,最灵活
Config config = ConfigService.getConfig("AppVersion"); String someKey = "AndroidMessage"; String someDefaultValue = "none"; String value = config.getProperty(someKey, someDefaultValue);
1
2
3
4Java配置方式
@EnableApolloConfig(value = "AppVersion") //AppVersion指定了namespace @Configuration public class ApolloConfig { @Value("${IOSVerCode}") public String iosVerCode; }
1
2
3
4
5
6
7
8
# 十三、SpringBoot整合日志log4j2
一个项目框架中日志都是必不可少的,随着技术的更新迭代,SpringBoot被越来越多的企业所采用。这里简单说说SpringBoot整合log2j2日志。
# 一、说明:
Log4J(Apache的一个开源项目,可以控制日志信息输送的目的地是控制台、文件、GUI组件等,可以控制每一条日志的输出格式,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码,虽然已经停止维护了,但目前绝大部分企业都是用的log4j)/LogBack(是Log4j的一个改良版本)/Log4J2(Log4j2已经不仅仅是Log4j的一个升级版本了,它从头到尾都被重写了)
日志门面slf4j:SLF4J,即简单日志门面(Simple Logging Facade for Java),它不是一个真正的日志实现,而是一个抽象层( abstraction layer),它允许你在后台使用任意一个日志实现。
Log4j2:相比与其他的日志系统,log4j2丢数据这种情况少;disruptor技术,在多线程环境下,性能高于logback等10倍以上;利用jdk1.5并发的特性,减少了死锁的发生;log4j2优越的性能其原因在于log4j2使用了LMAX,一个无锁的线程间通信库代替了,logback和log4j之前的队列. 并发性能大大提升。
# 二、整合步骤
# 1. 导入依赖包
SpringBoot默认使用logback的日志框架,所以排除logback,不然会出现jar依赖冲突的报错。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions><!--去掉springboot默认配置-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <!-- 引入log4j2依赖 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
13
14
# 2. 指定配置文件
默认名log4j2-spring.xml,就可以不用指定
logging:
config: classpath:log/log4j2-file-dev.xml
2
# 3. 详细配置说明
<?xml version="1.0" encoding="UTF-8"?>
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="WARN" monitorInterval="5">
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--变量配置-->
<Properties>
<!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
<!-- %logger{36} 表示 Logger 名字最长36个字符 -->
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 定义日志存储的路径 -->
<property name="FILE_PATH" value="更换为你的日志路径"/>
<property name="FILE_NAME" value="更换为你的项目名"/>
<Property name="PID">????</Property>
<Property name="LOG_EXCEPTION_CONVERSION_WORD">%xwEx</Property>
<Property name="LOG_LEVEL_PATTERN">%5p</Property>
<Property name="LOG_DATEFORMAT_PATTERN">yyyy-MM-dd HH:mm:ss.SSS</Property>
<Property name="CONSOLE_LOG_PATTERN">%clr{%d{${LOG_DATEFORMAT_PATTERN}}}{faint} %clr{${LOG_LEVEL_PATTERN}} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}
</Property>
<Property name="FILE_LOG_PATTERN">%d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t]
%-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}
</Property>
</Properties>
<appenders>
<console name="Console" target="SYSTEM_OUT">
<!--输出日志的格式-->
<PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}"/>
<!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
</console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
<File name="Filelog" fileName="${FILE_PATH}/test.log" append="false">
<PatternLayout pattern="${LOG_PATTERN}"/>
</File>
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log"
filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${sys:FILE_LOG_PATTERN}"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
<!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/warn.log"
filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
<!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log"
filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
<!-- <RabbitMQ name="rabbitmq" addresses="ip:port"-->
<!-- user="username" password="password" applicationId="applicationName" charset="UTF-8"-->
<!-- routingKeyPattern="applicationName"-->
<!-- exchange="Application.Log" deliveryMode="NON_PERSISTENT">-->
<!-- <JsonLayout>-->
<!-- <KeyValuePair key="appName" value="applicationName"/>-->
<!-- <KeyValuePair key="sysName" value="applicationName"/>-->
<!-- <KeyValuePair key="machine" value="$${ctx:machine}"/>-->
<!-- <KeyValuePair key="clientIp" value="$${ctx:clientIp}"/>-->
<!-- <KeyValuePair key="city" value="$${ctx:city}"/>-->
<!-- </JsonLayout>-->
<!-- </RabbitMQ>-->
</appenders>
<!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
<!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.mybatis" level="info" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<!--监控系统信息-->
<!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
<Logger name="org.springframework" level="info" additivity="false">
<AppenderRef ref="Console"/>
</Logger>
<root level="info">
<appender-ref ref="Console"/>
<appender-ref ref="Filelog"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
</loggers>
</configuration>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# 配置简介:
日志级别
- trace:追踪,就是程序推进一下,可以写个trace输出
- debug:调试,一般作为最低级别,trace基本不用
- info:输出重要的信息,使用较多
- warn:警告,有些信息不是错误信息,但也要给程序员一些提示
- error:错误信息。用的也很多
- fatal:致命错误
输出源
- CONSOLE(输出到控制台)
- FILE(输出到文件)
- RabbitMQ/Kafka/ES/MongoDB等等
格式
SimpleLayout:以简单的形式显示
HTMLLayout:以HTML表格显示
PatternLayout:自定义形式显示
%d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间,输出到毫秒的时间 %-5level : 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0 %c : logger的名称(%logger) %t : 输出当前线程名称 %p : 日志输出格式 %m : 日志内容,即 logger.info("message") %n : 换行符 %C : Java类名(%F) %L : 行号 %M : 方法名 %l : 输出语句所在的行数, 包括类名、方法名、文件名、行数 hostName : 本地机器名 hostAddress : 本地ip地址
1
2
3
4
5
6
7
8
9
10
11
12
13
使用
- private static final Logger log = LoggerFactory.getLogger(LogExampleOther.class);
- lombok得注解 @Slf4j
# 三、参考这个博客
https://www.cnblogs.com/keeya/p/10101547.html
# 十四、集成Mybatis和通用mapper
# 1. 导入相关依赖包
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<!--通用mapper启动器-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 2. 配置
server.port=8888
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db_web?useSSL=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=Asia/Shanghai
spring.datasource.username=xxxx
spring.datasource.password=xxxx
mybatis.mapper-locations=classpath:mapper/*.xml
logging.level.com.biubiu.web.mapper=debug
2
3
4
5
6
7
8
9
# 3. 启动类加上包扫描注解
@MapperScan 是在 tk.mybatis.spring.annotation.MapperScan
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@MapperScan("com.biubiu.web.mapper")
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 4. 实体类映射表
@Data
@Table(name = "tb_users")
public class User implements Serializable {
private static final long serialVersionUID = 3167212910028834735L;
@Id
private Integer id;
private String name;
private Integer age;
private Integer sex;
private String tel;
private String address;
private Date createTime;
@Transient//这个注解表示,查询的时候不查询phone这个字段
private Date updateTime;
private Integer valid;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 5. Mapper类的实现
import com.biubiu.web.entity.User;
import tk.mybatis.mapper.common.Mapper;
/**
* UserMapper
*
* @author biubiu
*/
public interface UserMapper extends Mapper<User> {
}
2
3
4
5
6
7
8
9
10
11
# 6. 测试就已经ok了
# 7. 拓展,现有方法不满足现有逻辑,自定义sql语句
a. userMapper新加方法
public interface UserMapper extends Mapper<User> { List<User> getUserByTel(String tel); void setUserValid(Integer[] valids); }
1
2
3
4
5
6b. xml文档编辑sql
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.biubiu.web.mapper.UserMapper"> <select id="getUserByTel" resultType="com.biubiu.web.entity.User" parameterType="java.lang.String"> select * from tb_users t where t.tel = #{tel} </select> <update id="deleteByIds" parameterType="integer"> update tb_users t <set> t.valid = 0 </set> where t.id in <foreach collection="array" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </update> </mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23测试成功
# 十五、MyBatis分页插件
# 1. 引入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>
2
3
4
5
# 2. 配置
pagehelper.helper-dialect=mysql
pagehelper.params=count=countSql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
2
3
4
# 3. 测试类
@Controller
public class PageController {
@Resource
private UserService userService;
@GetMapping("/index")
public String test(Model model,
@RequestParam(defaultValue = "1", value = "pageNum") Integer pageNum) {
PageHelper.startPage(pageNum, 2);
List<User> users = userService.queryAllUser();
PageInfo<User> userPageInfo = new PageInfo<>(users);
model.addAttribute("pageInfo", userPageInfo);
return "index";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 4. 测试页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div align="center">
<table border="1">
<tr>
<th>id</th>
<th>name</th>
<th>sex</th>
<th>age</th>
</tr>
<tr th:each="item:${pageInfo.list}">
<td th:text="${item.id}"></td>
<td th:text="${item.name}"></td>
<td th:text="${item.sex}"></td>
<td th:text="${item.age}"></td>
</tr>
</table>
<p>当前 <span th:text="${pageInfo.pageNum}"></span> 页,总 <span th:text="${pageInfo.pages}"></span> 页,共 <span th:text="${pageInfo.total}"></span> 条记录</p>
<a th:href="@{/index}">首页</a>
<a th:href="@{/index(pageNum=${pageInfo.hasPreviousPage}?${pageInfo.prePage}:1)}">上一页</a>
<a th:href="@{/index(pageNum=${pageInfo.hasNextPage}?${pageInfo.nextPage}:${pageInfo.pages})}">下一页</a>
<a th:href="@{/index(pageNum=${pageInfo.pages})}">尾页</a>
</div>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30