Spring Data套装基础之Repositories

1. 简介

Spring Data repository的目标是显著减少各种持久化存储在数据访问层的模板代码量。

简化一点的人话就是:减少数据库读写相关的代码量。

关于这一点Spring Data repository的确做到了,很多简单的操作甚至可以不用写实现代码,甚至不需要写SQL。

Spring Data套装中有很多工具,基本都是为了数据访问层需要。

  1. Spring Data JDBC
  2. Spring Data JPA
  3. Spring Data LDAP
  4. Spring Data MongoDB
  5. Spring Data Redis
  6. Spring Data R2DBC
  7. Spring Data REST
  8. Spring Data for Apache Cassandra
  9. Spring Data for Apache Geode
  10. Spring Data for Apache Solr
  11. Spring Data for Pivotal GemFire
  12. Spring Data Couchbase
  13. Spring Data Elasticsearch
  14. Spring Data Envers
  15. Spring Data Neo4j
  16. Spring Data JDBC Extensions
  17. Spring for Apache Hadoop

这篇文章主要介绍一下核心的通用的概念相关的东西。

2. 重要接口

Repository接口:

  1. Repository:只要名字按规范,接口就可以直接使用,不需要写SQL
  2. CrudRepository:继承了Repository,添加了增删改查相关功能
  3. PagingAndSortingRepository:继承了CrudRepository,添加了了排序分页相关

3. CrudRepository

CrudRepository中定义了一些常见的增删改查接口。

public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity);
Optional<T> findById(ID primaryKey);
Iterable<T> findAll();
long count();
void delete(T entity);
boolean existsById(ID primaryKey);
}

Repository接口有2个类型参数:

  1. T当前需要映射实体

  2. ID当前映射的实体中的主键类型

  3. save方法保存对象

  4. findById根据id获取对象

  5. findAll查询所有对象

  6. 返回索引对象数量

  7. 删除指定对象

  8. 检查指定id对象是否存在

如果Spring Data repository只是提供了一些接口,那它就没什么值得好说。

重要的是它为我们提供了不写代码,或者少写代码就能实现数据存储访问的方式。

下面,我们通过一个简洁的小例子来说明怎样简单使用。

4. 实例

假设我们使用SpringBoot、MySQL、Spring Data JPA。

4.1 依赖

首先引入JPA依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

4.2 配置

@Configuration
@EnableJpaRepositories(basePackages = {"vip.mycollege.mysql.jpa.repository"})
public class SpringDataJPAConfig {
}

这个配置主要是告诉Spring Repository接口在那些包下面。

Spring会去扫描这些包,找到继承了Repository的接口,为它们生成代理类。

application.properties文件配置数据库:

spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=tim
spring.datasource.password=123456

4.3 实体类

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "user")
public class User {

@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)//主键生成策略
@Column(name="id")//数据库字段名
private Integer id;

@Column(name = "username", unique = true, nullable = false, length = 30)
private String username;

@Column(name="age")
private Integer age;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", age=" + age +
'}';
}
}

主要是几个注解:

  1. @Entity:说明这是一个实体类
  2. @Table(name = “user”):用于生成表和实体类对应,name是表名
  3. @Id:说明这个字段是主键
  4. @GeneratedValue(strategy= GenerationType.IDENTITY):自动生成主键策略
  5. @Column(name=“id”):数据库字段名

4.4 接口类

import org.springframework.data.repository.CrudRepository;
import vip.mycollege.mysql.jpa.entity.User;

public interface UserCrudRepository extends CrudRepository<User,Integer> {
}

如果只是简单的增删改查,就这样就可以了,什么方法都不用添加,也不需要自己去实现接口

4.5 使用接口

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import vip.mycollege.mysql.jpa.entity.User;

import javax.annotation.Resource;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserCrudRepositoryTest {

@Resource
private UserCrudRepository userCrudRepository;

@Test
public void save(){
User user = new User();
user.setAge(20);
user.setUsername("allen");
userCrudRepository.save(user);
}

}

不需要创建表,直接就可以使用。

