05 | @Query 解决了什么问题?什么时候应该选择它?

news2024/11/20 9:38:38

上个课时我们介绍了 Query Define Method 的语法,这一课时来介绍一下 @Query 注解的语法是什么样的。我们通过快速体验 @Query 的方法、JpaQueryLookupStrategy 关键源码剖析、@Query 的基本用法、@Query 之 Projections 应用返回指定 DTO、@Query 动态查询解决方法,这几个部分来掌握 @Query 的用法。在学会了之后,你就可以应对工作中常见的 CURD 的写法问题。

快速体验 @Query 的方法

开始之前,首先来看一个 Demo,沿用我们之前的例子,新增一个 @Query 的方法,快速体验一下 @Query 的使用方法,如下所示:

复制代码

package com.example.jpa.example1;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface UserDtoRepository extends JpaRepository<User,Long> {
   //通过query注解根据name查询user信息
   @Query("From User where name=:name")
   User findByQuery(@Param("name") String nameParam);
}

然后,我们新增一个测试类:

复制代码

package com.example.jpa.example1;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
@DataJpaTest
public class UserRepositoryQueryTest {
   @Autowired
   private UserDtoRepository userDtoRepository;
   @Test
   public void testQueryAnnotation() {
//新增一条数据方便测试      userDtoRepository.save(User.builder().name("jackxx").email("123456@126.com").sex("man").address("shanghai").build());
      //调用上面的方法查看结果
      User user2 = userDtoRepository.findByQuery("jack");
      System.out.println(user2);
   }
}

最后,看到运行的结果如下:

复制代码

Hibernate: insert into user (address, email, name, sex, version, id) values (?, ?, ?, ?, ?, ?)
Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.email as email3_0_, user0_.name as name4_0_, user0_.sex as sex5_0_, user0_.version as version6_0_ from user user0_ where user0_.name=?
User(id=1, name=jack, email=123456@126.com, version=0, sex=man, address=shanghai)

通过上面的例子我们发现,这次不是通过方法名来生成查询语法,而是 @Query 注解在其中起了作用,使 "From User where name=:name"JPQL 生效了。那么它的实现原理是什么呢?我们通过源码来看一下。

JpaQueryLookupStrategy 关键源码剖析

我们在 03 课时已经介绍过 QueryLookupStrategy 的策略值有哪些,那么我们来看下源码是如何起作用的。

我们先打开 QueryExecutorMethodInterceptor 类,找到如下代码:

Drawing 0.png

再运行上面的测试用例,这时候在这里设置一个断点,可以看到默认的策略是CreateIfNotFound,也就是如果有@Query注解,那么以@Query的注解内容为准,可以忽略方法名。

我们继续往后面看,进入到 lookupStrategy.resolveQuery 里面,如下所示:

Drawing 1.png

通过上图的断点和红框之处,我们也发现了,Spring Data JPA 这个地方使用了策略、模式,当我们自己写策略模式的时候也可以进行参考。

那么接着往下 debug,进入到 resolveQuery 方法里面,如下图所示:

Drawing 2.png

我们可以看到图中 ①处,如果 Query 注解找到了,就不会走到 ② 处了(即我们第 03 课时中讲的 Defined Query Method 语法)。

这时我们点开 Query 里面的 Query 属性的值看一下,你会发现这里同时生成了两个 SQL:一个是查询总数的 Query 定义,另一个是查询结果 Query 定义。

到这里我们已经基本明白了,如果想看看 Query 具体是怎么生成的、上面的 @Param 注解是怎么生效的,可以在上面的图 ① 处 debug 继续往里面看,如下所示:

Drawing 3.png

我们继续一路 debug 就可以看到怎么通过 @Query 去生成 SQL 了,这个不是本节的重点,我在这里就简单带过了,你有兴趣可以自己去 debug 看一下。

那么原理我们掌握了,接下来看看 @Query 给我们提供了哪些语法吧,先看下基本用法。

@Query 的基本用法

在讲解它的语法之前,我们看一下它的注解源码,了解一下基本用法。

复制代码

package org.springframework.data.jpa.repository;
public @interface Query {
   /**
    * 指定JPQL的查询语句。(nativeQuery=true的时候,是原生的Sql语句)
	*/
   String value() default "";
   /**
	* 指定count的JPQL语句,如果不指定将根据query自动生成。
    * (如果当nativeQuery=true的时候,指的是原生的Sql语句)
    */
   String countQuery() default "";
   /**
    * 根据哪个字段来count,一般默认即可。
	*/
   String countProjection() default "";
   /**
    * 默认是false,表示value里面是不是原生的sql语句
	*/
   boolean nativeQuery() default false;
   /**
    * 可以指定一个query的名字,必须唯一的。
	* 如果不指定,默认的生成规则是:
    * {$domainClass}.${queryMethodName}
    */
   String name() default "";
   /*
    * 可以指定一个count的query的名字,必须唯一的。
	* 如果不指定,默认的生成规则是:
    * {$domainClass}.${queryMethodName}.count
    */
   String countName() default "";
}

所以到这里你会发现, @Query 用法是使用 JPQL 为实体创建声明式查询方法。我们一般只需要关心 @Query 里面的 value 和 nativeQuery、countQuery 的值即可,因为其他的不常用。

使用声明式 JPQL 查询有个好处,就是启动的时候就知道你的语法正确不正确。那么我们简单介绍一下 JPQL 语法。

JPQL 的语法

我们先看一下查询的语法结构,代码如下:

复制代码

SELECT ... FROM ...
[WHERE ...]
[GROUP BY ... [HAVING ...]]
[ORDER BY ...]

你会发现它的语法结构有点类似我们 SQL,唯一的区别就是 JPQL FROM 后面跟的是对象,而 SQL 里面的字段对应的是对象里面的属性字段。

同理我们看一下 update 和 delete 的语法结构:

复制代码

DELETE FROM ... [WHERE ...]
 
UPDATE ... SET ... [WHERE ...]

其中“…”省略的部分是实体对象名字和实体对象里面的字段名字,而其中类似 SQL 一样包含的语法关键字有:SELECT FROM WHERE UPDATE DELETE JOIN OUTER INNER LEFT GROUP BY HAVING FETCH DISTINCT OBJECT NULL TRUE FALSE NOT AND OR BETWEEN LIKE IN AS UNKNOWN EMPTY MEMBER OF IS AVG MAX MIN SUM COUNT ORDER BY ASC DESC MOD UPPER LOWER TRIM POSITION CHARACTER_LENGTH CHAR_LENGTH BIT_LENGTH CURRENT_TIME CURRENT_DATE CURRENT_TIMESTAMP NEW EXISTS ALL ANY SOME 这么多,我们就不一一介绍了。
这个语法用起来不复杂,遇到问题时,简单想一下 SQL 就可以知道了,我推荐一个 Oracle 的文档地址:https://docs.oracle.com/html/E13946_04/ejb3_langref.html,你也可以通过查看这个文档,找到解决问题的办法。

那么 JPQL 的语法你已经大概了解了,我们再来看下 @Query 怎么使用。

@Query 用法案例

我们通过几个案例来了解一下 @Query 的用法,你就可以知道 @Query 怎么使用、怎么传递参数、怎么分页等。

案例 1: 要在 Repository 的查询方法上声明一个注解,这里就是 @Query 注解标注的地方。

复制代码

public interface UserRepository extends JpaRepository<User, Long>{
  @Query("select u from User u where u.emailAddress = ?1")
  User findByEmailAddress(String emailAddress);
}

案例 2: LIKE 查询,注意 firstname 不会自动加上“%”关键字。

复制代码

public interface UserRepository extends JpaRepository<User, Long> {
  @Query("select u from User u where u.firstname like %?1")
  List<User> findByFirstnameEndsWith(String firstname);
}

案例 3: 直接用原始 SQL,nativeQuery = true 即可。

复制代码

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
  User findByEmailAddress(String emailAddress);
}

注意:nativeQuery 不支持直接 Sort 的参数查询。

案例 4: 下面是****nativeQuery 的排序错误的写法,会导致无法启动。

复制代码

public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "select * from user_info where first_name=?1",nativeQuery = true)
List<UserInfoEntity> findByFirstName(String firstName,Sort sort);
}

案例 5: nativeQuery 排序的正确写法。

复制代码

@Query(value = "select * from user_info where first_name=?1 order by ?2",nativeQuery = true)
List<UserInfoEntity> findByFirstName(String firstName,String sort);
//调用的地方写法last_name是数据里面的字段名,不是对象的字段名
repository.findByFirstName("jackzhang","last_name");

通过上面几个案例,我们看到了 @Query 的几种用法,你就会明白排序、参数、使用方法、LIKE、原始 SQL 怎么写。下面继续通过案例来看下 @Query 的排序。

@Query 的排序

@Query中在用JPQL的时候,想要实现排序,方法上直接用 PageRequest 或者 Sort 参数都可以做到。

在排序实例中,实际使用的属性需要与实体模型里面的字段相匹配,这意味着它们需要解析为查询中使用的属性或别名。我们看一下例子,这是一个state_field_path_expression JPQL的定义,并且 Sort 的对象支持一些特定的函数。

案例 6: Sort and JpaSort 的使用,它可以进行排序。

复制代码

public interface UserRepository extends JpaRepository<User, Long> {
  @Query("select u from User u where u.lastname like ?1%")
  List<User> findByAndSort(String lastname, Sort sort);
  @Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%")
  List<Object[]> findByAsArrayAndSort(String lastname, Sort sort);
}
//调用方的写法,如下:
repo.findByAndSort("lannister", new Sort("firstname"));
repo.findByAndSort("stark", new Sort("LENGTH(firstname)"));
repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)"));
repo.findByAsArrayAndSort("bolton", new Sort("fn_len"));

上面这个案例讲述的是排序用法,再来看下 @Query 的分页用法。

@Query 的分页

@Query 的分页分为两种情况,分别为 JPQL 的排序和 nativeQuery 的排序。看下面的案例。

案例 7:直接用 Page 对象接受接口,参数直接用 Pageable 的实现类即可。

复制代码

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "select u from User u where u.lastname = ?1")
  Page<User> findByLastname(String lastname, Pageable pageable);
}
//调用者的写法
repository.findByFirstName("jackzhang",new PageRequest(1,10));

案例 8:@Query 对原生 SQL 的分页支持,并不是特别友好,因为这种写法比较“骇客”,可能随着版本的不同会有所变化。我们以 MySQL 为例。

复制代码

 public interface UserRepository extends JpaRepository<UserInfoEntity, Integer>, JpaSpecificationExecutor<UserInfoEntity> {
   @Query(value = "select * from user_info where first_name=?1 /* #pageable# */",
         countQuery = "select count(*) from user_info where first_name=?1",
         nativeQuery = true)
   Page<UserInfoEntity> findByFirstName(String firstName, Pageable pageable);
}
//调用者的写法
return userRepository.findByFirstName("jackzhang",new PageRequest(1,10, Sort.Direction.DESC,"last_name"));
//打印出来的sql
select  *   from  user_info  where  first_name=? /* #pageable# */  order by  last_name desc limit ?, ?

这里需要注意:这个注释 /* #pageable# */ 必须有。
另外,随着版本的变化,这个方法有可能会进行优化。此外还有一种实现方法,就是自己写两个查询方法,自己手动分页。

关于 @Query 的用法,还有一个需要了解的内容,就是 @ Param 用法。

@Param 用法

@Param 注解指定方法参数的具体名称,通过绑定的参数名字指定查询条件,这样不需要关心参数的顺序。我比较推荐这种做法,因为它比较利于代码重构。如果不用 @Param 也是可以的,参数是有序的,这使得查询方法对参数位置的重构容易出错。我们看个案例。

案例 9:根据 firstname 和 lastname 参数查询 user 对象。

复制代码

public interface UserRepository extends JpaRepository<User, Long> {
  @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
  User findByLastnameOrFirstname(@Param("lastname") String lastname,
                                 @Param("firstname") String firstname);
}

案例 10: 根据参数进行查询,top 10 前面说的“query method”关键字照样有用,如下所示:

复制代码

public interface UserRepository extends JpaRepository<User, Long> {
  @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
  User findTop10ByLastnameOrFirstname(@Param("lastname") String lastname,
                                 @Param("firstname") String firstname);
}

这里说下我的经验之谈:你在通过 @Query 定义自己的查询方法时,我建议也用 Spring Data JPA 的 name query 的命名方法,这样下来风格就比较统一了。
上面我介绍了 @Query 的基本用法,下面介绍一下 @Query 在我们的实际应用中最受欢迎的两处场景。

@Query 之 Projections 应用返回指定 DTO

我们在之前的例子的基础上新增一张表 UserExtend,里面包含身份证、学号、年龄等信息,最终我们的实体变成如下模样:

复制代码

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserExtend { //用户扩展信息表
   @Id
   @GeneratedValue(strategy= GenerationType.AUTO)
   private Long id;
   private Long userId;
   private String idCard;
   private Integer ages;
   private String studentNumber;
}
@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User { //用户基本信息表
   @Id
   @GeneratedValue(strategy= GenerationType.AUTO)
   private Long id;
   private String name;
   private String email;
   @Version
   private Long version;
   private String sex;
   private String address;
}

如果我们想定义一个 DTO 对象,里面只要 name、email、idCard,这个时候我们怎么办呢?这种场景非常常见,但好多人使用的都不是最佳实践,我在这里介绍几种方式做一下对比。

我们先看一下,刚学 JPA 的时候别手别脚的写法:

复制代码

public interface UserDtoRepository extends JpaRepository<User,Long> {
   /**
    * 查询用户表里面的name、email和UserExtend表里面的idCard
    * @param id
    * @return
    */
   @Query("select u.name,u.email,e.idCard from User u,UserExtend e where u.id= e.userId and u.id=:id")
   List<Object[]> findByUserId(@Param("id") Long id);
}

我们通过下面的测试用例来取上面 findByUserId 方法返回的数据组结果值,再塞到 DTO 里面,代码如下:

复制代码

@Test
public void testQueryAnnotation() {
//新增一条用户数据  userDtoRepository.save(User.builder().name("jack").email("123456@126.com").sex("man").address("shanghai").build());
//再新增一条和用户一对一的UserExtend数据  userExtendRepository.save(UserExtend.builder().userId(1L).idCard("shengfengzhenghao").ages(18).studentNumber("xuehao001").build());
//查询我们想要的结果
   List<Object[]> userArray = userDtoRepository.findByUserId(1L);
   System.out.println(String.valueOf(userArray.get(0)[0])+String.valueOf(userArray.get(0)[1]));
   UserDto userDto = UserDto.builder().name(String.valueOf(userArray.get(0)[0])).build();
   System.out.println(userDto);
}

其实经验的丰富的“老司机”一看就知道这肯定不是最佳实践,这多麻烦呀,肯定会有更优解。那么我们再对此稍加改造,用 UserDto 接收返回结果。

利用 class UserDto 获取我们想要的结果

首先,我们新建一个 UserDto 类的内容。

复制代码

package com.example.jpa.example1;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
@AllArgsConstructor
public class UserDto {
    private String name,email,idCard;
}

其次,我们看下利用 @Query 在 Repository 里面怎么写。

复制代码

public interface UserDtoRepository extends JpaRepository<User, Long> {
   @Query("select new com.example.jpa.example1.UserDto(CONCAT(u.name,'JK123'),u.email,e.idCard) from User u,UserExtend e where u.id= e.userId and u.id=:id")
   UserDto findByUserDtoId(@Param("id") Long id);
}

我们利用 JPQL,new 了一个 UserDto;再通过构造方法,接收查询结果。其中你会发现,我们用 CONCAT 的关键字做了一个字符串拼接,这时有的同学就会问了,这种方法支持的关键字有哪些呢?

你可以查看JPQL的 Oracal 文档,也可以通过源码来看支持的关键字有哪些。

首先,我们打开 ParameterizedFunctionExpression 会发现 Hibernate 支持的关键字有这么多,都是 MySQL 数据库的查询关键字,这里就不一一解释了。

Drawing 4.png

然后,我们写一个测试方法,调用上面的方法测试一下。

复制代码

@Test
public void testQueryAnnotationDto() {
   userDtoRepository.save(User.builder().name("jack").email("123456@126.com").sex("man").address("shanghai").build());
   userExtendRepository.save(UserExtend.builder().userId(1L).idCard("shengfengzhenghao").ages(18).studentNumber("xuehao001").build());
   UserDto userDto = userDtoRepository.findByUserDtoId(1L);
   System.out.println(userDto);
}

最后,我们运行一下测试用例,结果如下。这时你会发现,我们按照预期操作得到了 UserDto 的结果。

复制代码

Hibernate: insert into user (address, email, name, sex, version, id) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into user_extend (ages, id_card, student_number, user_id, id) values (?, ?, ?, ?, ?)
Hibernate: select (user0_.name||'JK123') as col_0_0_, user0_.email as col_1_0_, userextend1_.id_card as col_2_0_ from user user0_ cross join user_extend userextend1_ where user0_.id=userextend1_.user_id and user0_.id=?
UserDto(name=jackJK123, email=123456@126.com, idCard=shengfengzhenghao)

那么还有更简单的方法吗?答案是有,下面我们利用 UserDto 接口来实现一下。

利用 UserDto 接口获得我们想要的结果

首先,新增一个 UserSimpleDto 接口来得到我们想要的 name、email、idCard 信息。

复制代码

package com.example.jpa.example1;
public interface UserSimpleDto {
   String getName();
   String getEmail();
   String getIdCard();
}

其次,在 UserDtoRepository 里面新增一个方法,返回结果是 UserSimpleDto 接口。

复制代码

public interface UserDtoRepository extends JpaRepository<User, Long> {
//利用接口DTO获得返回结果,需要注意的是每个字段需要as和接口里面的get方法名字保持一样
@Query("select CONCAT(u.name,'JK123') as name,UPPER(u.email) as email ,e.idCard as idCard from User u,UserExtend e where u.id= e.userId and u.id=:id")
UserSimpleDto findByUserSimpleDtoId(@Param("id") Long id);
}

然后,测试用例写法如下。

复制代码

@Test
public void testQueryAnnotationDto() {
   userDtoRepository.save(User.builder().name("jack").email("123456@126.com").sex("man").address("shanghai").build());
  userExtendRepository.save(UserExtend.builder().userId(1L).idCard("shengfengzhenghao").ages(18).studentNumber("xuehao001").build());
   UserSimpleDto userDto = userDtoRepository.findByUserSimpleDtoId(1L);
   System.out.println(userDto);  System.out.println(userDto.getName()+":"+userDto.getEmail()+":"+userDto.getIdCard());
}

最后,我们执行可以得到如下结果。

复制代码

org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap@373c28e5
jackJK123:123456@126.COM:shengfengzhenghao

我们发现,比起 DTO 我们不需要 new 了,并且接口只能读,那么我们返回的结果 DTO 的职责就更单一了,只用来查询。

接口的方式是我比较推荐的做法,因为它是只读的,对构造方法没有要求,返回的实际是 HashMap。

返回结果介绍完了,那么我们来看下一个最常见的问题:如何用 @Query 注解实现动态查询?

@Query 动态查询解决方法

我们看一个例子,来了解一下如何实现 @Query 的动态参数查询。

首先,新增一个 UserOnlyName 接口,只查询 User 里面的 name 和 email 字段。

