概述
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支持的变量如下:
变量名 | 使用方式 | 描述 |
---|---|---|
entityName | select 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所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复