由浅入深使用validation框架进行参数校验

news2025/1/9 14:12:07

1 引言

平时在业务开发过程中 controller 层的参数校验有时会存在下面这样的判断

public String add(UserVO userVO) {
    if(userVO.getAge() == null){
        return "年龄不能为空";
    }
    if(userVO.getAge() > 120){
        return "年龄不能超过120";
    }
    if(userVO.getName().isEmpty()){
        return "用户名不能为空";
    }
    // 省略一堆参数校验...
    return "OK";
}

业务代码还没开始写呢,光参数校验就写了一堆判断。这样写虽然没什么错,但是给人的感觉就是:不优雅,不专业。

其实java给我们提供了一组规范:JSR-303,spring又实现并扩展了这个规范,让我们能够优雅无侵入的实现参数的校验

2 JSR-303 规范

2.1 JSR:Java 规范提案

JSR即为Java Specification Requests的缩写,是Java 规范提案,常常定义一组规则,有的规则有默认的实现,有的则需要用户自己去实现。

2.2 JSR-303:Bean Validation规范

JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,为Bean验证定义了元数据模型和API,默认的元数据模型是通过Annotations注解来描述的,支持扩展。

官方参考的默认实现是Hibernate Validator,当然用户也可以自己扩展,比如常用的Spring JSR-303

注意:这只是一组规范,对应到代码就是一组接口,不能直接使用
这组规范在javax所提供的jar:jakarta.validation-api-xxx.jar中,要想使用,则需要导入并实现这组规范

 <dependency>
     <groupId>jakarta.validation</groupId>
     <artifactId>jakarta.validation-api</artifactId>
     <version>2.0.2</version>
 </dependency>

2.3 JSR-303 基本的约束原理

一个 constraint 通常由 annotation 和相应的 constraint validator 组成,它们是一对多的关系。

也就是说可以有多个 constraint validator 对应一个 annotation。

在运行时,Bean Validation 框架本身会根据被注释元素的类型来选择合适的 constraint validator 对数据进行验证。

  • constraint:一个约束
    • annotation:注解
    • constraint validator:约束校验器(可有多个)

有些时候,在用户的应用中需要一些更复杂的 constraint。Bean Validation 提供扩展 constraint 的机制,可以通过两种方法去实现。

  • 一种是组合现有的 constraint 来生成一个更复杂的 constraint
  • 另外一种是开发一个全新的 constraint。

2.4 JSR-303 中内置的约束

内置的约束注解如下图:
在这里插入图片描述

3 JSR-303规范的实现

3.1 Hibernate Validator

这是官方参考的默认实现,Hibernate JSR-303导入并实现了jakarta.validation-api,并对JSR-303默认的约束做了扩展,注意此实现与 Hibernate ORM 没有任何关系。

3.1.1 Hibernate Validator 附加的 constraint

Constraint详细信息
@Email被注释的元素必须是电子邮箱地址
@Length被注释的字符串的大小必须在指定的范围内
@NotEmpty被注释的字符串的必须非空
@Range被注释的元素必须在合适的范围内

3.1.2 导入依赖

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-el</artifactId>
    <version>9.0.63</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.3.Final</version>
    <scope>compile</scope>
</dependency>

3.1.3 简单使用

package myValid;

import lombok.Data;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import java.util.Iterator;
import java.util.Set;

@Data
public class User {

    @NotNull(message = "email不可以为空")
    @Email(message = "email不合法")
    private String email;

    public static void main(String[] args) {
        final User user = new User();
        user.setEmail("xxxxxx.com");
        //调用JSR303验证工具,校验参数
        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<User>> violations = validator.validate(user);
        Iterator<ConstraintViolation<User>> iter = violations.iterator();
        if (iter.hasNext()) {
            final ConstraintViolation<User> violation = iter.next();
            // email:email不合法
            System.out.println(violation.getPropertyPath() + ":" + violation.getMessage());
            // TODO 可以进行指定约束异常的抛出
        }
    }

}

3.2 Spring JSR-303

3.2.1 官方说明

JSR-303的javax.validation的变体。有效,支持验证组的规范。为方便使用Spring的JSR-303支持而设计,但不是JSR-303特有的。

可以与Spring MVC处理程序方法参数一起使用。通过org.springframework.validation.SmartValidator的验证提示概念支持,验证组类充当提示对象。

也可以与方法级验证一起使用,表示特定类应该在方法级进行验证(充当相应验证拦截器的切入点),但也可以在带注释的类中为方法级验证指定验证组。在方法级别应用此注释允许覆盖特定方法的验证组,但不作为切入点;然而,类级注释对于触发特定bean的方法验证是必要的。也可以用作自定义原型注释或自定义组特定的验证注释上的元注释。

3.2.2 通俗解释

  • 实现了JSR-303标准
  • 支持分组验证(特有)
  • 可以与Spring MVC收参的方法一起使用

4 Spring Validator的使用

4.1 导入依赖

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

点进入发现Spring Validator包含了Hibernate Validator,所以,spring环境下,我们直接使用Spring Validator会非常非常方便

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.7.0</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-el</artifactId>
      <version>9.0.63</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.2.3.Final</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

4.2 常用的约束说明

验证注解验证的数据类型说明
@AssertFalseBoolean,boolean验证注解的元素值是false
@AssertTrueBoolean,boolean验证注解的元素值是true
@NotNull任意类型验证注解的元素值不是null
@Null任意类型验证注解的元素值是null
@Min(value=值)BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型验证注解的元素值大于等于@Min指定的value值
@Max(value=值)和@Min要求一样验证注解的元素值小于等于@Max指定的value值
@DecimalMin(value=值)和@Min要求一样验证注解的元素值大于等于@ DecimalMin指定的value值
@DecimalMax(value=值)和@Min要求一样验证注解的元素值小于等于@ DecimalMax指定的value值
@Digits(integer=整数位数, fraction=小数位数)和@Min要求一样验证注解的元素值的整数位数和小数位数上限
@Size(min=下限, max=上限)字符串、Collection、Map、数组等验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、集合大小
@Pastjava.util.Date,java.util.Calendar;Joda Time类库的日期类型验证注解的元素值(日期类型)比当前时间早
@Future与@Past要求一样验证注解的元素值(日期类型)比当前时间晚
@NotBlankCharSequence子类型验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格
@Length(min=下限, max=上限)CharSequence子类型验证注解的元素值长度在min和max区间内
@NotEmptyCharSequence子类型、Collection、Map、数组验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@Range(min=最小值, max=最大值)BigDecimal,BigInteger,CharSequence, byte, short, int, long等原子类型和包装类型验证注解的元素值在最小值和最大值之间
@Email(regexp=正则表达式,flag=标志的模式)CharSequence子类型(如String)验证注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式
@Pattern(regexp=正则表达式,flag=标志的模式)String,任何CharSequence的子类型验证注解的元素值与指定的正则表达式匹配
@Valid任何非原子类型指定递归验证关联的对象如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加@Valid注解即可级联验证

4.3 重点:使用技巧

一般情况下,我们都是校验方法上面的形参,既然是形参,就分两种情况:

  • 一种是校验形参本身,这种情况需要在当前类上面添加@Validated注解,同时使用@NotNull等注解对该形参进行校验
  • 另一种则是形参内部属性的校验,这种情况可以在当前类上面添加@Validated注解,也可以不加,但是一定要在该形参上添加@Validated注解
  • 未通过校验时会抛出org.springframework.web.bind.MethodArgumentNotValidExceptionorg.springframework.validation.BindException等异常,我们捕获统一返回即可

下面进行举例说明,比如说我们有一个DocumentTransController

@RestController
@RequestMapping("/documentTrans")
public class DocumentTransController {

    private final DocumentTransService documentTransService;
    public DocumentTransController(DocumentTransService documentTransService) {
        this.documentTransService = documentTransService;
    }

    /**
     * 接收翻译请求(接收文件)
     * @return
     */
    @RequestMapping(value = "/documentTransByFile", method = RequestMethod.POST)
    public String transDocumentByFile(MultipartFile file,DocumentInfoBo documentInfoBo) {
        return documentTransService.documentTrans(file,documentInfoBo);
    }
    
}

@Data
public class DocumentInfoBo {
    /**
     * 所属用户id
     */
    @NotNull(message = "用户id不能为空")
    private Long userId;
    
    private UserInfo userInfo;

    @Data
    static class UserInfo {
        @NotNull(message = "用户名不能为空")
        private String username;
        private String phone;
    }
}    

4.3.1 校验方法形参本身

我现在想要校验transDocumentByFile()的file不能为空,则需要在DocumentTransController 类上添加@Validated注解,同时在(MultipartFile file)前面添加@NotNull 注解,如下:

@RestController
@RequestMapping("/documentTrans")
@Validated
public class DocumentTransController {

    private final DocumentTransService documentTransService;

    public DocumentTransController(DocumentTransService documentTransService) {
        this.documentTransService = documentTransService;
    }

    /**
     * 接收翻译请求(接收文件)
     * @return
     */
    @RequestMapping(value = "/documentTransByFile", method = RequestMethod.POST)
    public String transDocumentByFile(@NotNull MultipartFile file, DocumentInfoBo documentInfoBo) {
        return documentTransService.documentTrans(file,documentInfoBo);
    }
    
}

4.3.2 校验方法形参内部属性

在4.3.1的基础上,我现在想要校验DocumentInfoBo对象内的userId属性不能为空,则需要在(DocumentInfoBo documentInfoBo)前面添加@Validated注解

@RestController
@RequestMapping("/documentTrans")
@Validated
public class DocumentTransController {

    private final DocumentTransService documentTransService;

    public DocumentTransController(DocumentTransService documentTransService) {
        this.documentTransService = documentTransService;
    }

    /**
     * 接收翻译请求(接收文件)
     * @return
     */
    @RequestMapping(value = "/documentTransByFile", method = RequestMethod.POST)
    public String transDocumentByFile(@NotNull MultipartFile file,@Validated DocumentInfoBo documentInfoBo) {
        return documentTransService.documentTrans(file,documentInfoBo);
    }
    
}

4.3.3 嵌套验证

现在我想在4.3.1和4.3.2基础上,添加对userInfo属性的校验,则需要在userInfo上面添加 @Valid @NotNull(message = "userInfo不能为空")即可

@Data
public class DocumentInfoBo {
    /**
     * 所属用户id
     */
    @NotNull(message = "用户id不能为空")
    private Long userId;
    
    @Valid
    @NotNull(message = "userInfo不能为空")
    private UserInfo userInfo;

    @Data
    static class UserInfo {
        @NotNull(message = "用户名不能为空")
        private String username;
        private String phone;
    }
}    

4.3.4 分组参数校验

第一步、定义一个校验组类,声明四个接口对应不同场景校验

public class ValidationGroups {

	public interface Select {
	}
	
	public interface Insert {
	}
	
    public interface Update {
    }
    
    public interface Detail {
    }
  
    public interface Delete{

    }
}

第二步、在实体类具体属性添加校验规则及校验分组

@Data
public class Person {

    @NotNull(message = "personId不能为null",groups = Select.class)
    private Integer personId;
    
    @NotEmpty(message = "name不能为空",groups = Insert.class)
    private String name;
    
    private Integer age;
}

第三步、在控制层创建查询和新增接口,添加@Validated注解,指定分组,可多个。

    @GetMapping("/select")
    public Object select(@Validated({ValidationGroups.Select.class}) Person person) {
        return person;
    }

    @GetMapping("/insert")
    public Object insert(@Validated(ValidationGroups.Insert.class) Person person) {
        return person;
    }

4.4 重点:注意事项

Spring Validator不光可以用在Controller层,同样也可以作用于service、或者其他bean

4.5 重点:@Validated和@Valid的区别

很多时候@Validated和@Valid的界限分的并没有那么清楚,很多场景@Validated和@Valid效果也等同,那么他们到底有什么区别呢?什么时候该用哪个注解呢?

在检验Controller的入参是否符合规范时,使用@Validated或者@Valid在基本验证功能上没有太多区别。但是在分组、注解地方、嵌套验证等功能上两个有所不同:

