1. Обзор
В этом кратком руководстве мы обсудим, как создать репозиторий
данных Spring только для чтения. ``
Иногда необходимо считывать данные из базы данных, не изменяя их. В этом случае было бы идеально иметь интерфейс репозитория
только для чтения .
Это обеспечит возможность чтения данных без риска их изменения кем-либо.
2. Расширение репозитория
Начнем с проекта Spring Boot, который включает зависимость spring-boot-starter-data-jpa
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.4.3</version>
</dependency>
В эту зависимость включен популярный интерфейс CrudRepository
Spring Data , который поставляется с методами для всех основных операций CRUD (создание, чтение, обновление, удаление), которые необходимы большинству приложений. Однако он включает в себя несколько методов, которые изменяют данные, и нам нужен репозиторий, который имеет возможность только читать данные.
CrudRepository
фактически расширяет другой интерфейс под названием Repository
. Мы также можем расширить этот интерфейс, чтобы он соответствовал нашим потребностям.
Давайте создадим новый интерфейс, расширяющий Repository
:
@NoRepositoryBean
public interface ReadOnlyRepository<T, ID> extends Repository<T, ID> {
Optional<T> findById(ID id);
List<T> findAll();
}
Здесь мы определили только два метода только для чтения. Сущность, к которой обращается этот репозиторий, будет защищена от любых изменений.
Также важно отметить, что мы должны использовать аннотацию @NoRepositoryBean
, чтобы сообщить Spring, что мы хотим, чтобы этот репозиторий оставался универсальным. Это позволяет нам повторно использовать наш репозиторий только для чтения для любого количества различных сущностей.
Далее мы увидим, как привязать объект к нашему новому ReadOnlyRepository
.
3. Расширение репозитория только для чтения
Предположим, у нас есть простая сущность Book
, к которой мы хотели бы получить доступ:
@Entity
public class Book {
@Id
@GeneratedValue
private Long id;
private String author;
private String title;
//getters and setters
}
Теперь, когда у нас есть сохраняемая сущность, мы можем создать интерфейс репозитория, который наследуется от нашего ReadOnlyRepository
:
public interface BookReadOnlyRepository extends ReadOnlyRepository<Book, Long> {
List<Book> findByAuthor(String author);
List<Book> findByTitle(String title);
}
В дополнение к двум методам, которые он наследует, мы добавили еще два специфичных для книги метода только для чтения: findByAuthor()
и findByTitle()
. Всего в этом репозитории есть доступ к четырем методам только для чтения.
Наконец, давайте напишем тест, который обеспечит работоспособность нашего 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);
}
Чтобы сохранить книги в базе данных, прежде чем читать их обратно, мы создали BookRepository
, который расширяет CrudRepository
в тестовой области. Этот репозиторий не нужен в рамках основного проекта, но был необходим для этого теста.
public interface BookRepository
extends BookReadOnlyRepository, CrudRepository<Book, Long> {}
Мы смогли протестировать все четыре наших метода только для чтения и теперь можем повторно использовать интерфейс ReadOnlyRepository для других сущностей.
4. Вывод
Мы узнали, как расширить интерфейс репозитория
Spring Data, чтобы создать многоразовый репозиторий только для чтения. После этого мы привязали его к простой сущности Book
и написали тест, который доказал, что его функциональность работает так, как мы и ожидали.
Как всегда, рабочий пример этого кода можно найти на GitHub .