定义查询方法

从方法名称中可以制定特定用于存储的查询和更新

通过使用@Query注解手动定义查询

 

方法的查询策略设置

通过 @EnableJpaRepositories(queryLookupStrategy = QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND) 的 queryLookupStrategy参数设置

QueryLookupStrategy.Key的值有三个:

  • CREATE: 直接根据方法名进行创建,规则是根据方法名称的构造进行尝试,如果方法名不符合规则,启动的时候就会报异常
  • USE_DECLARED_QUERY:声明式创建,启动的时候尝试找到一个声明的查询,如果没有找到就抛出一个异常
  • CREATE_IF_NOT_FOUND: 默认值,以上两种方法的结合版

 

编写查询方法

待查询功能的方法名由查询策略(关键字)、查询字段和一些限制性条件组成

 

关键字列表(待补充)

 

查询结果处理

参数选择分页和排序

 

1.特定类型的参数,动态地将分页和排序应用于查询

// 当用到 Page 的时候会默认执行一条 count
Page<SystemUser> findByUname(String uname, Pageable pageable);
// 只知道是否有下一个 Slice 可用,不会执行count,不关心一共有多少页
Slice<SystemUser> findByUname(String uname,Pageable pageable);
// 排序支持返回List,排序也可以通过 Pageable 实例处理
List<SystemUser> findByUname(String uname, Sort sort);
// 分页支持返回List,page将不会创建构建实例所需的实例元数据
List<SystemUser> findByUname(String uname, Pageable pageable);

2.限制查询结果

SystemUser findFirstByOrderByUnameAsc();
SystemUser findTopByOrderByUnameAsc();
Page<SystemUser> queryFirst10ByUname(String uname);
Slice<SystemUser> findTop3ByUname(String uname);
List<SystemUser> findFirst10ByUname(String uname);
List<SystemUser> findTop3ByUname(String uname);

查询方法的结果可以通过关键字来限制first或top,其可以被互换地使用,可选数值可以追加到顶部/第一个以指定要返回的最大结果大小,如果被省略,则假设结果大小为1,支持Distinct关键字

 

查询结果的不同形式

1.流式查询结果

可以通过使用Java 8 Stream<T> 作为返回类型来逐步处理查询方法的结果

dao:

@Query("select u from t_user u")
Stream<SystemUser> findAllByStream();

测试代码:

@Test
// 一定要设置transaction,否则InvalidDataAccessApiUsageException
// 如果对流没有写的操作建议将readonly 设置为 true
@Transactional
public void testStream() {
    Stream<SystemUser> systemUserStream = null;
    try {
        systemUserStream = this.userRepository.findAllByStream();
        systemUserStream.forEach(System.out::println);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 使用完后关闭流
        if (systemUserStream != null){
            systemUserStream.close();
        }
    }
}

 

2.异步查询结果

使用Spring的异步方法执行功能异步的存储库查询

在调用时立即返回,并且实际的查询执行将发生在已提交给Spring TaskExecutor的任务中

比较适合定时任务的实际场景

@Async
Future<SystemUser> findByUname(String uname);
@Async
CompletableFuture<SystemUser> findOneByUname(String uname);
@Async
ListenableFuture<SystemUser> findOneByUnameAndEmail(String uname,String email);

 

Projections 对查询结果的扩展

Projections扩展支持: 指的是和DB查询结果的字段映射关系

一般情况下返回的字段和DB查询结果的字段是一一对应的,但有的时候我们需要返回一些特定的字段,不需要全部返回,或者返回一些复合型的字段,还要自己写逻辑

正常写法如下:

@Entity(name = "t_user")
@Data
public class SystemUser {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String uname;

    private String email;

    private String address;

}

 

仅仅返回实体类中的uname字段和email字段:

映射对象(entity)写法如下:

public interface NameEmailOnly {

    String getUname();

    String getEmail();
}

dao写法如下:

public interface UserRepository extends JpaRepository<SystemUser,Long> {
    Collection<NameEmailOnly> findByUname(String uname);
}

 

映射实体类支持用 Spring 中 @Value和SPEL

public interface NameEmailOnly {
    @Value("#{target.uname + ' ' + target.email}")
    String getUname();
}

 

映射实体还可以使用如下这种自定义计算:

@Component
class MyBean{
    public String getUname(SystemUser systemUser){
        // 自定义计算
        return systemUser.getUname() + "#" + systemUser.getAddress();
    }
}

public interface NameEmailOnly {
    @Value("#{@myBean.getUname(target)}")
    String getUname();
}

 

不仅仅可以使用interface 还支持Dto实体:

@Data
public class NameEmailOnlyDto {
    String uname;

    String email;

    // 必须要用全字段的构造方法,lombok中的@AllArgsConstructor
    public NameEmailOnlyDto(String uname, String email) {
        this.uname = uname;
        this.email = email;
    }
}

 

支持动态Projections

dao写法:

public interface UserRepository extends JpaRepository<SystemUser,Long> {
    <T> Collection<T> findByUname(String uname,Class<T> type);
}

测试代码:

@Test
public void testProjections(){
    Collection<SystemUser> systemUsers = this.userRepository.findByUname("zhangsan",SystemUser.class);
    System.out.println(systemUsers);
}

 

posted @ 2020-11-18 14:44  半雨微凉  阅读(272)  评论(0)    收藏  举报