我一直觉得 Spring Data Jpa 用的少,没想到最近有好几个小伙伴问 Jpa 的问题,想了想,我就把我知道的关于 Jpa 的一些知识点,整理一下写几篇文章和大家分享下,今天就先来看个简单的。
小伙伴们都知道,Spring Data Jpa 有一个“神奇”的功能,就是我们只需要按照既定的规则去定义方法名,就不用自己写 SQL 了,至于具体的实现原理,松哥在 2019 年的文章中就已经介绍过了,这里就不啰嗦了(公号后台回复 2019 有当年的文章索引)。
今天是想和大家捋一捋 Spring Data Jpa 所支持的方法前缀问题。
我们日常开发中,如果用到了 Spring Data Jpa,用的最多的就是 findXXX 了,有的人可能是用 getXXX 或者 readXXX,我觉得这几个是用的最多的几个了,其实 Spring Data Jpa 支持的方法前缀多达 11 种,我们来瞅瞅:
相关的源码在 org.springframework.data.repository.query.parser.PartTree 类中:
private static final String QUERY_PATTERN = "find|read|get|query|search|stream";
private static final String COUNT_PATTERN = "count";
private static final String EXISTS_PATTERN = "exists";
private static final String DELETE_PATTERN = "delete|remove";
接下来松哥举几个例子我们一起来捋一捋。如果小伙伴们对 Spring Data Jpa 处于从来没用过的状态,那么可以在公众号后台回复 666,松哥在之前的 Spring Boot 教程中介绍过,可以先学习下,否则学习本文可能会有点摸不着头脑。
1. 查询
先来看查询。
find/get/read/query/search 都算是常规前缀,我们来看几个例子:
public interface UserRepository extends JpaRepository<User,Integer> {
/**
* 根据用户名查询用户
* @param username
* @return
*/
User findUserByUsername(String username);
/**
* 根据用户地址查询用户
* @param address
* @return
*/
List<User> getUserByAddress(String address);
/**
* 查询某个日期之后出生的用户
* @param birthday
* @return
*/
List<User> readUserByBirthdayAfter(LocalDate birthday);
/**
* 查询某个日期之前出生的用户
* @param birthday
* @return
*/
List<User> queryUserByBirthdayBefore(LocalDate birthday);
/**
* 根据性别查询用户
* @param gender
* @return
*/
List<User> searchUserByGender(String gender);
}
每个方法的含义我都给注释上了,这里就不啰嗦了。
需要注意的是,在 IDEA 中写的时候,前面四个都有提示,最后那个 search 没有提示,没有提示小伙伴们自己写注意可别敲错了。。
除了上面这五个常规的,还有一个稍微特殊一点的,就是 stream,其实也不算特殊,毕竟 Java 的版本一年一个样,从 JDK8 到现在都快十年了,stream 早该掌握了(还不懂 stream 的小伙伴,可以在公众号后台回复 2021,有松哥去年文章索引,去年我在 WebFlux 系列中详细介绍过 stream)。
我们来看一个 stream 的例子:
public interface UserRepository extends JpaRepository<User,Integer> {
/**
* 根据地址查询用户,返回 stream 流
* @param address
* @return
*/
Stream<User> findUserByAddress(String address);
}
返回值是一个 stream 流即可。
这里有一个需要注意的地方,就是调用返回值为 stream 流的方法时,需要加事务,如果不加事务,会报如下错误:
org.springframework.dao.InvalidDataAccessApiUsageException: You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed. Make sure the code consuming the stream uses @Transactional or any other way of declaring a (read-only) transaction.
这个错误的意思很明确,就是你要把 stream 查询放到一个事务中,进而确保 stream 操作是数据库连接没有关闭。因为 stream 中并不保存数据,所以要是 Connection 关闭了,就没法操作数据了。
松哥给大家看一个简单的 stream 方法调用案例:
@Autowired
UserRepository userRepository;
@Test
@Transactional(readOnly = true)
void test01() {
List<User> list = userRepository.findUserByAddress("深圳").map(u -> {
u.setAddress("中国 " + u.getAddress());
return u;
}).collect(Collectors.toList());
System.out.println("list = " + list);
}
从数据库中查询到数据之后,给所有的 address 都加上"中国"二字。
2. 统计
count 关键字可以用来做统计,来看下面一个例子:
public interface UserRepository extends JpaRepository<User,Integer> {
/**
* 统计某个地址有多少用户
* @param address
* @return
*/
Long countByAddress(String address);
/**
* 去重统计某个地址有多少用户
* @param address
* @return
*/
Long countDistinctByAddress(String address);
}
两个统计的例子,第二个是去重之后统计。
3. 判断
exists 可以用来做判断,来看个例子:
public interface UserRepository extends JpaRepository<User,Integer> {
/**
* 判断某个地址是否存在用户
* @param address
* @return
*/
Boolean existsUserByAddress(String address);
}
4. 删除
用 delete 或者 remove 关键字可以做删除,来看两个例子:
public interface UserRepository extends JpaRepository<User,Integer> {
/**
* 根据地址删除用户
* @param address
* @return
*/
Integer deleteUserByAddress(String address);
/**
* 根据地址删除用户
* @param address
* @return
*/
Integer removeUserByAddress(String address);
}
需要注意的是,删除的方法也是要在事务中调用。
5. 小结
好啦,这就是松哥跟大家介绍的 Spring Data Jpa 中数据库操作方法默认的 10 种前缀,当然,如果这些前缀无法满足操作,那么就可以上 @Query 注解了,这是另外一个话题了,以后聊。