我是靠谱客的博主 感动航空,最近开发中收集的这篇文章主要介绍07 Spring Data JPA查询操作2Spring Data JPA查询操作2,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Spring Data JPA查询操作2

注解查询时我们在查询操作中很少用到查询操作,因为Spring Data JPA基本自带的已经帮我们完成了很多复杂的查询,但是在开发中又会遇到各种意料之外的操作,所以这里介绍注解操作,扩展性非常强大(更加支持原生的SQL语法)。

@Query注解

@Query源码及解释

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.QueryAnnotation;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@QueryAnnotation
@Documented
public @interface Query {
    /*
        指定JPQL的查询语句。
            当nativeQuery=true时,此处的value可以替换成原生sql
     */
    String value() default "";

    /*
        指定count的JPQL语句,如果不指定将根据query自动生成
     */
    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用法

使用命名查询方法为实体声明查询是一种有效的方法,对于少量查询很有效。一般只需要关心@Query里面的value和nativeQuery的值。使用声明式JPQL查询有一个好处,就是启动的时候可以知道语法正确与否。

示例1:声明一个注解在Repository的查询方法上

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

/**
 * TODO
 *
 * @author: Yizq
 * @data: 2020/8/23 4:40 下午
 */

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

示例2:Like查询,注意firstName不会自动加上%关键字的

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

示例3:直接使用原生SQL

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

navtiveQuery不支持直接Sort的参数查询

示例4:使用原生sql排序

public interface UserRepository extends JpaRepository<User,Long> {
    @Query(value = "select * from user where last_name = ?1 order by ?2", nativeQuery = true)
    Collection<UsersOnly> chaxun(String lastName,String sort);
    
    //错误写法:Collection<UsersOnly> queryLastName(String lastName,Sort sort);

}

@Query排序

@Query在JPQL下想实现排序,直接永PageRequest或者直接用Sort参数都可以。

在排序实例中实际使用的属性需要与实体建模里面的字段相匹配,这意味着他们需要解析为查询中使用的属性或别名。

示例1: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);
}

//调用方的写法,如下
userRepository.findByAndSort("zhangsan", Sort.by("firstname"));
userRepository.findByAndSort("zhangsan", Sort.by("length(firstname)"));
userRepository.findByAndSort("zhangsan", JpaSort.unsafe("length(firstname)"));
userRepository.findByAsArrayAndSort("zhangsan", Sort.by("fn_len"));

@Query分页

示例1:直接使用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);
    
    
}
//调用方的写法,如下
userRepository.findByLastName("zhangsan", PageRequest.of(1, 10));

@Param用法

默认清空下,参数是通过顺序绑定在查询语句上的。这使得查询方法对参数为止的重构容易出错。为了解决这个问题,可以使用@Param注解指定方法参数的具体名称,通过绑定参数名字做查询条件。

示例1:根据参数进行查询

public interface UserRepository extends JpaRepository<User,Long> {

    @Query("select u from User u where u.firstName = :firstname or u.lastName = :lastname")
    User findByLastNameOrFirstName(@Param("firstName") String firstname, @Param("lastname") String lastname);

}

SpEL表达式的支持

在Spring Data JPA1.4+,支持@Query中使用SpEL表达式来接收变量。

SpEL支持的变量如下:

变量名使用方式描述
entityNameselect x from #{#entityName} x根据指定的Repository自动查询相关的entityName。
有两种方式能被解析出来
(1)如果定义了@Entity的注解,直接用其属性名
(2)如果没定义,直接用实体类的名称

示例:

定义一个User实体类,并使用@Entity注解

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String lastName;

    private String firstName;

    private String email;

    private String age;

    private String gender;
}

定义一个Repository

public interface UserRepository extends JpaRepository<User> {
    @Query("select t from #{#entityName} t where t.lastName = ?1")
    List<User> findAllByLastName(String lastName);
}

可以将以上方法抽出作为公用

import javax.persistence.MappedSuperclass;

@MappedSuperclass
public abstract class AbstractMappedType {
 String attribute;
}
@NoRepositoryBean
public interface MappedTypeRepository<T extends AbstractMappedType> extends Repository<T,Long> {

 @Query("select t from #{#entityName} t where t.attribute = ?1")
 List<T> findAllByAttribute(String attribute);

}
@Entity
public class User extends AbstractMappedType{
...
}

ps:使用MappedTypeRepository作为一个公用的父类,自己的Repository可以继承他