复制代码

package com.example.jpa.example1;
//获得返回结果
public interface UserOnlyName {
    String getName();
    String getEmail();
}

其次,在我们的 UserDtoRepository 里面新增两个方法:一个是利用 JPQL 实现动态查询,一个是利用原始 SQL 实现动态查询。

复制代码

package com.example.jpa.example1;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface UserDtoRepository extends JpaRepository<User, Long> {
   /**
    * 利用JQPl动态查询用户信息
    * @param name
    * @param email
    * @return UserSimpleDto接口
    */
   @Query("select u.name as name,u.email as email from User u where (:name is null or u.name =:name) and (:email is null or u.email =:email)")
   UserOnlyName findByUser(@Param("name") String name,@Param("email") String email);
   /**
    * 利用原始sql动态查询用户信息
    * @param user
    * @return
    */
   @Query(value = "select u.name as name,u.email as email from user u where (:#{#user.name} is null or u.name =:#{#user.name}) and (:#{#user.email} is null or u.email =:#{#user.email})",nativeQuery = true)
   UserOnlyName findByUser(@Param("user") User user);

然后,我们新增一个测试类,测试一下上面方法的结果。

复制代码

@Test
public void testQueryDinamicDto() {
   userDtoRepository.save(User.builder().name("jack").email("123456@126.com").sex("man").address("shanghai").build());
   UserOnlyName userDto = userDtoRepository.findByUser("jack", null);
   System.out.println(userDto.getName() + ":" + userDto.getEmail());
   UserOnlyName userDto2 = userDtoRepository.findByUser(User.builder().email("123456@126.com").build());
   System.out.println(userDto2.getName() + ":" + userDto2.getEmail());
}

最后,运行结果如下。

复制代码

Hibernate: insert into user (address, email, name, sex, version, id) values (?, ?, ?, ?, ?, ?)
 : binding parameter [1] as [VARCHAR] - [shanghai]
 : binding parameter [2] as [VARCHAR] - [123456@126.com]
 : binding parameter [3] as [VARCHAR] - [jack]
 : binding parameter [4] as [VARCHAR] - [man]
 : binding parameter [5] as [BIGINT] - [0]
 : binding parameter [6] as [BIGINT] - [1]
Hibernate: select user0_.name as col_0_0_, user0_.email as col_1_0_ from user user0_ where (? is null or user0_.name=?) and (? is null or user0_.email=?)
 : binding parameter [1] as [VARCHAR] - [jack]
 : binding parameter [2] as [VARCHAR] - [jack]
 : binding parameter [3] as [VARCHAR] - [null]
 : binding parameter [4] as [VARCHAR] - [null]
jack:123456@126.com
Hibernate: select u.name as name,u.email as email from user u where (? is null or u.name =?) and (? is null or u.email =?)
 : binding parameter [1] as [VARBINARY] - [null]
 : binding parameter [2] as [VARBINARY] - [null]
 : binding parameter [3] as [VARCHAR] - [123456@126.com]
 : binding parameter [4] as [VARCHAR] - [123456@126.com]
jack:123456@126.com

注意:其中我们打印了一下 SQL 传入的参数,是为了让我们更清楚参数都传入了什么值。
上面的两个方法,我们分别采用了 JPQL 的动态参数和 SPEL 的表达式方式获取参数(这个我们在第 26 课时“SpEL 解决了哪些问题”中再详细介绍)。

通过上面的实例可以看得出来,我们采用了 :email isnullor s.email = :email 这种方式来实现动态查询的效果,实际工作中也可以演变得很复杂。所以,我们再看一个实际工作中复杂一点的例子。

通过原始 sql,根据动态条件 room 关联 room_record 来获取 room_record 的结果。

Drawing 5.png

通过 JPQL 动态参数查询 RoomRecord,如下图:

Drawing 6.png

这两个例子就是比较复杂的动态查询的案例,和上面的简单动态查询语法是一样的,你仔细看一下就会明白,并且可以轻松应用到工作中进行动态查询。

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

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

相关文章

网工笔记整理:策略工具Filter-policy的使用

一、概述 Filter-Policy&#xff08;过滤-策略&#xff09;是一个很常用的路由信息过滤工具&#xff0c;能够对接收、发布、引入的路由进行过滤&#xff0c;可应用于IS-IS、OSPF、BGP等协议。 Filter-policy在距离矢量路由协议中的应用 filter-policy import&#xff1a;不发…

Python学习基础笔记六十五——布尔值

布尔对象&#xff1a; Python中有一种对象类型称之为布尔对象&#xff08;英文叫bool&#xff09;。 布尔对象只有两种取值&#xff0c;True和False。对应的是真和假&#xff0c;或者说是和否。True对应的是&#xff0c;False对应的是否。 我觉得这句话是一个关键&#xff1a…

04 | 如何利用 Repository 中的方法返回值解决实际问题?

上一课时&#xff0c;我们着重讲了方法名和参数的使用方法&#xff0c;这一课时我们来看下Repository 支持的返回结果有哪些&#xff0c;以及 DTO 类型的返回结果如何自定义&#xff0c;及其在实际工作场景中我们如何做。通过本课时的学习&#xff0c;你将了解到 Repository 的…

Ansible概述以及模块

目录 一、Ansible概述 1、Ansible是什么 2、Ansible的作用 3、Ansible的特性 4、Ansible的工作机制 5、Ansible的特点 二、Ansible安装部署 1、管理端安装ansible 2、配置主机清单 3、配置密钥对验证 三、Absible命令行模块 1、command模块 2、shell模块 3、cron …

Jackson+Feign反序列化问题排查

概述 本文记录在使用Spring Cloud微服务开发时遇到的一个反序列化问题&#xff0c;RPC/HTTP框架使用的是Feign&#xff0c;JSON序列化反序列化工具是Jackson。 问题 测试环境的ELK告警日志如下&#xff1a; - [43f42bf7] 500 Server Error for HTTP POST "/api/open/d…

一篇文章让你了解“JWT“

一.JWT简介 1.概念 JWT (JSON Web Token) 是一种用于在网络上安全传输信息的开放标准&#xff08;RFC 7519&#xff09;。它是一种紧凑且自包含的方式&#xff0c;用于在不同组件之间传递信息&#xff0c;通常用于身份验证和授权目的。JWT 是以 JSON 格式编码的令牌&#xff…

Shell后门脚本

说明&#xff1a;请在云服务器中执行&#xff0c;执行脚本后会发生 1、创建${create_user}用户并赋予sudo权限2、获取公网IP地址和SSH端口3、将用户公网IPSSH端口信息发送给${mail_from}变量 #!/bin/bash # 收件人邮箱 mail_fromxxxxx.com sshd_port$(grep "^Port"…

AI低代码维格云甘特视图怎么用?

甘特视图,以日期为横轴展示任务持续时长和先后顺序,简称甘特图。 项目管理过程中,合理分配任务和资源至关重要,使用甘特图,妥当解决以下场景: 想知道整个项目的周期多长,哪些任务对项目的周期影响最大; 想知道每个任务的时间有多长,任务的优先级和依赖关系是什么; 想…

VUE3页面截取部署后的二级目录地址

用vue3开发了一个项目&#xff0c;只能部署在根目录&#xff0c;不能加二级目录&#xff0c;后来网上找了解决方案&#xff0c;在vite.config.ts中增加base: ./,配置解决问题&#xff0c;参考下图&#xff1a; 但部署后要获取部署的二级目录地址切遇到问题&#xff0c;后来想了…

SQL如何导入数据以及第一次上机作业

如何导入excel数据 首先得学会导入数据 使用excel格式不需要改成其它格式&#xff08;如csv&#xff0c;txt&#xff09;&#xff0c;因为你改了到时候还是会报错&#xff08;实践过使用Sum统计总数一直说我数据格式有问题&#xff09; 首先右键TSGL数据库->任务->导入数…

makefile规则查找

文章目录 对应 跟我一起写Makefile2.1**makefile** **的规则****2.2** **一个示例****2.5** **让** **make** **自动推导****2.6 makefile** **的另一种风格****2.7** **清空目录的规则****2.10** **包含其它** **Makefile****3.4** **文件搜寻****3.5** **伪目标****3.6** **多…

AWS香港Web3方案日,防御云安全实践案例受关注

9月26日&#xff0c;AWS合作伙伴之Web3解决方案日在香港举办。来自人工智能、Web3等领域的创业公司、技术专家、风险投资商&#xff0c;就元宇宙时代未来发展进行了深入交流。现场展示了顶象防御云在金融与Web3领域的安全实践案例。 Web3为互联网体系架构的一个整体演进和升级&…

LoRa技术未来发展前景:物联网和边缘计算的引领者

随着物联网和边缘计算的快速发展&#xff0c;低功耗广域网&#xff08;LoRa&#xff09;技术在连接远距离设备、实现长距离通信和满足低功耗需求方面崭露头角。本文将分析LoRa技术在未来的发展前景&#xff0c;尤其是在物联网和边缘计算领域的潜在影响。 LoRa技术的核心优势 1…

自动化产线集控系统(西门子CNC 840D/840DSL远程控制)

1.1项目背景 RQQ/VF120机组目前为1人操作3台机床&#xff0c;需在机台旁监控。为了改善人员在班中劳动强度非常大的现状&#xff0c;调整好每台机床的节奏&#xff0c;以保证机床的最少的等待时间。本项目旨在通过远程监视设备运行过程关键参数&#xff0c;操作人员人员可远程监…

清洁洗鞋商城小程序的作用是什么

人靠衣装&#xff0c;一身干净合身的衣物总是给人赏心悦目的感觉&#xff0c;人们对颜值要求越来越高&#xff0c;不仅是衣服&#xff0c;鞋也是重要的组成部分。各种品牌样式鞋&#xff0c;很多人家里往往有几十双&#xff0c;而在清洁这一块&#xff0c;没有时间、或材质特殊…

JS加密/解密之webpack打包代码逆向

Webpack 是一个强大的打包工具&#xff0c;能够将多个文件打包成一个或多个最终的文件。然而&#xff0c;将已经经过打包的代码还原回原始源代码并不是一件直接的事情&#xff0c;因为 webpack 打包的过程通常会对代码进行压缩、混淆和优化&#xff0c;丢失了部分变量名和代码结…

K8S环境搭建

K8S环境搭建 前置条件 部署3台VM&#xff0c;一台作为master,两台作为slave需要保障vm之间网络是互通的 为vm安装docker # 安装/更新 yum-utils yum install -y yum-utils#添加阿里镜像稳定版仓库 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce…

TCP/IP(六)TCP的连接管理(三)半连接

一 半连接和全连接 内容参考小林coding 说明&#xff1a; 普通中小厂不会问的这么深,这么细,但是当性能调优阶段必须掌握原理 ① 内容提纲 ② 概念 说明&#xff1a; 半连接只是一个中间状态 ② 图谱 二 半连接 ① 查看 TCP 半连接队列长度 root 用户 执行 --> …

mysql面试题45:读写分离常见方案、哪些中间件可以实现读写分离

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:说一说你知道的读写分离常见方案 读写分离是一种常见的数据库架构方案,旨在分担数据库的读写压力,提高系统的性能和可扩展性。以下是两种常见的…

图解==>渗透测试KAILI系统的基本操作(第九课)

渗透测试KAILI系统的基本操作(第九课) 渗透测试KAILI系统的安装环境(第八课)-CSDN博客 第一部分 认识KAILI操作系统的使用 以2022年版本为例 第二部分 kaili 最新版本的操作系统 的图形话页面 英文版本 第三部分 13条基础命令操作回顾 1 第一条命令 ls ls/ less cat …