Spring6+SpringBoot+Mybatis详情配置

Spring6

Spring 是轻量级的开源的JavaEE框架,解决企业应用开发的复杂性。它有两个核心模块:IOC和AOP

官网:https://spring.io/

Spring6特点:

要求JDK17、Maven3.6、Idea工具2022

IOC

控制反转,把创建对象过程交给Spring进行管理,作用:降低耦合

底层:XML解析、工厂模式、反射

提供IOC容器实现两种方式

  1. BeanFactory 是Spring内部的使用的接口,加载配置文件不会创建对象
  2. ApplicationContext BeanFactory 接口的子qq接口,一般开发人员使用,加载配置文件会把配置文件对象进行创建

IOC操作Bean二种方式

  1. 基于XML方式

    // XML文件配置 (set注入) 
    //   autowire=“byName” 是根据属性名称自动装配 , 可放在class=“”后面
    <bean id="id属性" class=“包的全路径” >
    	<property name="属性名" value="属性值"></property>
    </bean>
    
    // 创建对象
    ApplicationContext ac = new ClassPathXmlApplicationContext("XML文件.xml");
    IAccountService as = (IAccountService) ac.getBean("id属性");
    
  2. 基于注解方式

    // 创建Bean实例
    @Component
    @Service
    @Controller
    @Repository
    // 属性注入
    @AutoWired  根据属性类型自动装配
    @Qualifier  根据属性名称进行注入
    @Resource   根据类型注入,可以根据名称注入
    @Value      注入普通类型
    // 创建配置类,替换XML文件
    @Configuration
    @ComponentScan(basePackages={"com.包名"}) // 扫描包
    public class{
        ...
    }
    // 创建对象
    ApplicationContext ac = new AnnotationConfigApplicationContext(.class);
    IAccountService as = (IAccountService) ac.getBean("id属性");
    

AOP

面向切面,不修改原代码进行增强操作

底层:动态代理

动态代理两种方式

  1. JDK动态代理 有接口情况,使用Proxy类实现
  2. CGLIB动态代理 没有接口情况

常用术语

  1. 连接点:类里的哪些方法可以被加强
  2. 切入点:实际被真正增强的方法
  3. 通知:实际增强的逻辑就是通知,分为:前置通知、后置通知、环绕通知、异常通知、最终通知
  4. 切面:把通知应用到切入点的过程

切入点表达式

语法结构:execution([权限修饰符][返回类型][类的全路径][方法名称]([参数列表]))
例子:对com.abc.dao包下里所有类的全部方法进行增强
execution(* com.abc.dao.*.*(..)) 
例子:对com.abc.dao.A类的add方法进行增强
execution(* com.abc.dao.A.add(..)) 
例子:对com.abc.dao.A,所有的方法进行增强
execution(* com.abc.dao.A.* (..)) 

案例:

# 1.引包
 <!--aop依赖-->
 <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>6.0.2</version>
 </dependency>
 <!--aspects依赖-->
 <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.0.2</version>
 </dependency>

# 2.AspectJ注解
@Aspect // 这个注解就是生成代理对象
@Component
@Order(1) // 设置多个类同一个方法增强,设置优先级,值越少越先执行    
public class LogAdvice {

    // 切入点注解 (相同的切入点 - 提取)
    @Pointcut("execution(* com.abc.dao.A.add(..)")
    private void check() {
    }
    
    // 前置通知  [check] 就是使用相同的切入点方法名
    @Before("check()")
    public void before(JoinPoint joinPoint){
        System.out.println("before,代理逻辑前执行");
        String name = joinPoint.getSignature().getName();
        System.out.println(" 该方法名称: "+name);
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            System.out.println(" 参数: "+arg);
        }
    }
    // 后置通知 - 也可以是最终通知,遇到异常也会执行
    @After("execution(* com.abc.dao.A.add(..)")
    public void after(){
        System.out.println("after 代理逻辑后执行");
    }
    // 前置通知 - 返回通知,有异常就不执行, res 返回值
    @AfterReturning(value = "check()", returning = "res")
    public void afterReturning(JoinPoint joinPoint, Object res){
         System.out.println("after 代理逻辑后执行,有异常就不执行,比有@After先执行");
    }
    // 异常通知
    @AfterThrowing(value = "check()",throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Throwable ex){
       System.out.println("after 代理逻辑后执行,有异常才执行,会执行这里" + ex.toString());
    }
    // 环绕通知
    @Around("execution(* com.abc.dao.A.add(..)")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知之 前执行");
        joinPoint.proceed(); // 执行增强逻辑
        System.out.println("环绕通知之 后执行");
    }
}   

Log4j2

是一个开源的日志记录组件

<!--log4j2的依赖-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.19.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j2-impl</artifactId>
    <version>2.19.0</version>
</dependency>

配置log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <loggers>
        <!-- level指定日志级别,从低到高的优先级:-->
        <root level="DEBUG">
            <appender-ref ref="spring6log"/>
            <appender-ref ref="RollingFile"/>
            <appender-ref ref="log"/>
        </root>
    </loggers>

    <appenders>
        <!--输出日志信息到控制台-->
        <console name="spring6log" target="SYSTEM_OUT">
            <!--控制日志输出的格式-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
        </console>

        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
        <File name="log" fileName="F:/log/xxx/test.log" append="false">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>
    </appenders>
</configuration>

使用:

private Logger logger = LoggerFactory.getLogger(TestUser.class);
logger.info("ok");

事务

事务是数据库操作最基本的单元。