@Modifying修改查询

源码:

public @interface Modifying {
		// 如果配置了一级缓存,这个时候用clearAutomatically=true,就会刷新hibernate的一级缓存,不然你在同一个接口中更新一个对象,查出来的对象就是没有更新之前的状态
    boolean clearAutomatically() default false;
}

可以通过在@Modifying注解实现只需要参数绑定的update查询的执行

public interface UserRepository extends JpaRepository<User,Long> {
    @Modifying
    @Query("update User u set u.firstName = ?1 where u.lastName = ?2")
    int setFiexedFirstnameFor(String firstName, String lastName);
}

简单针对某个特定属性的更新也可以用基类里面的通过save。还有第三种方法就是自己定义Repository,使用EntityManager来进行更新操作。

@QueryHints

有很多的数据都支持Hint Query的用法,不过这种查询比较老旧,会在后面慢慢被淘汰。

@Procedure存储过程的查询方法

@Procedure源码

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Procedure {
		// 数据库里面存储过程的名称
    String value() default "";
		// 数据库里面存储过程的名称
    String procedureName() default "";
		// 在EntityManager中的名字,NamedStoredProcedureQuery使用
    String name() default "";
		// 输出参数的名字
    String outputParameterName() default "";
}

示例:

1、创建一个存储过程名称为plus1inout,有两个参数、两个结果

CREATE PROCEDURE plus1inout(IN arg int,OUT res int)
BEGIN
SELECT (arg+10) into res;
END

2、使用@NamedStoredProcedureQuery注解来调用存储过程。这个必须定义在一个实体上面。

@Entity
@NamedStoredProcedureQuery(
        name = "User.plus1",
        procedureName = "plus1inout",
        parameters = {
                @StoredProcedureParameter(
                        mode = ParameterMode.IN,
                        name = "arg",
                        type = Integer.class
                ),
                @StoredProcedureParameter(
                        mode = ParameterMode.OUT,
                        name = "res",
                        type = Integer.class
                )
        }
)
public class User{

关键点:

  • 存储过程使用注释@NamedStoredProcedureQuery,并且绑定到一个JPA表
  • procedureName是存储过程的名称
  • name是JPA中存储过程的名字
  • 使用注释@StoredProcedureParameter来定义存储过程使用的IN/OUT参数

3、调用存储过程

public interface UserRepository1 extends JpaRepository<User,Long> {
    @Procedure("plus1inout")
    Integer explicitlyNamePlus1intout(Integer arg);
    
    @Procedure(procedureName = "plus1intout")
    Integer plus1inout(Integer arg);

    @Procedure(name = "User.plus1IO")
    Integer entityAnnotatedCustomNameProcedurePlus1IO(@Param("args") Integer args);
}

关键点:

  • @Procedure的procedureName参数必须匹配@NamedStoredProcedureQuery的procedureName
  • @Procedure的name参数必须匹配@NamedStoredProcedureQuery的name
    • @Param必须匹配@StoredProcedureParameter注释的name参数

@NamedQueries预定义查询

预定义查询是一种查询的形式

1、在@Entity下添加@NamedQuery定义

@Repeatable(NamedQueries.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NamedQuery {
		// query的名称,规则:实体.方法名
    String name();
		// 具体的JPQL查询语法
    String query();

    LockModeType lockMode() default LockModeType.NONE;

    QueryHint[] hints() default {};
}

query里面的值只能是JPQL。查询参数也要和实体对应起来。因为实际场景中这种破坏Entity的侵入式很不美观,也不方便。在工作尽量减少使用

2、与之对应的还有@NamedNativeQuery,用法一样,与之不同的是query里面支持原生SQL,而非字段对应

示例:

实体里面的写法

@Entity
@NamedQuery(
        name = "User.findByFirstName",
        query="select c from User c where c.firstName = ?1"
)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String lastName;

    private String firstName;

    private String email;

    private String age;

    private String gender;
}

UserRepository的写法

User findByFirstName(String firstName);

调用者的写法

User user = rep.findByFirstName("zhangsan")

优先级

  • SpringJPA里面的比较:@Query>@NameQuery>方法定义查询

最后

以上就是感动航空为你收集整理的07 Spring Data JPA查询操作2Spring Data JPA查询操作2的全部内容,希望文章能够帮你解决07 Spring Data JPA查询操作2Spring Data JPA查询操作2所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(67)

评论列表共有 0 条评论

立即
投稿
返回
顶部