Статьи

Что нового в весеннем выпуске данных Гослинг?

Более 300 проблем, исправленных в 12 проектах, усложняют отслеживание событий, произошедших с момента последнего выпуска. Итак, вот более подробный отрывок из некоторых новых функций, которые мы готовили во время последней итерации.

Специальные выборочные графики JPA.

После выпуска релиза Dijkstra мы смогли ссылаться на граф именованных сущностей, объявленный на сущности, посредством @EntityGraphаннотации в репозиториях, поддерживаемых JPA. В приведенном ниже примере это заставляет имя и фамилию загружаться с нетерпением, в то время как все остальные остаются загруженными лениво.

@Entity
@NamedEntityGraphs(
  @NamedEntityGraph(name = "with-tags",
    attributeNodes = { @NamedAttributeNode("tags") }))
class Product {

  @ManyToMany
  Set<Tag> tags;

  // other properties omitted
}

interface ProductRepository extends Repository<Customer, Long> {

  @EntityGraph("with-tags")
  Product findOneById(Long id);
}

Выпуск Гослинга теперь продвигает нашу историю JPA 2.1 на один шаг вперед, расширяя ее до специальных определений графиков. При явном указании свойств с помощью @EntityGraph(attributePaths = …)метода запроса вам не нужно NamedEntityGraphдобавлять аннотацию к вашей сущности.

@Entity
class Product {

  @ManyToMany
  Set<Tag> tags;

  // other properties omitted
}

interface ProductRepository extends Repository<Customer, Long> {

  @EntityGraph(attributePaths = {"tags"})
  Product findOneById(Long id);
}

Веб-поддержка Querydsl

Веб-поддержка Spring Data уже позволяет вам объявлять параметры типа Pageableв методах вашего контроллера. Недавно представленная интеграция с Querydsl расширяет возможности, позволяя вам получить готовый к использованию продукт, Predicateполученный непосредственно из строки запроса HTTP. Эта функция автоматически включается, когда @EnableSpringDataWebSupportнастроен и Querydsl найден в пути к классам.

Если Predicateиспользуется без дальнейшей настройки, мы попытаемся разрешить корневой тип для Predicateразрешения из возвращаемого типа метода, хотя в большинстве случаев может быть лучше явно объявить желаемую ссылку на тип через @QuerydslPredicate(root = …). Имея это на месте, атрибуты строки запроса привязываются к соответствующим свойствам типа, который создает, например,

QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews")) 

от ?firstname=Dave&lastname=Matthewsиспользования привязки, зависящей от типа свойства по умолчанию.

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Controller
  @RequiredArgsConstructor(onConstructor = @__(@Autowired))
  static class UserController {

    private final UserRepository repository;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    String index(Model model,
                 @QuerydslPredicate(root = User.class) Predicate predicate,
                 Pageable pageable) {

      model.addAttribute("users", repository.findAll(predicate, pageable));
      return "index";
    }
  }
}

Теперь не всегда имеет смысл использовать привязку по умолчанию (равно), а скорее выделенную привязку для свойства или для конкретного типа. Для достижения этого просто переопределите значения по умолчанию, предоставив объект, QuerydslBinderCustomizerкоторый может быть зарегистрирован через @QuerydslPredicate(bindings = …)или просто внедрен в хранилище.

interface UserRepository extends CrudRepository<User, String>,
    QueryDslPredicateExecutor<User>,
    QuerydslBinderCustomizer<QUser> {

  // Query methods go here

  @Override
  default public void customize(QuerydslBindings bindings, QUser user) {

    bindings.bind(user.nationality).first(
      (path, value) -> path.equalsIgnoreCase(value)); // 1
    bindings.bind(String.class).first(
      (StringPath path, String value) -> path.containsIgnoreCase(value)); // 2
    bindings.excluding(user.password);
  }
}

Как вы можете видеть, мы используем лямбды Java 8 вдоль ссылок на типобезопасные свойства стороннего Querydsl, чтобы определить привязку для выделенного свойства (1) или для всех свойств данного типа (2). Использование QuerydslBindings.excludingпозволяет удалить пути из запросов.

Найдите полный рабочий пример в репозитории примеров Spring Data и ознакомьтесь со справочной документацией для получения подробной информации.

Spring Data REST

Поддержка Querydsl

Поддержка Querydsl, представленная в Spring Data Commons (см.), Была интегрирована в Spring Data REST. Это означает, что вы можете фильтровать ресурсы вашей коллекции, добавляя простые параметры запроса свойств в URI запроса.

В примере Spring Data REST обнажая адреса магазинов Starbucks, обратите внимание , как StoreRepository в настоящее время реализует как QueryDslPredicateExecutorи QuerydslBinderCustomizer<QStore>так же , как описано выше.

Ресурс сбора для хранилищ, предоставляемых Spring Data REST, позволит вам затем отправлять такие запросы:

$ http :8080/api/stores?address.city=York