特性:(ACID)

  1. 原子性:要么都成功,要么都失败
  2. 一致性:操作前和操作后,总量是不变的
  3. 隔离性:每个事务是互相隔离的
  4. 持久性:当提交后,数据就会发生变化

事务操作:

注解:@Transactional

传播行为:(有7个,常用)

  • Propagation.REQUIRED 如果有事务运行,在当前方法事务内运行,否则就创建新的事务;
  • Propagation.REQUIRES_NEW 当前方法在自己的事务内运行
  • Propagation.SUPPORTS 如果有事务运行,当前方法在这个事务内运行,否则可以不运行事务中

隔离级别:

产生的问题:

  • 脏读 :一个未提交的事务读到了另一个事务读到
  • 不可重复读:一个未提交事务读到了另一个事务修改的数据
  • 幻读:一个未提交事务读到了另一个事务添加的数据

解决问题:

  • 读未提交 Isolation.READ_UNCOMMITTED
  • 读已提交 isolation = Isolation.READ_COMMITTED
  • 可重复读 Isolation.REPEATABLE_REA
  • 串行化 Isolation.SERIALIZABLE
  • 数据库默认的 isolation = Isolation.DEFAULT

例子:

@Transactional(
    timeout = 20, // 超时时间: 默认是-1,单位:秒
    propagation = Propagation.SUPPORTS,   // 传播行为
   	isolation = Isolation.READ_COMMITTED, // 隔离级别
    readOnly = true, // 是否只读,默认 false ,true代表只能查询
    rollbackFor = Exception.class,  // 设置出现哪些异常进行回滚
    noRollbackFor = Exception.class // 设置哪些异常不进行回滚
) 

开启事务注解:@EnableTransactionManagement

数据校验

Validation

案例:

  1. 创建 效验器
@Service
@Validated
public class MyService {
    //	使用 @Valid 注解标记了 User 参数,表示对该参数进行校验、
    //	使用 @NotNull 注解标记了 User 参数,表示该参数不能为空。
    public String testParams(@NotNull @Valid User user) {
        return user.toString();
    }
}
  1. 创建实体
/**
 *  数据校验:Validation  - 基于方法演示
 *      2. 创建实体类
 *      常用注解:
 *      @NotNull:  检查字段值不为null。
 *      @NotEmpty: 检查字符串、集合或数组的值不为空。
 *      @NotBlank: 检查字符串的值不为空或不只包含空格字符。
 *      @Min(value): 检查字段值大于或等于指定的最小值。
 *      @Max(value): 检查字段值小于或等于指定的最大值。
 *      @Size(max, min): 检查字段值的大小在指定范围内。
 *      @Email: 检查字段值符合电子邮件的格式要求。
 *      @Pattern(regex): 检查字段值符合指定的正则表达式。
 */
@Data
public class User {
    @NotNull
    private String name;

    @Min(0)
    @Max(120)
    private int age;

    @Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手机号码格式错误")
    @NotBlank(message = "手机号码不能为空")
    private String phone;
}
  1. 演示
// 如果校验不通过,将抛出 ConstraintViolationException 异常、如果校验通过,执行其他业务逻辑
public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
        MyService myService = context.getBean(MyService.class);
        User user = new User();
        user.setAge(-1);
        // 验证方法 ,执行方法
        myService.testParams(user);
}

自定义效验

  1. 创建自定义校验注解
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// @Constraint 标记该注解为校验注解,并指定对应的校验器类 -- 指定规则的逻辑
@Constraint(validatedBy = {CannotBlankValidator.class})
public @interface CannotBlank {
    //默认错误消息
    String message() default "不能包含空格";
    //分组 - 可以指定校验器在哪个分组生效
    Class<?>[] groups() default {};
    //负载 - Payload 可在自定义校验器中使用
    Class<? extends Payload>[] payload() default {};
    //指定多个时使用
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        CannotBlank[] value();
    }
}
  1. 编写校验类 - 效验规则
public class CannotBlankValidator implements ConstraintValidator<CannotBlank, String> {
    // initialize() 方法用于初始化校验器
    @Override
    public void initialize(CannotBlank constraintAnnotation) {
    }
    // isValid() 方法是校验的核心逻辑,用于判断被校验的值是否符合自定义的校验规则
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        //null时不进行校验
        if (value != null && value.contains(" ")) {
            //获取默认提示信息
            String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
            System.out.println("default message :" + defaultConstraintMessageTemplate);
            //禁用默认提示信息
            context.disableDefaultConstraintViolation();
            //设置提示语
            context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
            return false;
        }
        return true;
    }
}
  1. 使用,演示与上同步

AOT

提前编译,运行时编译。可以把源码直接转换机器码,启动快,内存占用低。需要安装。

使用前需要安装Native Image插件,在这里没有详细安装步骤。

SpringBoot

介绍

简化Spring应用开发的一个框架,整个Spring技术栈的一个大整合。

官方网站:https://spring.io/projects

优点:

  • 快速创建Spring项目,使用嵌入式的服务,内置了Tomcat
  • starters(启动器)自动依赖与版本控制
  • 无需配置XML,开箱即用

系统要求:

Java 8、Maven 3.3+

maven配置:

给maven 的settings.xml配置文件的profiles标签添加

<mirrors>
	<mirror>
		<id>nexus-aliyun</id>
		<mirrorOf>central</mirrorOf>
		<name>Nexus aliyun</name>
		<url>http://maven.aliyun.com/nexus/content/groups/public</url>
	</mirror>
