Spring Data на примере JPA

Введение


Spring Data позволяет легче создавать Spring-управляемые приложения которые используют новые способы доступа к данным, например нереляционные базы данных, map-reduce фреймворки, cloud сервисы, а так же уже хорошо улучшенную поддердку реляционных баз данных.

В этой статье будет рассмотрен один из под-проектов Spring Data — JPA

Что может Spring Data — JPA

  • Создание и поддержка репозиториев созданных при помощи Spring и JPA
  • Поддержка QueryDSL и JPA запросов
  • Аудит доменных классов
  • Поддержка порционной загрузки, сортировки, динамимических запросов
  • Поддержка XML мэппинга для сущностей


Для чего вам может понадобиться Spring Data — JPA

Я бы ответил так — если вам нужно быстро в проекте создать Repository слой базируемый на JPA, предназначенный в основном для CRUD операций, и вы не хотите создавать абстрактные дао, интерфейсы их реализации, то Spring Data — JPA это хороший выбор.

С чего начать


Будем считать у вас уже есть maven проект с подключенным Spring, базой данных, настроенным EntityManager-ом.

1. Добавьте артефакт со Spring Data — JPA

<dependency>
   <groupId>org.springframework.data</groupId>
   <artifactId>spring-data-jpa</artifactId>
   <version>1.0.2.RELEASE</version>
</dependency>


2. В ваш applicationContext.xml нужно добавить путь где будут храниться ваши Repository интерфейсы

<jpa:repositories base-package="com.test.repository" />


3. Создать Entity и Repository интерфейс для него

package com.test.entity;
...
 
@Entity
public class Test {
 
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = "id")
  private Long id;
 
  private boolean dummy;

  private int tries;

  ...

}

package com.test.repository;
 
import org.springframework.data.repository.CrudRepository;
import com.test.entity.Test;
 
public interface TestRepository extends CrudRepository<Test, Long> {}


4. Теперь вы можете использовать созданный интерфейс в вашем приложении

public class TestServiceImpl extends TestService {
 
   @Autowired
   TestRepository testRepository;
 
  ...
}


Наследовавшись от CrudRepository вы получили возможность вызывать такие методы как:

  • save
  • findOne
  • exists
  • findAll
  • count
  • delete
  • deleteAll


без необходимости реализовывать их имплементацию.

Работа с запросами, сортировкой, порционной загрузкой


Рассмотрим на примере: вам нужно сделать запрос, который выберет все Test записи, у которых поле «dummy» установленно в false, и записи отсортированны по полю «tries» в порядке ABC.

Для решения такой задачи вы можете выбрать один из нескольких вариантов:

Вариант 1:

@Query("FROM Test where dummy = ?1 ORDER BY tries ASC")
List<Product> findTests(boolean dummyVal);


, или вариант 2:

List<Test> findByDummyOrderByTriesAsc(boolean dummyVal);


Если с первым способом все предельно просто и это знакомый запрос, то второй способ заключается в том, чтобы составить имя метода, особым способом использую ключевые слова, такие как: «find», «order», имя переменных и тд. Разработчики Spring Data — JPA постарались учесть большинство возможных вариантов, которые могут вам понадобится.

Specification и CriteriaBuilder


Если вам нужно написать действительно сложный запрос для этого вы можете использовать Specification.
Пример в котором в зависимости от «retries» будут выбраны данные с разными значениями «dummy».

public final class TestSpecs {

    public static Specification<Test> checkRetries(final int retries) {
        return new Specification<Test>() {
            @Override
            public Predicate toPredicate(Root<Test> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                if (retries > 10) {
                    return cb.equal(root.get("dummy"), false);
                } else {
                    return cb.equal(root.get("dummy"), true);
                }
            }
        };
    }

}


Следующий пример покажет как можно использовать созданный Specification для фильтра всех данных.
Расширим ваш интерфейс при помощи JpaSpecificationExecutor

public interface TestRepository extends CrudRepository<Test, Long>, JpaSpecificationExecutor<Test> {}


и вызовем метод findAll передав ему созданную Specification

public class TestServiceImpl extends TestService {
 
   @Autowired
   TestRepository testRepository;

   public void doTest() {
      int retries = 5;
      List<Test> result = testRepository.findAll(Specifications.where(TestSpecs.checkRetries(retries))
      ...
   }

}


Документация


Основной сайт проекта со списком всех Spring Data подпроектов.
Сайт проекта Spring Data — JPA
Техническая документация с примерами всех возможностей

Комментарии 12

    0
    Оу, крайне интересная штука. Я почему-то считал, что она только для no-sql. Спасибо!
      0
      она модульная и есть очень много интересных решений для EJB…
      0
      К сожалению, не поддерживает Java Configuration.
        0
        Я бы ещё добавил абзац про Custom Implementation
        Бывает, что нужная операция не помещается в название метода и один запрос @Query. Да и использовать CriteriaBuilder иногда сложновато. Тогда на помощь могут прийти эти самые custom implementations, чтобы написать что-то, напрямую используя EntityManager (или похожие классы для NoSql источников данных)
          0
          А тут небольшой реальный пример. Оно полезно и с другой стороны, при рефакторинге с целью перехода на Spring Data JPA. Допустим есть большой проект, где все операции идут через EntityManager. Делаем кастом репозитории на все это, а потом постепенно переводим на @Query/Query from method names/Specification убирая методы из кастом репозитория.
          +1
          Все бы ничего, но CrudRepository в текущей версии не существует, переведенная документация не актуальна. Но есть интерфейс JpaRepository и имплементация SimpleJpaRepository. Причем у SimpleJpaRepository нет дифолтного конструктора, очень удобно :) Поигрался я с этой поделкой и решил что использовать пока что не стану, рановато.

          Делал CRUD репзитории подобным не красивым образом:
          @Repository
          public class SmartModelRepositoryImpl extends SimpleJpaRepository<SmartModel, Long> implements
          SmartModelRepository<SmartModel, Long> {

          public SmartModelRepositoryImpl(Class domainClass, EntityManager em) {
          super(domainClass, em);
          }

          @Autowired
          public SmartModelRepositoryImpl(SharedEntityManagerBean emb) {
          this(SmartModel.class, emb.getObject());
          }

          }

          +

          />

          0
          Поработал плотнее с этой библиотекой (перевел на не один небольшой проект, где ранее было сделано на code.google.com/p/hibernate-generic-dao/), мнение изменилось в положительную сторону, можно сказать код стал даже немного изящным :)

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое