1. Overview
1.概述
In this short tutorial, we’ll discuss how to create a read-only Spring Data Repository.
在这个简短的教程中,我们将讨论如何创建一个只读的Spring Data Repository.。
It’s sometimes necessary to read data out of a database without having to modify it. In this case, having a read-only Repository interface would be perfect.
有时有必要从数据库中读出数据而不必修改它。在这种情况下,拥有一个只读的Repository接口将是完美的。
It’ll provide the ability to read data without the risk of anyone changing it.
它将提供读取数据的能力,而没有任何人改变数据的风险。
2. Extending Repository
2.扩展Repository
Let’s begin with a Spring Boot project that includes the spring-boot-starter-data-jpa dependency:
让我们从一个包含spring-boot-starter-data-jpa依赖项的Spring Boot项目开始。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.4.3</version>
</dependency>
Included in this dependency is Spring Data’s popular CrudRepository interface, which comes with methods for all the basic CRUD operations (create, read, update, delete) that most applications need. However, it includes several methods that modify data, and we need a repository that only has the ability to read data.
这个依赖关系包括Spring Data流行的CrudRepository接口,它带有大多数应用程序需要的所有基本CRUD操作(创建、读取、更新、删除)的方法。然而,它包括几个修改数据的方法,而我们需要一个只具有读取数据能力的仓库。
CrudRepository actually extends another interface called Repository. We can also extend this interface to fit our needs.
CrudRepository实际上扩展了另一个名为Repository的接口。我们也可以扩展这个接口以适应我们的需要。
Let’s create a new interface that extends Repository:
让我们创建一个扩展Repository的新接口。
@NoRepositoryBean
public interface ReadOnlyRepository<T, ID> extends Repository<T, ID> {
Optional<T> findById(ID id);
List<T> findAll();
}
Here, we’ve only defined two read-only methods. The entity that is accessed by this repository will be safe from any modification.
在这里,我们只定义了两个只读的方法。被这个存储库访问的实体将是安全的,不会有任何修改。
It is also important to note that we must use the @NoRepositoryBean annotation to tell Spring that we want this repository to remain generic. This allows us to reuse our read-only repository for as many different entities as we want.
同样重要的是,我们必须使用@NoRepositoryBean注解来告诉Spring,我们希望这个资源库保持通用。这使得我们可以将我们的只读资源库重复用于我们想要的不同实体。
Next, we’ll see how to tie an entity to our new ReadOnlyRepository.
接下来,我们将看到如何将一个实体与我们新的ReadOnlyRepository绑定。
3. Extending ReadOnlyRepository
3.扩展ReadOnlyRepository
Let’s assume we have a simple Book entity that we would like to access:
让我们假设我们有一个简单的Book实体,我们想访问它。
@Entity
public class Book {
@Id
@GeneratedValue
private Long id;
private String author;
private String title;
//getters and setters
}
Now that we have a persistable entity, we can create a repository interface that inherits from our ReadOnlyRepository:
现在我们有了一个可持续的实体,我们可以创建一个继承自ReadOnlyRepository的存储库接口。
public interface BookReadOnlyRepository extends ReadOnlyRepository<Book, Long> {
List<Book> findByAuthor(String author);
List<Book> findByTitle(String title);
}
In addition to the two methods that it inherits, we’ve added two more Book-specific read-only methods: findByAuthor() and findByTitle(). In total, this repository has access to four read-only methods.
除了它所继承的两个方法外,我们还增加了两个针对图书的只读方法。findByAuthor()和findByTitle()。总的来说,这个资源库可以访问四个只读方法。
Finally, let’s write a test that will ensure the functionality of our BookReadOnlyRepository:
最后,让我们写一个测试,确保我们的BookReadOnlyRepository的功能。
@Test
public void givenBooks_whenUsingReadOnlyRepository_thenGetThem() {
Book aChristmasCarolCharlesDickens = new Book();
aChristmasCarolCharlesDickens.setTitle("A Christmas Carol");
aChristmasCarolCharlesDickens.setAuthor("Charles Dickens");
bookRepository.save(aChristmasCarolCharlesDickens);
Book greatExpectationsCharlesDickens = new Book();
greatExpectationsCharlesDickens.setTitle("Great Expectations");
greatExpectationsCharlesDickens.setAuthor("Charles Dickens");
bookRepository.save(greatExpectationsCharlesDickens);
Book greatExpectationsKathyAcker = new Book();
greatExpectationsKathyAcker.setTitle("Great Expectations");
greatExpectationsKathyAcker.setAuthor("Kathy Acker");
bookRepository.save(greatExpectationsKathyAcker);
List<Book> charlesDickensBooks = bookReadOnlyRepository.findByAuthor("Charles Dickens");
Assertions.assertEquals(2, charlesDickensBooks.size());
List<Book> greatExpectationsBooks = bookReadOnlyRepository.findByTitle("Great Expectations");
Assertions.assertEquals(2, greatExpectationsBooks.size());
List<Book> allBooks = bookReadOnlyRepository.findAll();
Assertions.assertEquals(3, allBooks.size());
Long bookId = allBooks.get(0).getId();
Book book = bookReadOnlyRepository.findById(bookId).orElseThrow(NoSuchElementException::new);
Assertions.assertNotNull(book);
}
In order to save the books into the database before reading them back out, we created a BookRepository that extends CrudRepository in the test scope. This repository is not needed in the main project scope but was necessary for this test.
为了在读回书籍之前将其保存到数据库中,我们在测试范围内创建了一个扩展CrudRepository的BookRepository。这个存储库在主项目范围内不需要,但对这个测试来说是必要的。
public interface BookRepository
extends BookReadOnlyRepository, CrudRepository<Book, Long> {}
We were able to test all four of our read-only methods and can now reuse the ReadOnlyRepository interface for other entities.
我们能够测试所有四个只读方法,现在可以为其他实体重新使用ReadOnlyRepository接口。
4. Conclusion
4.总结
We learned how to extend Spring Data’s Repository interface in order to create a reusable read-only repository. After that, we tied it to a simple Book entity and wrote a test that proved its functionality works as we would expect.
我们学习了如何扩展Spring Data的Repository接口,以创建一个可重用的只读存储库。之后,我们把它和一个简单的Book实体联系起来,并写了一个测试,证明其功能如我们所期望的那样工作。
As always, a working example of this code can be found over on GitHub.
像往常一样,可以在GitHub上找到这个代码的工作实例。