@Validated@Valid
分组提供分组功能,可在入参验证时,根据不同的分组采用不同的验证机制。无分组功能
可注解位置可以用在类型、方法和方法参数上。但是不能用在成员属性上可以用在方法、构造函数、方法参数和成员属性上(两者是否能用于成员属性上直接影响能否提供嵌套验证的功能)
嵌套验证用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性上。也无法提供框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。
  • @Valid 被注释的元素是一个对象,需要检查此对象的所有字段值
  • @Validated 被注解的元素是一个对象或者一个类,需要检查此对象的所有字段值

使用建议:常规情况使用@Validated注解,嵌套验证时使用@Valid注解

4.6 校验异常统一处理示例

异常对应:

  • 使用form data方式调用接口,校验异常抛出 BindException
  • 使用 json 请求体调用接口,校验异常抛出 MethodArgumentNotValidException
  • 单个参数校验异常抛出ConstraintViolationException
@RestControllerAdvice
public class ExceptionHandle {

    private final Logger logger = LoggerFactory.getLogger(getClass());
    
 	@ExceptionHandler(value = Exception.class)
    public JSONResult<Object> handle(Exception e) {
        logger.error(e.getMessage(), e);
        if (e instanceof MyException) {
            MyException myException = (MyException) e;
            return JSONResult.error(myException.getCode(),myException.getMessage());
        } else {
            return JSONResult.error(ResultEnum.UNKNOWN_ERROR.getCode(), e.getMessage());
        }
    }

    /**
     * 方法参数校验
     * 由于spring捕获异常的问题,导致此异常的编码格式无法修改为UTF-8,
     * 故不使用RestControlle的ResponseBody,自己实现httpServletResponse的IO。
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public JSONResult<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        final BindingResult bindingResult = e.getBindingResult();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        String msg = ResultEnum.VALIDATION_ERROR.getMsg();
        if(!fieldErrors.isEmpty()){
            // 返回错误信息
            msg = fieldErrors.get(0).getField()+":"+fieldErrors.get(0).getDefaultMessage();
        }
        logger.error(e.getMessage(), e);
        return JSONResult.error(ResultEnum.VALIDATION_ERROR.getCode(), msg.isEmpty()?e.getCause().getMessage():msg);
    }

    /**
     * 捕获校验异常
     * @param e
     * @return
     */
    @ExceptionHandler(ValidationException.class)
    public JSONResult<Object> handleValidationException(ValidationException e) {
        logger.error(e.getMessage(), e);
        return JSONResult.error(ResultEnum.VALIDATION_ERROR.getCode(), e.getCause().getMessage());
    }


    /**
     * 捕获校验绑定异常
     * @param e
     * @return
     */
    @ExceptionHandler(BindException.class)
    public JSONResult<Object> handleBindException(BindException e) {
        logger.error(e.getMessage(), e);
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        String msg = "";
        if(!fieldErrors.isEmpty()){
            // 返回错误信息
            msg = fieldErrors.get(0).getDefaultMessage();
        }
        return JSONResult.error(ResultEnum.VALIDATION_ERROR.getCode(), msg.isEmpty()?e.getCause().getMessage():msg);
    }