</mirrors>
 
<profiles>
    <profile>
        <id>jdk-1.8</id>
        <activation>
        	<activeByDefault>true</activeByDefault>
      	 	<jdk>1.8</jdk>
        </activation>
        <properties>
        	<maven.compiler.source>1.8</maven.compiler.source>
        	<maven.compiler.target>1.8</maven.compiler.target>
        	<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
        </properties>
    </profile>
</profiles>

特点:

  • 依赖管理

    父项目做依赖管理、开发导入starter场景启动类、无需关注版本号

  • 自动配置

    自动配置了tomcat、配置SpringMVC、Web常见功能

    常用注解:

    @Configuration  //告诉SpringBoot这是一个配置类 == 配置文件
    @Bean           //给容器中添加组件。
    @ComponentScan  //包扫描
    @Import({User.class, User2.class})  // 给容器中自动创建出这两个类型的组件
    @Conditional    //条件装配 ,注入
    @ImportResource //原生配置文件引入
    @Component      //实现Bean的注入
    @ConfigurationProperties //配置绑定  取到properties文件中的内容,封装到Bean中 配合@Component注解
    @EnableConfigurationProperties  同上,就不需要加 @Component
    

Spring自动配置:

启动类
@SpringBootApplication (引导类)
     @SpringBootConfiguration
        @Configuration 定义为配置类 - 等价于配置文件
          @Component   添加到Spring容器中,是个组件
     @EnableAutoConfiguration
        @AutoConfigurationPackage 将引导包所在子包下面的组件,添加Spring容器中
        @Import
        会给容器导入非常多的自动 配置类,并配置好。(这样省去手写配置注入Spring容器)
     @ComponentScan 这个注解被标识,会被Spring容器进行管理
总结:
SpringBoot先加载所有的自动配置类,每个自动动,按配置类条件去生效,生效后就给容器配置很多的组件,容器有这些组件,就有了这些功能。
如果我们定制配置,可以直接@Bean替换底层默认的组件。

核心功能

yaml配置文件

如果出现乱码,可在IDEA工具搜索-( File Encodings) 配置utf-8格式

# yaml表示以上对象
person:
  userName: zhangsan
  birth: 2019/12/12 20:12:33
  # 数组 List<String> arr (代表arr里有2个元素)
  arr: 
    - jpg
    - png
  # map Map<String, List<Pet>> allPets; 
  allPets:
    sick:
      - {name: tom}
      - {name: jerry,weight: 47}
    health: [{name: mario,weight: 47}]

@ConfigurationProperties(prefix = "person")
@Component
public class Person {
	private String userName;
	...
}

日志配置

SpringBoot默认采用SLF4j +logback

打印日志:

// 测试 - 日志 (级别顺序)
Logger log = LoggerFactory.getLogger(getClass());
log.trace("这是 跟踪运行信息");
log.debug("这是 跟调试信息");
log.info("这是 跟info自定义信息");
log.warn("这是 跟警告信息");
log.error("这是 记录错误信息");
// 输出日志 【时间 日志级别 进程 [线程] 完整包名 信息】

基本日志配置:

# 日志配置
logging:
  level:
    # 指定指定包的日志级别 com.xx.boot包
    com:
      xx:
        boot: debug
    # 修改springboot的root 默认级别
    root: info
  file:
    # 日志输出位置
    name: G:/log/abd.log
  pattern:
    # 控制台输出格式:'时间 日志级别 [线程] 日志前缀包名50限时长度是50  日志信息 %n是换行'
    console: '%d{yyyy-mm-dd} %-5level [%thread] %logger{50} - %msg%n'
    # 日志文件输出格式
    file: '%d{yyyy-MMM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{15} - %msg%n'

使用自定义日志:Log4j2

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <!-- 除去默认的日志包 -->
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- 引入新的日志包 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>  

简化开发

  • lombok

     <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <version>1.18.10</version>
     </dependency>
    
  • dev-tools

     # 快捷键 Ctrl + F9 ,自动加载编译
     <dependency>
     	 <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-devtools</artifactId>
         <optional>true</optional>
     </dependency>
    

静态资源访问

web相关jar包:https://www.webjars.org/

静态目录:/static /public /resources /META-INF/resources

改变默认的静态资源路径

spring:
  # 设置静态资源访问前缀,默认无前缀,访问:localhost:8080/res/xxx.css
  mvc:
    static-path-pattern: /res/**
  # 指定静态资源的文件目录
  resources:
    static-locations: [classpath:/st1/]
    # static-locations: [classpath:/st1/,/st2/,/st3/]
    # 禁用所有静态资源规则
    add-mappings: false

RESTful

@RequestMapping(value = "/user",method = RequestMethod.GET)    ----  @GetMapping()
@RequestMapping(value = "/user",method = RequestMethod.POST)   ----  @POSTMapping()
@RequestMapping(value = "/user",method = RequestMethod.PUT)    ----  @PUTMapping()
@RequestMapping(value = "/user",method = RequestMethod.DELETE) ----  @DELETEMapping()

# yml文件开启页面表单的Rest功能
spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true

常用注解

@PathVariable 获取路径变量

@RequestHeader 获取消息头信息

@RequestParam 请求消息参数

@CookieValue 获取Cookie值

@RequestBody 请求体信息 - 只有post请求需要

@RequestAttrbute 获取请求域中的值

@ModelAttribute 矩阵变量,默认禁用

例子:

@RestController
public class TestController {

    // car/2
    @GetMapping("/car/{id}")
    public String getCar(@PathVariable("id") Integer id,
                                     @RequestHeader("User-Agent") String userAgent,
                                     // 全部的消息头信息
                                     @RequestHeader Map<String,String> header,
                                     @RequestParam("age") Integer age,
                                     // xxx?age=8&arrs=n1&arrs=n2&arrs=n3
                                     @RequestParam("inters") List<String> arrs,
                                     @CookieValue("_ga") String _ga,
                                     // 得到Cookie对象
                                     @CookieValue("_ga") Cookie cookie){
        return null;
		// return "forward:/pageA";  // 转到到pageA页
    }

    // @RequestBody 请求体信息
    @PostMapping("/save")
    public Map postMethod(@RequestBody String content){
        Map<String,Object> map = new HashMap<>();
        map.put("content",content);
        return map;
      
    }
}

复杂参数:

Map、Model(放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder

内容协商:

根据客户端接收能力不同,返回不同媒体类型的数据。

<dependency>
     <groupId>com.fasterxml.jackson.dataformat</groupId>
     <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

# yml文件开启请求参数内容协商模式
spring:
    contentnegotiation:
      favor-parameter: true  #开启请求参数内容协商模式

只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。

模板引擎

Thymeleaf使用:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

基本语法:

表达式名字语法用途
变量取值${…}获取请求域、session域、对象等值
选择变量*{…}获取上下文对象值
消息#{…}获取国际化等值
链接@{…}生成链接
片段表达式~{…}jsp:include 作用,引入公共页面片段

静态页面全部放在resources/templates目录下

拦截器

1.继承HandlerInterceptor 接口

package com.good.yan.config.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * 拦截器
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    /**
     * 目标方法执行之前
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String requestURI = request.getRequestURI();
        log.info("preHandle拦截的请求路径是{}",requestURI);
        //登录检查逻辑
        HttpSession session = request.getSession();
        Object loginUser = session.getAttribute("LoginState");
        if(loginUser != null){
            //放行
            return true;
        }
        //拦截住。未登录。跳转到登录页
        request.setAttribute("msg","请先登录");
        request.getRequestDispatcher("/login").forward(request,response);
        return false;
    }


    /**
     * 目标方法执行完成以后
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle执行{}",modelAndView);
    }

    /**
     * 页面渲染以后
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion执行异常{}",ex);
    }

}

2.配置拦截器

package com.good.yan.config.interceptor;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 1、编写一个拦截器实现HandlerInterceptor接口
 * 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
 * 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
 */
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

    /** 默认显示首页 */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home/list");
        registry.addViewController("/index.html").setViewName("home/list");
        registry.addViewController("/index").setViewName("home/list");
    }

    /** 拦截器   */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")  //所有请求都被拦截包括静态资源
                .excludePathPatterns("/login","/doLogin","/asserts/**"); //放行的请求
    }

}

文件上传

/**
  * MultipartFile 自动封装上传过来的文件
  * @param email
  * @param username
  * @param headerImg
  * @param photos
  * @return
 */
 @PostMapping("/upload")
 public String upload(@RequestPart("headerImg") MultipartFile headerImg,
                      @RequestPart("photos") MultipartFile[] photos) throws IOException {
     
     if(!headerImg.isEmpty()){
         //保存到文件服务器,OSS服务器
         headerImg.transferTo(new File("H:\\cache\\"+headerImg.getOriginalFilename()));
     }

     if(photos.length > 0){
         for (MultipartFile photo : photos) {
             if(!photo.isEmpty()){
                 photo.transferTo(new File("H:\\cache\\"+photo.getOriginalFilename()));
             }
         }
     }
     return "上传ok";
}

异常处理

自定义错误页:在templates目录下寻找error目录下的404.html 、5xx.html的页面。

定义全局异常:

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 获取其它异常。包括500
     */
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
//    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR,reason = "用户访问太多")
    @ExceptionHandler(value = Exception.class)
    public Map<String, Object> defaultErrorHandler(Exception e) {
        log.error("Exception", e);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("code", FIVE);
        map.put("message", e.getMessage());
        return map;
    }

}    

数据访问

SpringBoot连接Mysql

# 1.引入包
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
# 2.修改yml配置文件
spring:
  datasource:
    username: 用户名
    password: 密码
    # mysql8版本以上的驱动包,需要指定以下时区
    url: jdbc:mysql://127.0.0.1:3306/数据库?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    driver-class-name: com.mysql.cj.jdbc.Driver

使用Druid数据源

官网地址:https://github.com/alibaba/druid

