Spring Data JPA的实现原理是采用动态代理
机制,所以将介绍两种查询方式
想要以上操作,只需要实体Repository继承Spring Data Common里面的Repository接口即可。
通过@EnableJpaRepositories(queryLookupStrategy = QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND)
可以配置方法的查询策略,其中QueryLookupStrategy.Key的值一共有三个
除非有特殊需求,一般情况下不用管。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.repository.query.QueryLookupStrategy;
@EnableJpaRepositories(queryLookupStrategy = QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND)
@SpringBootApplication
public class Springdatajpa01Application {
public static void main(String[] args) {
SpringApplication.run(Springdatajpa01Application.class, args);
}
}
内部基础架构中有个根据方法名的查询生成器机制,对于在存储库的实体上创建约束查询很有用。该机制方法的前缀有find...By、read...By、query...By、count...By和get...By,从这些方法可以分析它的其余部分。引入子句可以包含其他表达式。用一句话概述:待查询功能的方法名由查询策略、查询字段和一些限制条件组成。在如下例子中,可以直接在controller里面调用
import org.springframework.data.repository.Repository;
import java.util.List;
/**
* TODO
*
* @author: Yizq
* @data: 2020/8/23 6:47 下午
*/
public interface PersonRepository extends Repository<User,Long> {
/**
*
* @description: and的查询关系
* @param email 邮箱
* @param lastName 姓氏
* @return: {@link List< User>}
* @author: Yizq
* @data: 2020/8/23 6:57 下午
*/
List<User> findByEmailAndLastName(String email, String lastName);
/**
*
* @description: 包含distinct去重、or的SQL语法
* @param email 邮箱
* @param lastName 姓氏
* @return: {@link List< User>}
* @author: Yizq
* @data: 2020/8/23 6:57 下午
*/
List<User> findDistinctPeopleByEmailOrLastName(String email, String lastName);
List<User> findPeopleDistinctByEmailOrLastName(String email, String lastName);
/**
*
* @description: 根据LastName字段查询忽略大小写
* @param lastName 姓氏
* @return: {@link List< User>}
* @author: Yizq
* @data: 2020/8/23 6:58 下午
*/
List<User> findByLastNameIgnoreCase(String lastName);
/**
*
* @description: 根据LastName和firstName查询equa并且忽略大小写
* @param lastName 姓氏
* @param firstName 名称
* @return: {@link List< User>}
* @author: Yizq
* @data: 2020/8/23 6:59 下午
*/
List<User> findByLastNameAndFirstNameAllIgnoreCase(String lastName, String firstName);
/**
*
* @description: 对查询结果根据lastName排序
* @param lastName 姓氏
* @return: {@link List< User>}
* @author: Yizq
* @data: 2020/8/23 7:00 下午
*/
List<User> findByLastNameOrderByFirstNameAsc(String lastName);
}
关键字 | 样品方法 | SQPL片段 | 原生SQL |
---|---|---|---|
And | findPersonByLastNameAndFirstName(String lastName,String firstName) | ...where x.lastName = ?1 and x.firstName = ?2 | ...where lastName = 1? and firstName = 2? |
Or | findPersonByLastnameOrFirstname(String lastName,String firstName) | ...where x.lastName= ?1 or x.firstName = ?2 | ...where lastName = ?1 or firstName = ?2 |
Is、Equals | findPersonByLastName(String lastName) findPersonByLastNameIs(String lastName) findPersonByLastNameEquals(String lastName) | ...where x.lastName= ?1 | ...where lastName = ?1 |
Between | findPersonByAge(int minAge, int maxAge) | ...where x.age between ?1 and ?2 | ...where age between ?1 and ?2 |
LessThan | findPersonByAgeLessThan(int age) | ...where x.age < ?1 | ...where age < ?1 |
LessThanEqual | findPersonByAgeLessThanEqual(int age) | ...where x.age <= ?1 | ...where age <= ?1 |
GreaterThan | findPersonByAgeGreaterThanEqual(int age) | ...where x.age > ?1 | ...where age > ?1 |
GreaterThanEqual | findPersonByAgeGreaterThanEqual(int age) | ...where x.age >= ?1 | ...where age >= ?1 |
After | findPersonByStartDateAfter(Date startDate) | ...where x.startDate > ?1 | ...where startDate > ?1 |
Before | findPersonByStartDateBefore(Date startDate) | ...where x.startDate < ?1 | ...where startDate < ?1 |
isNull、Null | findPersonByLastNameIsNull/findPersonByLastNameNull | ...where x.lastName is null | ...where lastName is null |
isNotNull、NotNull | findPersonByLastNameisNotNull/findPersonByLastNameNotNull | ...where x.lastName is not null | ...where lastName is not null |
Like | findPersonByLastNameLike(String lastName) | ...where x.lastName like ‘%‘ + ?1 + ‘%‘ | ...where lastName like ?1 |
NotLike | findPersonByLastNameNotLike(String lastName) | ...where x.lastName not like ?1 | ...where lastName not like ?1 |
StartingWith | findPersonByLastNameStartingWith(String lastName) | ...where x.lastName like ‘%‘ + ?1 | ...where lastName like ‘%‘ + ?1 |
EndingWith | findPersonByLastNameEndingWith(String lastName) | ...where x.lastName like ?1 + ‘%‘ | ...where lastName like ?1 + ‘%‘ |
Containing | findPersonByLastNameContaiting(String lastName) | ...where x.lastName like ‘%‘ + ?1 + ‘%‘ | ...where lastName like ‘%‘ + ?1 + ‘%‘ |
OrderBy | findPersonByOrderByLastNameDesc/Asc | ...order by x.lastName desc | ...order by lastName desc |
Not | findPersonByLastNameNot(String lastName) | ...where x.lastName <> ?1 | ...where lastName <> ?1 |
In | findPersonByAgeIn(List |
...where x.lastName in(?1) | ...where lastName in(?1) |
NotIn | findPersonByAgeNotIn(List |
...where x.lastName not in(?1) | ...where lastName not in(?1) |
True | findPersonByFlagTrue(Boolean flag) | ...where x.flag = true | ...where flag = true |
False | findPersonByFlagFalse(Boolean flag) | ...where x.flag = false | ...where flag = false |
IgnoreCase | findPersonByLaseNameIgnoreCase(String lastName) | ...where upper(x.lastName) = upper(?1) | ...where upper(lastName) = upper(?1) |
属性表达式只能引用托管(泛型)实体的直接属性。如假设Person实体对象中有一个Address属性里面包含一个ZipCode属性。
在这种情况下,方法名为:
List<Person> findByAddressZipCode(ZipCode zipCode);
创建及其查找的过程是:解析算法首先将整个part解析为属性,并使用该名称检查对象中的属性。如果算法成功,就使用该属性。如果不是,就拆分右侧驼峰部分的信号源到头部和尾部,并试图找出相应的属性,在我们的例子中是AddressZip和Code。如果算法找到一个具有头部的属性,那么它需要尾部,并从那里继续构建树,然后按照刚刚描述的方式将尾部分割。如果第一个分割不匹配,就将分割点移动到左侧(Address、ZipCode),然后继续。
虽然在这种大多数情况下应该起作用,但是算法可能会选择错误的属性。假设Person类也有一个addressZip属性,该算法将在第一个分割轮中匹配,并且基本上会选择错误的属性,最后失败(因为该类姓addressZip可能没有code属性)
要解决这个歧义,可以在方法名称中手动定义遍历点,所以我们的方法名称最终会是:
List<Person> findByAddress_ZipCode(ZipCode zipCode);
当然Spring JPA里面将下划线视为保留字符,但是强烈建议遵循标准Java命令规范雨夜定
1、特定类型的参数,动态地将分页和排序应用到查询
示例:在查询方法中使用Pageable、Silce和Sort
Page<User> findByLastName(String lastName,Pageable pageable);
Silce<User> findByLastName(String lastName,Pageable pageable);
List<User> findByLastName(String lastName,Sort sort);
List<User> findByLastName(String lastName,Pageable pageable);
第一个方式允许将org.springframework.data.domain.Pageable实例传递给查询方法,以便动态的将分页添加到静态定义的查询中。Page知道可用的元素和页面的总数。它通过基础框架里面触发计数来计算总数。这种是昂贵的,当用到Pageable的时候会默认执行一条count语句。而Slice的作用是,知道是否有下一个Silce可用,不会执行count,所以当查询较大的结果集时,只知道数据是否足够就可以啦,而且相关的业务场景也不用关心一共又多少页。
排序选项也通过Pageable实例处理。如果只需要排序,那么在org.springframework.data.domain.Sort参数中添加一个参数即可
2、限制查询结果
示例:在查询方法上加限制查询结果的关键字first和top
User findFirstByOrOrderByLastNameAsc();
User findTopByOrOrderByAgeDesc();
Page<User> queryFirst10ByLastName(String lastName, Pageable pageable);
Slice<User> findTop3ByLastName(String lastName, Pageable pageable);
List<User> findFirst10ByLastName(String lastName, Sort sort);
List<User> findTop10ByLastName(String lastName, Sort sort);
查询方法的结果可以通过关键字来限制first或top,其可以被互换地使用。可选的数值可以追到加顶部/第一个以指定要返回的最大结果大小。如果数字被忽略,假设结果大小为1。限制表达式也支持distinct关键字。此外,对于结果集限制为一个实体的查询,支持将结果包装到一个实例中的Optional中。如果将分页或切片应用于限制查询分页,则在限制结果中应用。
3、查询结果的不同形式
3.1、流式查询结果
可以通过使用Java 8 stream<T>
作为返回类型来逐步处理查询方式的结果,而不是简单地将结果包装在Stream数据存储中,特定的方式用于执行流
示例:使用Java8 流式传输查询的结果Stream<T>
@Query("select u from User u")
Stream<User> findAllByCustomerQueryAndStream();
Stream<User> readAllByFirstNameNotNull();
@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);
流的关闭问题,try cache是一种关闭方法。
Stream<User> stream; try { stream = userRepository.findAllByCustomerQueryAndStream(); stream.forEach(...); } catch (Exception e) { e.printStackTrace(); }finally { if (stream != null) { stream.close(); } }
3.2 异步查询结果
可以使用Spring的异步方法执行功能异步的存储库查询。这意味着方法将在调用时立即返回,并且实际的查询执行将发生在已提交给Spring TaskExceutor的任务中,比较适合定时任务的实际场景。
@Async
Future<User> findByFirstName(String firstName);
@Async
CompletableFuture<User> findOneByFirstName(String firstName);
@Async
ListenableFuture<User> findOneLastName(String lastName);
Spring JPA对Projections扩展的支持是非常好的。从字面意义上理解就是映射,指的是和DB查询结果的字段映射关系。一般情况下,返回的字段和DB查询结果的字段是一一对应的,但有的时候,我们需要返回一些指定的字段,不需要全部返回,或者只返回一些复合型的字段,还要自己写逻辑。Spring Data正是考虑到这一点,允许对专用返回类型进行建模,以便我们有更多的选择,将部分字段显示成试图对象。
假设Perso是一个正常的实体,和数据表Person一一对应,正常的写法如下:
@Entity
public class Person {
@Id
UUID id;
String firstName,lastName;
Address address;
@Entity
static class Address{
String zipCode,city,Steet;
}
}
1、我们想要返回其中与name相关的字段,应该怎么做呢?基于projections的思路。首先需要声明一个接口,包含其中的属性的方法即可,例如
public interface NamesOnly{
String getFirstName;
String getLastName;
}
Repository里面的写法如下,直接用这个对象接收结果即可
public interface PersonRepository extends Repository<Person, UUID> {
Collection<NamesOnly> findByLastName(String lastName);
// 对应sql
// select user0_.first_name as col_0_0_, user0_.last_name as col_1_0_ from user user0_ where user0_.last_name=?
}
2、查询关联的子对象
interface PersonSummary {
String getfirstName;
String getLastName;
AddressSummary getAddress();
interface AddressSummary{
String getCity();
}
}
3、@Value和SPEL也支持
interface NamesOnly{
@Value("#{target.firstName + ‘ ‘+target.lastName}")
String getFullName();
}
//select user0_.id as id1_1_, user0_.age as age2_1_, user0_.email as email3_1_, user0_.first_name as first_na4_1_, user0_.gender as gender5_1_, user0_.last_name as last_nam6_1_ from user user0_ where user0_.last_name=?
这里的getFullName不是实体中的属性
4、对Spel表达式的支持远不止这些
@Component
public class MyBean {
public static String getFullName(User user) {
// 可以进行运算、值改变等操作
return "张三";
}
}
ps:方法必须是public修饰的,并且是静态
public interface UsersOnly {
@Value("#{@myBean.getFullName(target)}")
String getFullName();
}
//select user0_.id as id1_1_, user0_.age as age2_1_, user0_.email as email3_1_, user0_.first_name as first_na4_1_, user0_.gender as gender5_1_, user0_.last_name as last_nam6_1_ from user user0_ where user0_.last_name=?
target所指向的是在Repository里对应的对象泛型
5、还可以通过Spel表达式取到方法里面的参数值
public interface UsersOnly {
@Value("#{args[0] +‘ ‘ + target.firstName +‘!‘}")
String getFullName();
}
6、以上方法都是支持interface,还可以支持Dto
public class UserDto {
private final String firstName;
private final String lastName;
public UserDto(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
//Hibernate: select user0_.first_name as col_0_0_, user0_.last_name as col_1_0_ from user user0_ where user0_.last_name=?
ps:直接在Dto中使用get/set属性即可
7、支持动态projections。通过泛型,可以根据不同的业务清空返回不同的字段集合。可以对UserRepository做一些变化,例如:
public interface UserRepository<T> extends JpaRepository<User,Long> {
<T> Collection<T> findByLastName(String lastName,Class<T> type);
}
当一个Repository上的查询方法被调用时,Advice拦截器会在方法真正地实现调用钱限制性MethodIntercept的invoke方法,这样我们可以有机会在真正方法实现执行前执行其他方法
原文:https://www.cnblogs.com/xianbeier/p/13688413.html