除了save方法,CrudRepository中其他的方法也都可以直接使用,不用再去写实现类SQL。

5. PagingAndSortingRepository

如果要分页排序怎么办?

Spring Data Repository也考虑到了,实现PagingAndSortingRepository接口就可以了。

PagingAndSortingRepository继承了CrudRepository。

public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}

还是只需要继承接口就可以了。

public interface UserPagingAndSortingRepository extends PagingAndSortingRepository<User,Integer> {
}

然后,就可以直接使用了:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit4.SpringRunner;
import vip.mycollege.mysql.jpa.entity.User;

import javax.annotation.Resource;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserPagingAndSortingRepositoryTest {

@Resource
private UserPagingAndSortingRepository userPagingAndSortingRepository;

@Test
public void testPagingAndSortingRepositorySort() {
Sort sort = Sort.by(Sort.Direction.DESC, "id");
List<User> list = (List<User>) userPagingAndSortingRepository.findAll(sort);
for (User users : list) {
System.out.println(users);
}
}

@Test
public void testPagingAndSortingRepositoryPaging() {
Pageable pageable = PageRequest.of(1, 2);
Page<User> page = userPagingAndSortingRepository.findAll(pageable);
System.out.println("数据的总条数:" + page.getTotalElements());
System.out.println("总页数:" + page.getTotalPages());
List<User> list = page.getContent();
for (User users : list) {
System.out.println(users);
}
}

@Test
public void testPagingAndSortingRepositorySortAndPaging() {
Sort sort = Sort.by(Sort.Direction.DESC, "id");
Pageable pageable = PageRequest.of(0, 10, sort);
Page<User> page = userPagingAndSortingRepository.findAll(pageable);
System.out.println("数据的总条数:" + page.getTotalElements());
System.out.println("总页数:" + page.getTotalPages());
List<User> list = page.getContent();
for (User users : list) {
System.out.println(users);
}
}
}

6. 自定义Repository

如果觉得CrudRepository方法比较多,不想暴露那么多接口,那可以直接继承Repository。

@NoRepositoryBean
interface BaseRepository<T, ID> extends Repository<T, ID> {
Optional<T> findById(ID id);
<S extends T> S save(S entity);
}

interface UserRepository extends BaseRepository<User, Long> {
User findByUsername(String username);
}

@NoRepositoryBean注解告诉Spring这个接口不用创建代理对象实例,主要是用来继承的。

7. 查询方法(通过方法名解析)

除了已经定好的接口方法,Spring Data Repository还可以通过解析方法名来执行查询,如:

public interface UserRepository extends Repository<User,Integer> {
List<User> findByUsernameAndAge(String name,Integer age);
}

然后,也不需要实现方法就可以直接使用:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import vip.mycollege.mysql.jpa.entity.User;

import javax.annotation.Resource;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UsersRepositoryTest {

@Resource
private UserRepository userRepository;

@Test
public void findByUsernameAndAge(){
List<User> users = userRepository.findByUsernameAndAge("tim", 20);
users.forEach(System.out::println);
}
}

8. 分页排序

//    Page<User> findByAge(Integer age,Pageable pageable);

Slice<User> findByAge(Integer age, Pageable pageable);

List<User> findByAge(Integer age,Sort sort);
@Test
public void findAgeSlice(){
Pageable pageable = PageRequest.of(0,3);
while(true){
Slice<User> slice = userRepository.findByAge(20,pageable);
List<User> users = slice.getContent();
users.forEach(System.out::println);
if (!slice.hasNext()){
break;
}
pageable = slice.nextPageable();
}
}

//    @Test
//    public void findAgePage(){
//        Pageable pageable = PageRequest.of(1, 3);
//        Page<User> page = userRepository.findByAge(20,pageable);
//        List<User> users = page.getContent();
//        users.forEach(System.out::println);
//    }

@Test
public void findSort(){
Sort sort = Sort.by(Sort.Direction.DESC, "username");
List<User> users = userRepository.findByAge(20,sort);
users.forEach(System.out::println);
}

9. 异步