    /**
     * 捕获违反约束异常
     * @param e
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public JSONResult<Object> handConstraintViolationException(ConstraintViolationException e) {
        final Iterator<ConstraintViolation<?>> iterator = e.getConstraintViolations().iterator();
        while (iterator.hasNext()) {
            final ConstraintViolation<?> constraintViolation = iterator.next();
            final String errorMsg = constraintViolation.getMessage();
            logger.error(e.getMessage(), e);
            return JSONResult.error(ResultEnum.VALIDATION_ERROR.getCode(), errorMsg);
        }
        return JSONResult.error(ResultEnum.VALIDATION_ERROR.getCode(), e.getMessage());
    }

5 扩展:注解失效的原因排查

1、是否忘记导入依赖
在2.3.0版本之前spring-boot-starter-web是默认集成validation依赖的,但是在2.3.0开始就去掉了该依赖,所以需要自己添加。

2、如果要校验对象本身,则需要在所在类上面添加@Validated注解
在这里插入图片描述
3、涉及嵌套验证,是否忘记添加@Valid注解

6 使用ConstraintValidator自定义注解和校验器

https://blog.csdn.net/weixin_43702146/article/details/125657418

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/170514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mysql安全之权限用户管理参考手册

一、背景 日常Mysql维护过程中&#xff0c;基于安全要求和规定&#xff0c;需要对Mysql进行分权&#xff0c;接入金库、账户密码满足16位复杂度&#xff0c;对特定表授权&#xff0c;只读用户&#xff0c;最小化权限等处理&#xff1b;本文简要梳理下常用命令操作&#xff0c;…

vue2.x中$attrs的使用

最近笔者在做大屏项目的时候&#xff0c;由于组件数据传递&#xff0c;一层传递一层&#xff0c;使用vuex或者pinia又显得过于笨重。故而想起了那个传说中的v-bind"$attrs"以及v-on"$listeners"&#xff0c;下面就来聊下使用&#xff1a; 上图组件之间的关…

2022年度盘点|FinClip 扩展SDK推荐

2022年&#xff0c;FinClip 团队进行了24个产品迭代&#xff0c;为了丰富FinClip 的平台能力&#xff0c;除了核心SDK之外&#xff0c;我们还为开发者们提供了扩展SDK&#xff0c;扩展SDK是一个依赖核心SDK的库&#xff0c;里面提供了核心SDK中所没有的各种小程序API。 官方希…

[从零开始]用python制作识图翻译器·四

具体实现 整个工程文件已经上传到我的代码仓库。 正式开始 项目结构 数据存储 因为本系统为自用而非商用&#xff0c;并且偏向功能性&#xff0c;所以直接用一个config.json文件保存所有的用户数据&#xff0c;就不用加密了。 原型设计 如图&#xff0c;本系统一共包含两个用…

mp4是什么格式?如何录制mp4格式的视频?一招解决

相信不少的小伙伴都听说过mp4格式&#xff0c;或者在日常的学习生活中就接触过不少mp4格式的文件。但也有不少的小伙伴还不是很了解mp4。mp4是什么格式&#xff1f;我们又该如何录制mp4格式的视频呢&#xff1f;今天小编教大家一招&#xff0c;轻松解决这个问题。 一、mp4是什么…

python3.11安装, 解决pip is configured with locations that require TLS/SSL问题

系统&#xff1a;centos7.4&#xff08;虚拟机&#xff09;python版本&#xff1a;本机自带的2.7.5&#xff0c;以及参考python安装的python3.11pip版本&#xff1a;本机自带的8.1.2&#xff0c;参考pip安装&升级升级到了20.3.4&#xff0c;pip3版本为22.3.1openssl版本&am…

【新年快乐】禁止燃放烟花爆竹那就用Python画场烟花秀吧

目录 前言 Python界面设计之Tkinter 简单创建一个界面 在界面上创建画布 导入图片到界面中 其他一些重要函数 用Python画一场烟花秀 烟花的粒子类 烟花秀的实现 完整程序 前言 新年即将来临&#xff0c;在这个举国欢庆的日子里&#xff0c;怎么能少的了灿烂又热…

Spring AOP统一功能处理(切面、切点、连接点、通知)

目录 一、 AOP的一些前置知识 1.1什么是Aop 1.2 AOP的作用 1.3AOP基础组成 二、SpringAOP的实现 2.1添加SpringAOP框架支持 2.2定义切面(Aspect) 2.3定义切点(Pointcut) 2.4定义通知(Advice) 三、实例展示&#xff08;计时器&#xff09; 代码实现 一、 AOP的一些前…

常用损失函数-交叉熵损失函数、MAE、MSE、smoothL1

目录标题常见的损失函数1、分类任务1.1 多分类任务1.2 二分类任务2、 回归任务2.1 MAE损失2.2 MSE损失2.3 smooth L1损失总结常见的损失函数 损失函数&#xff1a;衡量模型参数的质量的函数&#xff0c;衡量方式是比较网络输出和真实输出的差异。ybar与y 之间的差异 损失函数、…

Jtag_To_AXI的简单使用

文章目录基本操作其他基本操作 搭建的bd文件如下所示&#xff0c; 对tcl命令进行封装后&#xff0c;测试结果如下&#xff0c; 其他 网上看到JTAG to AXI Master的API函数读写操作这篇文章&#xff0c;试着复现一下&#xff0c;没成功&#xff0c;VS工程下的log文件显示打…

量子应用与未来市场:浅谈一下未来的发展方向

量子通信和量子网络 量子信息科学领域探索的是如何在量子系统中国将信息编码&#xff0c;包括量子力学的相关统计、存在的局限性和独特的可解释性&#xff0c;量子信息科学领域是量子计算、量子通信、量子传感的基础。量子通信的研发重点是在对信息进行量子编码之后&#xff0…

Long和Integer相互转换

目录一、int和long互相转换&#xff08;一&#xff09;long转化为int&#xff08;1&#xff09;类型强制转换&#xff08;2&#xff09;利用BigDecimal强制转换&#xff08;二&#xff09;int转化为long&#xff08;1&#xff09;类型强制转换&#xff08;2&#xff09;利用Big…

【go语言http2.0client源码分析】

go语言之http2.0client源码分析client.GetNewRequestdo requesthttp2.Transport.RoundTripGetClientConnnewClientConnReadFrameprocessHeaderswrite request上一篇分析了http2.0的实现之后,这里分析一下client的实现。 package mainimport ("crypto/tls""cryp…

第二层:对象的初始化和清理

文章目录前情回顾对象特性——对象的初始化和清理构造函数和析构函数什么是构造函数和析构函数构造函数构造函数的语法构造函数的分类按照参数分类按照类型分类拷贝构造函数拷贝函数的调用时机深拷贝和浅拷贝构造函数的调用方法括号法显示法隐式转换法构造函数规则析构函数析构…

DM8:达梦数据库DEM--dmagent监控服务器代理部署(详细步骤)

DM8:达梦数据库DEM部署dmagent代理环境&#xff08;详细步骤&#xff09;1 dmagent代理下载部署1.1通过web DEM下载代理包1.2 从数据库服务器目录直接拷贝dmagent2 部署JDK环境2.1 使用数据库自带的JDK包2.2 配置服务器JDK环境变量3 配置agent.ini文件3.1 103 节点 &#xff0c…

C语言——运算符与表达式

一、赋值运算符 运算符描述实例将右边操作数的值赋给左边操作数AB,即将B的值赋给A加法赋值运算符&#xff0c;将右边操作数加上左边操作数的结果赋值给左边操作数B A 等价于 B B A-减法赋值运算符&#xff0c;将左边操作数减去右边操作数的结果赋值给左边操作数B - A 等价于…

擎创动态 | 酸了酸了,这年会也忒燃了吧

前言&#xff1a;受疫情影响的这一年里&#xff0c;擎创科技逆流勇上&#xff0c;汇聚点点星火&#xff0c;点亮新的征程。擎创的服务随之正式遍及全国&#xff0c;北起重工业基地的东三省&#xff0c;南至改革开放最早的粤港澳大湾区&#xff0c;东起经济中心的大本营上海&…

数据开发面试问题记录

因作者近期正在投递数据开发岗位&#xff0c;所以会在此记录一些面试过程中的问题&#xff0c;持续更新&#xff0c;直到入职新公司为止 1. 数仓建模的三范式理论 所谓的范式&#xff0c;就是我们在关系建模的时候所遵从的一些规范&#xff0c;而三范式&#xff0c;指的就是三…

QML工程之初始工程代码分析

接着上一讲&#xff0c;当建立完工程之后&#xff0c;IDE 会呈现如下的界面下面的代码是main.cpp&#xff0c;工程起始运行的代码段&#xff0c;具体的函数说明都在代码段里面进行了标注。#include <QGuiApplication> //主要是ui进程运行头函数&#xff0c;包含事件循环&…

寻找数组的中心下标

Python-寻找数组的中心下标 题目 给你一个整数数组 nums &#xff0c;请计算数组的 中心下标 。 数组 中心下标 是数组的一个下标&#xff0c;其左侧所有元素相加的和等于右侧所有元素相加的和。 如果中心下标位于数组最左端&#xff0c;那么左侧数之和视为 0 &#xff0c;因…