{
    "_embedded": {
        "stores": [
            {
                "_links": {
                }, 
                "address": {
                    "city": "New York", 
                    "location": { "x": -73.938421, "y": 40.851 }, 
                    "street": "803 W 181st St", 
                    "zip": "10033-4516"
                }, 
                "name": "Washington Hgts/181st St"
            }, 
            {
                "_links": {
                }, 
                "address": {
                    "city": "New York", 
                    "location": { "x": -73.939822, "y": 40.84135 }, 
                    "street": "4001 Broadway", 
                    "zip": "10032-1508"
                }, 
                "name": "168th & Broadway"
            }, 
        ]
    }, 
    "_links": {
    }, 
    "page": {
        "number": 0, 
        "size": 20, 
        "totalElements": 209, 
        "totalPages": 11
    }
}

Обратите внимание, как возвращаются только магазины, чей город оканчивается на «Йорк», как определено в реализации QuerydslBinderCustomizerв StoresRepository.

В настоящее время мы ищем варианты, чтобы более четко рекламировать этот механизм запросов, например, использовать переменные шаблона и даже предоставлять расширенные средства отображения для настройки имен параметров запроса, которые будут использоваться.

Пользовательский браузер HAL

Релиз Gosling Spring Data REST поставляется с дополнительным модулем, который оборачивает HAL-браузер Майка Келли и настраивает его с помощью нескольких настроек, позволяющих использовать метаданные API, которые мы предоставляем. Чтобы использовать браузер с вашим приложением, просто добавьте spring-data-rest-hal-browserмодуль в ваш проект, и ваш корень API будет обслуживать браузер для приема запросов text/html. Стандартные ответы HAL, конечно, по-прежнему обслуживаются по умолчанию или если вы используете Acceptзаголовок на основе JSON .

Хотя модуль Spring Data REST позволяет легко добавить браузер в ваше приложение, он также слегка настраивает браузер. Когда вы нажимаете кнопку, чтобы вызвать незапрошенный GETзапрос, браузер обычно открывает модальное диалоговое окно, которое ожидает некоторый необработанный ввод JSON. Хотя это, конечно, замечательно, если вы знаете, что делаете, это немного подвержено ошибкам и не очень удобно, так как вы должны знать о структуре данных, которую ожидает сервер.

Spring Data REST предоставляет документы схемы JSON для типов, предоставляемых системой, используя profileотношение ссылок, что делает схему общедоступной для обнаружения без привязки логики обнаружения к самому Spring Data REST. Экземпляр браузера, который мы отправляем, ищет эти метаданные схемы и, если он может их найти, передает их редактору JSON, чтобы заменить диалог по умолчанию формой, полностью производной от схемы JSON.

Посмотрите, как форма позволяет добавлять позиции, поскольку схема представляет ее как массив. Поля цены и даты заказа помечены как доступные только для чтения, поле местоположения позволяет выбирать значения из перечисления с интернационализированными значениями.

Пример проекта можно найти на GitHub .

Интернационализация ссылок и перечислений

Как вы могли видеть на скриншотах выше, restbucks:ordersссылка сопровождалась понятным для человека описанием. Описания извлекаются из необязательного пакета ресурсов rest-messagesс использованием _links.$rel.titleключей для определения читаемого значения. В этом примере используется rest-messages.propertiesзапасной пакет ресурсов, но он также содержит rest-messages_de.propertiesметки для возврата немецких меток для клиентов, отправляющих Accept-Languageнабор заголовков de.

Тот же пакет ресурсов можно использовать для интернационализации значений перечисления, чтобы их можно было использовать на клиенте в удобочитаемой форме. Чтобы не сломать существующие приложения, это должно быть явно активировано через RepositoryRestConfiguration.setEnableEnumTranslation(…). Подробности о переводе можно настроить на EnumTranslationConfiguration.

Spring Data GemFire ​​и Apache Geode

Поддержка Pivotal GemFire ​​8.1 и Apache Geode является наиболее заметным дополнением к Spring Data GemFire ​​1.7. Pivotal GemFire ​​был представлен инкубатору Apache ранее в этом году, и команда Spring Data быстро отреагировала, чтобы включить поддержку в Spring Data GemFire.

Кроме того, было добавлено несколько других функций для упрощения разработки приложений GemFire ​​и Apache Geode с использованием Spring. Например, разработчики могут теперь определять конкретные политики истечения срока действия объекта домена приложения, используя аннотации:

@TimeToLiveExpiration(
  timeout = "@expirationSettings['spel.defined.timeout']" action="DESTROY")
@IdleTimeoutExpiration(
  timeout = "1800" action="${property.placeholder.defined.action}")
class ApplicationDomainObject { … }

Аннотации годности на основе поддержки как SPEL и Spring PlaceHolder собственности значения. Чтобы включить политики истечения срока действия на основе аннотаций, вам нужно только настроить реализацию CustomExpiry Spring Data GemFire , AnnotationBasedExpiration , в регионах GemFire ​​для одного или обоих типов TTL и TTI:

<gfe:partitioned-region id="Example" persistent="false" …>
  <gfe:custom-entry-ttl>
    <bean class="….gemfire.support.AnnotationBasedExpiration" factory-method="forTimeToLive"/>
  </gfe:custom-entry-ttl>
  <gfe:custom-entry-tti ref="ttiExpiration"/>
</gfe:partitioned-region>

<bean id="ttiExpiration" class="….gemfire.support.AnnotationBasedExpiration" factory-method="forIdleTimeout">
  <constructor-arg ref="defaultExpirationAttributes"/>
</bean>

<bean id="defaultExpirationAttributes" class="….ExpirationAttributes">
  <constructor-arg value="600"/>
  <constructor-arg value="#{T(….ExpirationAction).DESTROY}"/>
</bean>

Смотрите справочное руководство, чтобы узнать больше . Далее была добавлена ​​поддержка расширений OQL методом запросов к репозиторию с помощью аннотаций:

interface CustomerRepository implements CrudRepository<Cutomer, Long> {

  @Trace
  @Limit(25)
  @Import("org.exmple.Customer")
  @Hint("CustomerLastNameIdx")
  List<Customer> findByLastNameOrderByLastNameAsc(String lastName);
}

@Traceвключает отладку отдельных операторов OQL. @Limitограничивает число результатов в наборе результатов запроса и @Importпозволяет приложениям различать типы объектов с одинаковыми именами. Например, ваше приложение может определять как org.example.app.core.Customerи org.example.app.vendor.xyz.Customerтипы. См GemFire в документ для получения более подробной информации. @Hintпозволяет использовать подсказки OQL для определения индексов, применимых к запросу. Узнайте больше о расширениях OQL здесь .

Наконец, Spring Data GemFire ​​предлагает поддержку GemFire Cache и моментальных снимков регионов, используя пространство имен Spring Data GemFire ​​XML:

<gfe:partitioned-region id="Example" persistent="false" … />

<gfe-data:snapshot-service id="exampleRegionSnapshotService" region-ref="Example">
  <gfe-data:snapshot-import location="/path/to/import/example.snapshot"/>
  <gfe-data:snapshot-export locator="/path/to/export/example.snapshot"/>
</gfe-data:snapshot-service>

Вы можете узнать больше о том, как Spring Data GemFire ​​поддерживает ZIP-файлы при импорте, об использовании Spring ApplicationEventsдля запуска импорта и экспорта моментальных снимков, а также о том, как правильно фильтровать данные, импортируемые и экспортируемые, здесь .

Spring Data KeyValue и хранилища на основе карт

Прошло довольно много времени с тех пор, как нас попросили предоставить очень простую Mapоснованную реализацию для репозиториев Spring Data для различных — в основном для тестирования — целей. В конечном итоге запросы завершились активацией модуля KeyValue немного другим способом, чем он существовал ранее.

Spring Data KeyValue теперь состоит из базовой Mapреализации репозитория, которая будет использовать Spring Expression Language (SpEL) для запроса значений по умолчанию, обеспечивает сортировку, разбиение на страницы и интеграцию Querydsl на основе своего модуля сбора. Он также предоставляет специальные API-интерфейсы, позволяющие хранилищам ключей-значений использовать специфичные для магазина оптимизации при хранении, поиске и, что наиболее важно, при выполнении запросов, если это необходимо.

Механизм запросов по умолчанию, используемый репозиториями Spring Data KeyValue, основан на SpEL и позволяет определять и выполнять сложные запросы. Этот подход демонстрирует свою реальную мощность при запуске в COMPILEDрежиме, поскольку он эффективно компилирует выражения фильтра, которые должны быть выполнены для значений. В качестве альтернативы вы также можете использовать выражения Querydsl для безопасных запросов.

@Configuration
@EnableMapRepositories("com.acme.repositories")
class AppConfig {}

@KeySpace("user")
class User {

  String @Id id;
  String firstname;
}

interface UserRepository extends CrudRepository<User, String> {
  List<String> findByFirstnameStartingWith(String firstname);
}

В настоящее время у нас есть расширения этого API для Ehcache, Hazelcast и Aerospike, и мы с нетерпением ждем возможности оценить возможности интеграции Redis и, возможно, перенести некоторые из API Gemfire для их использования.

Что дальше?

Следующим шагом будет SpringOne2GX в Вашингтоне, округ Колумбия — мы будем рады видеть вас там — лучшее место, чтобы связаться с командой, узнать о новых возможностях и просто приятно провести время. Тем временем мы уже готовим следующий сервисный релиз для поезда выпуска Фаулера и начали работу над новыми функциями для поезда выпуска Хоппера (Sshh … Мы дадим краткую характеристику Hopper в выступлении «Что нового в Spring Data?» На SpringOne ).

Релиз написан Кристофом Штроблом.