# 1.引入包
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.17</version>
</dependency>
# 2.修改yml配置文件   
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    druid:
      initial-size: 5
      min-idle: 5
      max-active: 20
      max-wait: 60000
      #...
      # 监控哪些包
      aop-patterns: com.good.yan.modules.*
      # 底层开启功能,stat(sql监控),wall(防火墙)    
      filters: stat,wall    

      stat-view-servlet:   # 配置监控页功能
        enabled: true      # 开启维护功能
        login-username: admin  # 访问的用户名和密码
        login-password: admin
        resetEnable: false # 是否重置按钮启用

      web-stat-filter:  # 监控web
        enabled: true
        urlPattern: /*
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'

      filter:
        stat:    # 对上面filters里面的stat的详细配置
          slow-sql-millis: 1000
          logSlowSql: true
          enabled: true
        wall:
          enabled: true
          config:
            drop-table-allow: false

详情配置地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter

整合MyBatis操作

官网地址:https://github.com/mybatis

# 1.引入包
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
</dependency>
# 2.修改yml配置文件
mybatis:
  # 映射配置文件路径
  mapper-locations: classpath:mybatis/mapper/*.xml
  # 核心配置文件路径
  # config-location: classpath:mybatis/mybatis-config.xml
  # mybatis的全局配置
  configuration:
    map-underscore-to-camel-case: true

配置文件配置:https://mybatis.org/mybatis-3/zh/configuration.html

单元测试

JUnit5常用注解

// 测试方法的名称
@DisplayName("A类的功能测试")
@SpringBootTest
public class test4 {

    @DisplayName("测试方法名称1")
    // 测试类 - 也可以加事务注解,(测试完自动回滚)
    @Transactional(isolation = Isolation.DEFAULT)
    @Test
    void test1() {
        System.out.println("test1测试");
    }

    // 重复测试 5 次
    @RepeatedTest(5)
    // 超时时间 (如果超时,会自动报出超时异常)
    @Timeout(value = 500, unit = TimeUnit.MILLISECONDS)
    // 表示这个注解不执行
    @Disabled
    @Test
    void test2() {
        System.out.println("test2测试");
    }

    // 每个测试方法 - 都需要执行
    @BeforeEach()
    void befor() {
        System.out.println("测试方法之前执行。。");
    }

    @AfterEach()
    void after() {
        System.out.println("测试方法之后执行。。");
    }

    // 只会调用一次,所以一般加 static 修饰
    @BeforeAll()
    static void beforAll() {
        System.out.println("所有的测试方法之前执行。。只执行1一次");
    }

    @AfterAll()
    static void afterAll() {
        System.out.println("所有的测试方法之后执行。。只执行1一次");
    }
}
断言:

来检查业务逻辑返回的数据是否合理,所有运行的结果,会有测试报告;

// 需要导入这个包
import static org.junit.jupiter.api.Assertions.*;

// 断言:测试方法需要满足的条件进行验证,检查方法是否合理
@DisplayName("测试断言1 - 普通断言")
@Test
void testdy1() {
    // assertEquals 是否相等  参数1 期望值 ,参数2 实际值
    assertEquals(1, 1, "测试断言1?");
    // 例子: assertEquals(期望值, 实际值, "测试断言1?");

    // assertSame 是否同同一个对象
    // assertSame(new User(), new User(), "测试断言2?");

    // assertArrayEquals 是否同数组相同
    assertArrayEquals(new int[]{1, 2}, new int[]{1, 2}, "测试断言3?");

    // assertThrows 异常断言,如果出异常代表正常
    assertThrows(ArithmeticException.class, () -> {
        System.out.println("执行业务代码逻辑。。。");
        int i = 10 / 0; // 会出现 ArithmeticException 异常
    }, "测试断言4?");

    // assertTimeout 超时测试,如果超过设置的时间(参数1),就会出现异常
    // assertTimeout(Duration.ofMillis(1000), () -> {
    //        System.out.println("执行业务代码逻辑--超时。。。");
    //        Thread.sleep(1200);
    // }, "测试断言5?");

    // fail 快速失败 (满足条件就触发)
    if (1 == 1) {
        fail("测试断言6?");
    }
}
前置条件
// 前置条件:测试方法需要满足的条件进行验证,前置条件使测试方法的执行终止
@DisplayName("测试前置条件1 - 假设")
@Test
void testjs1() {
    Assumptions.assumeTrue(false,"结果不是true,才打印这个");
    System.out.println("jjj"); // 如果不满足条件,后面的逻辑不会执行,jjj也不会打印
}
参数化测试

不同的参数,多次运行

// 参数化测试:可设置不同的参数多次运行某个测试方法
@DisplayName("参数化测试1 - 参数化")
@ParameterizedTest
// 这个注解就是多个参数值
@ValueSource(ints = {1,3,4,5})
void testcs1(int i) {
    System.out.println(i);
    // 该方法 执行 4次,参数是:1,3,4,5
}

@DisplayName("参数化测试2 - 参数化")
@ParameterizedTest
// 参数化 指定具体方法名,自定义参数对象
@MethodSource("stringStream")
void testcs2(String str) {
    // 该方法 执行 3次,参数是:"参数1","参数2","参数3"
    System.out.println(str);
}

// 方法名 stringStream ,代表有哪些参数,参数是String类型
static Stream<String> stringStream(){
    return Stream.of("参数1","参数2","参数3");
}

指标监控

每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。

常用监控:health 健康端点、metrics 运行时的指标 、loggers 日志记录

# 1.引入依赖包
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# 2.修改配置文件
management:
  endpoints:
    enabled-by-default: true  #暴露所有端点信息
    web:
      exposure:
        include: '*'  #以web方式暴露,暴露所有

# 3.访问测试
http://localhost:8080/actuator/beans
http://localhost:8080/actuator/configprops
http://localhost:8080/actuator/metrics
http://localhost:8080/actuator/metrics/jvm.gc.pause
http://localhost:8080/actuator/endpointName/detailPath

可视化

地址: https://github.com/codecentric/spring-boot-admin

详细配置:https://www.yuque.com/atguigu/springboot/sgpvgn

快速开始:https://codecentric.github.io/spring-boot-admin/2.5.1/#getting-started

# 1.服务端配置-引入依赖包
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
    <version>2.3.1</version>
</dependency>
# 2.服务端配置-启动类增加 @EnableAdminServer注解

# 3.客户端配置-引入包
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>2.3.1</version>
</dependency>
# 4.客户端配置-更改配置文件
spring:
  boot:
    admin:
      client:
        url: http://localhost:8888  # 服务端的地址
        instance:
          prefer-ip: true  # 使用ip进行注册
# 5.访问服务端,可以看到可视化界面

配置完启动 - 服务端

访问地址:http://localhost:8888/applications

在导航栏-应用墙 可以看到客户端信息;可以看到:

  • 健康信息 : 所有服务都健康 - 当前服务是健康,包含:db、redis等;

    在这里插入图片描述

  • 性能监听

在这里插入图片描述

  • 日志配置:可以设置日志级别

  • JVM:可观看线程、内存信息

邮箱配置

配置邮箱服务器:

步骤1:

在这里插入图片描述

步骤2:点了生成授权码,发送短信,得到密钥。

在这里插入图片描述

点击什么是IMAP,它又是如何设置?

在客户端设置 - 发送邮件服务器:smtp.qq.com

发送邮件相关案例:

# 引入相关包 
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
# 修改配置文件
#  配置邮箱信息
#  spring.mail.username=xxx@qq.com
#  spring.mail.password=秘密
#  发送邮件服务器
#  spring.mail.host=smtp.qq.com

相关代码

 @Autowired(required = false)
 JavaMailSenderImpl javaMailSender;

 @Test
 void test3() {
    // 发送简单的邮件
    SimpleMailMessage message = new SimpleMailMessage();
    message.setSubject("标题");
    message.setText("邮件内容");
    message.setFrom("发件人邮箱");
    message.setTo("收件人邮箱");
    javaMailSender.send(message);
    System.out.println("发送成功");
 }

Mybatis

是一个基于Java的持久层框架。

下载地址:https://github.com/mybatis/mybatis-3

关于环境搭建在我的码云SpringBoot案例有:

详细配置:https://mybatis.org/mybatis-3/zh/configuration.html

核心包

<!-- Mybatis核心 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>
<!-- junit测试 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.18</version>
</dependency>
<!-- log4j日志 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

获取参数两种方式

#{} 与 ${}区别:

#{} 占位符;${}本质就是字符串拼接

// 动态设置表名,使用${}
<select id="getUserByTable" resultType="cn.good.yan.pojo.User">
     select * from ${table}
</select>

默认的类型别名

我们在resultType=“别名”,这里写别名,映射对应的类型

alias(别名)mapped type
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
digdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratoriterator

增删改查案例

  1. 添加

    <insert id="insertUser">
    	insert into t_user values(null,'admin','123456')
    </insert>
    
  2. 删除

     <delete id="deleteUser">
         delete from t_user where id = 6
     </delete>
    
  3. 修改

     <update id="updateUser">
         update t_user set username = '张三' where id = 5
     </update>
    
  4. 查询一个实体类对象

    <select id="getUserById" resultType="com.xxx.User">  
    	select * from t_user where id = 2  
    </select>
    
  5. 模糊查询

    <select id="getUserByLike" resultType="com.xxx.User">
    	<!--select * from t_user where username like concat('%',#{mohu},'%')  escape '/' -->  
    	select * from t_user where username like "%"#{mohu}"%"
    </select>
    
  6. 自定义映射resultMap

    <!-- 一对一 -->
    <resultMap id="BillResultMap" type="BillOne">
        <!-- 配置主键 property 属性名  column 数据库字段名-->
        <id property="bid" column="bid"></id>
        <result property="billCode" column="bill_code"></result>
        <result property="user.realName" column="real_name"></result>
        <result property="user.username" column="username"></result>
    </resultMap>
    
    <!-- 一对多 ,集合使用collection -->
    <resultMap id="ContentResultMap" type="Content">
        <id property="id" column="id"></id>
        <result property="content" column="content"></result>
        <collection property="bills" ofType="Bill">
            <id property="bid" column="bid"></id>
            <result property="billCode" column="bill_code"></result>
            <result property="billName" column="bill_name"></result>
        </collection>
    </resultMap>
    <select id="findAll" resultMap="ContentResultMap" >
        select * from content c left join bill b on c.id = b.pid
    </select>
    
    <!-- 多对一 -->
    <resultMap id="BillResultMap" type="Bill">
        <id property="bid" column="bid"></id>
        <result property="billCode" column="bill_code"></result>
        <result property="billName" column="bill_name"></result>
        <association property="content" javaType="Content">
            <id property="id" column="id"></id>
            <result property="content" column="content"></result>
        </association>
    </resultMap>
    <select id="findBillAll" resultMap="BillResultMap">
        select * from bill b left join content c on b.pid = c.id
    </select>
    
  7. 动态SQL

    <!-- if where 标签 -->
    <select id="getEmp" resultType="Emp">
    	select * from t_emp
    	<where>
    		<if test="empName != null and empName !=''">
    			emp_name = #{empName}
    		</if>
    	</where>
    </select>
    
    <!-- trim 标签
    	 prefix:前缀添加内容  suffix:后缀添加内容
    	 prefixOverrides:前缀要去掉内容  suffixOverrides:后缀要去掉内容
    -->
    <select id="getEmp" resultType="Emp">
    	select * from content
        <trim prefix="where" suffixOverrides="and|or">
            <if test="nodeCode != null and nodeCode != ''">
                node_Code = #{nodeCode} and
            </if>
        </trim>
    </select>
    
    <!-- choose、when、otherwise  标签 -->
    <select id="findAllChoose" resultMap="CResultMap" >
        select * from content c left join bill b on c.id = b.pid
        <where>
            <choose>
                <when test="bill.billCode != null and bill.billCode != ''">
                    b.bill_code like concat('%',#{bill.billCode} ,'%') escape '/'
                </when>
                <when test="bill.billName != null and bill.billName != ''">
                    b.bill_name like concat('%',#{bill.billName} ,'%') escape '/'
                </when>
                <!-- 都不满足,执行这里逻辑 -->
                <otherwise>
                    b.pay = 1
                </otherwise>
            </choose>
        </where>
    </select>
    
    <!-- foreach 标签
     	 collection:设置要循环的数组或者集合  item:当前每个数据
    	 separator:循环之间的分隔符
    	 open:循环前的开始符    close:循环后的结束符
    -->
    <delete id="deleteByAll">
        delete from content where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>
    
    <!-- sql 标签 , include 使用 -->
    <sql id="Table_Name"> bill </sql>
    <include refid="Table_Name"/>
    

注解版的增删改查

// useGeneratedKeys是否使用自增主键,keyProperty 指定主键值
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("INSERT INTO bill (id,code,name) VALUES (null,#{code},#{name}) ")
int add(Bill bill);

@Update("update bill set bill_Name=#{billName} where bid=#{bid}")
int update(Bill bill);

@Select("select * from bill where bill_code=#{billCode} ")
Bill findByBillCode(String billCode);

@Select(" select * from bill where bid = #{bid} ")
List<Bill> findByBid(Long bid);

// 批量查询 ,注意:参数是字符串 1,3,4
@Select(" select * from bill where bid in (${bids}) ")
List<Bill> findByBids(String bids);

@Delete("delete from user where uid=#{uid}")
int deletePByPid(Long uid);

延迟加载

# 1. 配置文件:mybatis-config.xml
<settings>
	<!--开启延迟加载-->
	<setting name="lazyLoadingEnabled" value="true"/>
</settings>

# 2. 此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。
#    association和collection中的fetchType属性进行设置,fetchType="lazy(延迟加载)|eager(立即加载)"

	<resultMap id="BillResultMOne" type="Bill">
		<id property="bid" column="bid"></id>
		<result property="billCode" column="bill_code"></result>
		<result property="billName" column="bill_name"></result>
		<!-- 方式3 分步查询  
				fetchType="lazy(延迟加载)|eager(立即加载) 
				property 实体的字段名  select 对应的Mapper文件
				column   关联字段名    fetchType 延迟加载
		-->
		<association property="content"
					 select="com.xxx.dao.ContentMapper.getContent"
					 column="id"
					 fetchType="lazy"></association>
	</resultMap>

MyBatis的缓存

一级缓存

一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,默认是开启的。

失效情况:

  1. 不同的SqlSession下有不同的一级缓存
  2. 查询条件不同
  3. 同一个SqlSession两次查询期间执行了任何一次增删改操作
  4. 同一个SqlSession两次查询期间手动清空了缓存
二级缓存

二级缓存是SqlSessionFactory级别。

开启条件:

  1. 在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置
  2. 在映射文件中设置标签
  3. 二级缓存必须在SqlSession关闭或提交之后有效
  4. 查询的数据所转换的实体类类型必须实现序列化的接口

相关配置:

  • 在mapper配置文件中添加的cache标签可以设置一些属性

  • eviction属性:缓存回收策略

  • LRU 最近最少使用的:移除最长时间不被使用的对象,默认的。

    FIFO 先进先出:按对象进入缓存的顺序来移除它们。

    SOFT 软引用:移除基于垃圾回收器状态和软引用规则的对象。

    WEAK 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

  • flushInterval属性:刷新间隔,单位毫秒

  • 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(增删改)时刷新

  • size属性:引用数目,正整数,代表缓存最多可以存储多少个对象,太大容易导致内存溢出

  • readOnly属性:只读,true/false

    true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。

    false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false

MyBatis缓存查询顺序

  1. 先查询二级缓存,如果有之间返回,没有下一步
  2. 再查询一级缓存,如果有也返回,没有就进行查询数据库
  3. SqlSession关闭,会把当前一级缓存写入到二级缓存
缓存EHCache
# 1.引入包
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

# 2.创建ehcache.xml配置文件,必须以这个为命名
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

    <diskStore path="java.io.tmpdir"/>

    <!--defaultCache:chcache的默认缓存策略  -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>

    <cache name="Bill"
           maxElementsInMemory="10000"
           eternal="false"
           timeToIdleSeconds="120"
           timeToLiveSeconds="120"
           maxElementsOnDisk="10000000"
           diskExpiryThreadIntervalSeconds="120"
           memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </cache>
</ehcache>

# 3.配置文件添加这行
spring:
  # ehcache 缓存配置
  cache:
    ehcache:
      config: classpath:/ehcache.xml
    type: ehcache

# 4.启动类添加这个注解
// 开启缓存
@EnableCaching

# 5.实体类实现可序列化接口Serializable
# 6. 使用
// 定义缓存名字
@CacheConfig(cacheNames = "Bill")

// 进行添加缓存
@Cacheable(value = "Bill",key = "'bill:'+#bid")
public List<Bill> findAll() {
    return billMapper.findAll();
}

// 修改,刷新到缓存中
@CachePut(value = "Bill", key = "#bill.bid")
public Bill updateById(Bill bill) {
    billMapper.updateByBid(bill.getBid());
    return bill;
}
    
// 删除缓存
@CacheEvict(value="Bill", allEntries=true)
public void save(Bill bill) {
	//...
}   

逆向工程

  1. 新建一个springboot 工程

  2. 修改pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.7</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!-- mybatis generator 自动生成代码插件 -->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.7</version>
                <configuration>
                    <!-- 在控制台打印执行日志 -->
                    <verbose>true</verbose>
                    <!-- 重复生成时会覆盖之前的文件-->
                    <overwrite>true</overwrite
                    <configurationFile>
                        src/main/resources/generatorConfig.xml
                    </configurationFile>
                </configuration>
                <!-- 数据库连接选择8.0以上的,因为用的mysql8.0-->
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>8.0.16</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>
    
  3. 创建generator.xml配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
            PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
            "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    <generatorConfiguration>
        <!-- context 是逆向工程的主要配置信息 -->
        <!-- id:起个名字 -->
        <!-- targetRuntime:设置生成的文件适用于那个 mybatis 版本  MyBatis3 奢华版  MyBatis3Simple 简洁版 -->
        <context id="default" targetRuntime="MyBatis3Simple">
    
            <!--optional,指在创建class时,对注释进行控制-->
            <commentGenerator>
                <property name="suppressDate" value="true"/>
                <!-- 是否去除自动生成的注释 true:是 : false:否 -->
                <property name="suppressAllComments" value="true"/>
            </commentGenerator>
    
            <!--jdbc的数据库连接 wg_insert 为数据库名字-->
            <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                            connectionURL="jdbc:mysql://10.238.233.180:3306/b?useUnicode=true&amp;characeterEncoding=utf-8&amp;serverTimezone=UTC"
                            userId="root"
                            password="xxx"></jdbcConnection>
    
            <!--非必须,类型处理器,在数据库类型和java类型之间的转换控制-->
            <javaTypeResolver>
                <!-- 默认情况下数据库中的 decimal,bigInt 在 Java 对应是 sql 下的 BigDecimal 类 -->
                <!-- 不是 double 和 long 类型 -->
                <!-- 使用常用的基本类型代替 sql 包下的引用类型 -->
                <property name="forceBigDecimals" value="false"/>
            </javaTypeResolver>
    
            <!-- targetPackage:生成的实体类所在的包 -->
            <!-- targetProject:生成的实体类所在的硬盘位置 -->
            <javaModelGenerator targetPackage="com.good.yan.modules.entities"
                                targetProject="src/main/java">
                <!-- 是否允许子包 -->
                <property name="enableSubPackages" value="false"/>
                <!-- 是否对modal添加构造函数 -->
                <property name="constructorBased" value="true"/>
                <!-- 是否清理从数据库中查询出的字符串左右两边的空白字符 -->
                <property name="trimStrings" value="true"/>
                <!-- 建立modal对象是否不可改变 即生成的modal对象不会有setter方法,只有构造方法 -->
                <property name="immutable" value="false"/>
            </javaModelGenerator>
            <!-- targetPackage 和 targetProject:生成的 mapper 文件的包和位置 -->
            <sqlMapGenerator targetPackage="com.good.yan.modules.mapper"
                             targetProject="src/main/java">
                <!-- 针对数据库的一个配置,是否把 schema 作为字包名 -->
                <property name="enableSubPackages" value="false"/>
            </sqlMapGenerator>
            <!-- targetPackage 和 targetProject:生成的 interface 文件的包和位置 -->
            <javaClientGenerator type="XMLMAPPER"
                                 targetPackage="com.good.yan.modules.dao"
                                 targetProject="src/main/java">
                <!-- 针对 oracle 数据库的一个配置,是否把 schema 作为字包名 -->
                <property name="enableSubPackages" value="false"/>
            </javaClientGenerator>
            <!-- tableName是数据库中的表名,domainObjectName是生成的JAVA模型名,后面的参数不用改,要生成更多的表就在下面继续加table标签 -->
            <!--<table tableName="student" domainObjectName="Student"
                   enableCountByExample="false" enableUpdateByExample="false"
                   enableDeleteByExample="false" enableSelectByExample="false"
                   selectByExampleQueryId="false"></table>-->
            <table tableName="t_md_org" domainObjectName="Org"
                   enableCountByExample="false" enableUpdateByExample="false"
                   enableDeleteByExample="false" enableSelectByExample="false"
                   selectByExampleQueryId="false"></table>
        </context>
    </generatorConfiguration>
    
  4. 运行,点击maven中的generate进行运行。

分页插件

# 1.引包
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper</artifactId>
	<version>5.2.0</version>
</dependency>

# 2.配置分页插件
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!--分页参数合理化  -->
        <property name="reasonable" value="true"/>
    </plugin>
</plugins>

# 3.案例
public void testPageHelper() throws IOException {
	PageHelper.startPage(1, 2);
 	List<Bill> all = billMapper.findAll();
    // 参数:数据 、 页码 尽量是奇数  1,2,3,4,5
    PageInfo<Bill> pageInfo = new PageInfo<>(all,5);
}

常用数据:

  • pageNum:当前页的页码
  • pageSize:每页显示的条数
  • size:当前页显示的真实条数
  • total:总记录数
  • pages:总页数
  • prePage:上一页的页码
  • nextPage:下一页的页码
  • isFirstPage/isLastPage:是否为第一页/最后一页
  • hasPreviousPage/hasNextPage:是否存在上一页/下一页
  • navigatePages:导航分页的页码数
  • navigatepageNums:导航分页的页码,[1,2,3,4,5]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值