Spring Data Repository还支持异步查询:

@Async
Future<User> findByUsername(String username);

@Async
CompletableFuture<User> findOneByUsername(String username);

@Async
ListenableFuture<User> findOneByAge(Integer age);

然后,可以这样调用:

@Test
public void findByUsername() throws ExecutionException, InterruptedException {
Future<User> tim = userRepository.findByUsername("tim");
System.out.println(tim.get());
}

10. top、first

List<User> findFirst10ByUsername(String username, Sort sort);
List<User> findTop10ByUsername(String username);

11. 删除计数

interface UserRepository extends CrudRepository<User, Long> {
long countByUsername(String username);
long deleteByUsername(String username);
List<User> removeByUsername(String username);
}

12. Stream

Spring Data Repository可以返回Stream。

Stream<User> readAllByAgeAfter(Integer age);
@Test
@Transactional(readOnly = true)
public void readAllByAgeAfter() {
try (Stream<User> userStream = userRepository.readAllByAgeAfter(20)) {
userStream.forEach(System.out::println);
}
}

13. 查询方法解析

我们已经知道Spring Data Repository非常强大的功能就是根据方法名解析查询,不用自己写SQL或者查询逻辑之类,只需要Repository中的方法名遵循相关规范就可以。

findBy + 属性名 + 关键字(And、Or…) + 属性名 + 关键字(And、Or…)

Spring Data Jpa会对方法进行解析,会先把方法中find…By、read…By、query…By、count…By、get…By等去掉,并且根据剩下的关键字and、or等解析生成SQL。

感兴趣的朋友可以看一下:PartTree、Part类

关键字方法名称示例查询提示
IsfindByFirstname,findByFirstnameIs=
OrfindByLastnameOrFirstnameor
InfindByIdIn(Collection ids)in
AndfindByLastnameAndFirstnameand
NotfindByLastnameNotnot
AscfindByLastnameAscasc
TopfindTop10ByUsernametop
TruefindByActiveTruetrue
LikefindByFirstnameLikelike
NotInfindByIdNotIn(Collection ids)not in
FalsefindByActiveFalsefalse
AfterfindByStartDateAfter>
BeforefindByStartDateBefore<
EqualsfindByFirstname,findByFirstnameEquals=
IsNullfindByAgeIsNullis null
NotNullfindByAgeNotNullis not null
BetweenfindByStartDateBetweenbetween and
NotLikefindByFirstnameNotLikelike
OrderByfindByAgeOrderByLastnameDescorder by
LessThanfindByAgeLessThan<=
DistinctfindByUsernameDistinctdistinct
IsNotNullfindByAgeIsNotNullis not null
EndingWithfindByFirstnameEndingWithlike
ContainingfindByFirstnameContaininglike
IgnoreCasefindByFirstnameIgnoreCase=
GreaterThanfindByAgeGreaterThan>
StartingWithfindByFirstnameStartingWithlike
LessThanEqualfindByAgeLessThanEqual<=
GreaterThanEqualfindByAgeGreaterThanEqual>=

14. @Query

@Query可以直接指定SQL语句,只需要将nativeQuery设置为true

@Query(value = "select * from user where username like ?1",nativeQuery = true)
List<User> queryByUsername(String username);

在SQL语句中使用?1,?2…?n这样的占位符和方法参数对应。

默认,@Query指定的是JPQL:

@Query("select u from User u where u.username like ?1%")
Page<User> findByUsernameLikePageable(String username, Pageable pageable);
@Test
public void findByUsernameLikePageable(){
PageRequest pageRequest = PageRequest.of(0, 5);
Page<User> page = userJpaRepository.findByUsernameLikePageable("t", pageRequest);
System.out.println(page.getTotalPages());
page.getContent().forEach(System.out::println);
}

注意:在JPQL查询使用的是类名User而不是表名user,使用的是别名而不是字段,如果写成:

@Query(“select id,name from User u where u.username like ?1%”)

查出来的就是Object对象。

15. 文档资料

Spring Data Repositories
查询方法解析

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值