Представляем Spring Data JDBC

от автора

В предстоящий релиз Spring Data под кодовым именем Lovelace мы собираемся включить новый модуль: Spring Data JDBC.

Идея Spring Data JDBC заключается в том, чтобы предоставить доступ к реляционным базам данных без использования всей сложности JPA.

JPA предлагает такие функции, как ленивая загрузка (lazy loading), кеширование и отслеживание изменений (dirty tracking). Не смотря на то, что эти фичи очень крутые, если они, конечно, вам действительно нужны, они могут сильно усложнить понимание логики доступа к данным.

Мехнизм ленивой загрузки может внезапно выполнить ресурсоемкие запросы, или и вовсе упасть с исключением. Кэширование может встать на вашем пути когда вы решите сравнить две версии entity, а вкупе с отслеживанием изменений помешает понять — в какой же момент реально выполнятся все операции с базой данных?

Spring Data JDBC фокусируется на гораздо более простой модели. Не будет кеширования, отслеживания изменений, или ленивой загрузки. Вместо этого, SQL запросы будут выполнены тогда и только тогда, когда вы вызваете метод репозитория. Возвращаемый результат будет полностью загружен в память после выполнения метода. Не будет и механизма «сесии» или прокси-объектов для entities. И все это должно сделать Spring Data JDBC более простым и понятным инструментов для доступа к данным.

Разумеется, такой упрощенный подход выливается в целый ряд ограничений, о которых мы поговорим в следующих постах. Грядущий релиз это самая первая версия библиотеки, у нас есть много планов и замыслов, которые мы хотим реализовать, но мы вынуждены их отложить, чтобы дать вам возможность начать пользоваться Spring Data JDBC как можно раньше.

Пример

Для начала, нам нужно определить entity:

class Customer {     @Id     Long id;     String firstName;     LocalDate dob; }

Обратите внимание, что мы не определяем ни геттеров, ни сеттеров. Вы, конечно, можете их добавить, если хотите. В сущности, единственное требование к entity — это иметь поле аннотированное аннотацией Id (но именно org.springframework.data.annotation.Id, а не javax.persistence one).

Дальше, нужно определить репозиторий. Самый простой способ это сделать — расширить интерфейс CrudRepository.

interface CustomerRepository extends CrudRepository<Customer, Long> {}

Наконец, нужно сконфигурировать ApplicationContext чтобы реализация этого интерфейса была созданы автоматически:

@Configuration @EnableJdbcRepositories (1) public class CustomerConfig extends JdbcConfiguration { (2)      @Bean     NamedParameterJdbcOperations operations() { (3)         return new NamedParameterJdbcTemplate(dataSource());     }      @Bean     PlatformTransactionManager transactionManager() { (4)         return new DataSourceTransactionManager(dataSource());     }      @Bean     DataSource dataSource(){ (5)         return new EmbeddedDatabaseBuilder()                 .generateUniqueName(true)                 .setType(EmbeddedDatabaseType.HSQL)                 .addScript("create-customer-schema.sql")                 .build();     } }

Давайте разберем конфигурацию более детально.

  1. EnableJdbcRepositories активирует автоматическое создание репозиториев. Для того, чтобы это работало, нужно предоставить несколько дополнительных бинов, для чего и потребуется остальная часть нашего класса конфигурации.
  2. Т.к. конфигурационный класс расширяет JdbcConfiguration, несколько бинов будут добавлены в контекст автоматически. Их так же можно перекрыть, если нужно изменить поведение Spring Data JDBC. Но в данном примере мы оставим поведение по-умолчанию.
  3. Очень важный компонент это NamedParameterJdbcOperations, который используется для выполнения запросов к базе.
  4. Менеджер транзакций, строго говоря, не обязателен. Но без него не будет и поддержки транзакций, а это мало кому понравится, правда?
  5. Spring Data JDBC не использует DataSource напрямую, но TransactionManager и NamedParameterJdbcOperation требуют его наличия в контексте, поэтому мы и определяем нужный бин.

Это все, что нужно, чтобы начать работать со Spring Data JDBC. Теперь напишем тест, чтобы посмотреть, как это все работает:

@RunWith(SpringRunner.class) @Transactional @ContextConfiguration(classes = CustomerConfig.class) public class CustomerRepositoryTest {      @Autowired CustomerRepository customerRepo;      @Test     public void createSimpleCustomer() {          Customer customer = new Customer();         customer.dob = LocalDate.of(1904, 5, 14);         customer.firstName = "Albert";          Customer saved = customerRepo.save(customer);          assertThat(saved.id).isNotNull();          saved.firstName = "Hans Albert";          customerRepo.save(saved);          Optional<Customer> reloaded = customerRepo.findById(saved.id);          assertThat(reloaded).isNotEmpty();          assertThat(reloaded.get().firstName).isEqualTo("Hans Albert");     } }

Аннотация @Query

Только со стандартными методами CRUD репозитория из класса CrudRepository далеко не уедешь. Мы намеренно решили отложить автоматическую генерацию запроса — популярную фичу Spring Data, когда SQL запрос генерится на основе имени метода — на будущие релизы. А на текущий момент, вы можете просто использовать уже знакомую аннотацию @Query чтобы точно указать, какой SQL запрос должен быть выполнен.

@Query("select id, first_name, dob from customer where upper(first_name) like '%' || upper(:name) || '%' ") List<Customer> findByName(@Param("name") String name);

Если вы хотите изменять или удалять данные в запросе, можете добавить к методу аннотацию @Modifying.

Давайте напишем тест, чтобы посмотреть, как работает наш новый метод.

@Test public void findByName() {      Customer customer = new Customer();     customer.dob = LocalDate.of(1904, 5, 14);     customer.firstName = "Albert";      Customer saved = customerRepo.save(customer);      assertThat(saved.id).isNotNull();      customer.id= null; (1)     customer.firstName = "Bertram";      customerRepo.save(customer);      customer.id= null;     customer.firstName = "Beth";      customerRepo.save(customer);      assertThat(customerRepo.findByName("bert")).hasSize(2); (2) }

  1. Т.к. связь между Java объектов и записью в базе только по полю Id и типу, то установка Idв null и сохранение этого объекта создаст новую запись.
  2. В запросе мы используем case-sensitive like, и поэтому мы находим « Albert » и « Bertram », но не « Beth ».

В завершение

Конечно, о Spring Data JDBC можно рассказать гораздо больше, и мы обязательно расскажем в следующих статьях.

А пока, вы можете изучить код примеров, документацию, и, разумеется, исходный код. Если у вас появятся вопросы — не стесняйтесь их задавать на StackOverflow. А если вы нашли баг или хотите запросить новую фичу — пожалуйста, создайте тикет.


ссылка на оригинал статьи https://habr.com/post/423697/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *