Во-первых, один предмет домашнего обихода, прежде чем мы начнем...
Я вижу, что вы упомянули Pivotal GemFire 9.7
в справке по документации GemFire выше, но затем ссылаетесь на Spring Data GemFire (SDG) 1.3.3.RELEASE
справочные документы. Я очень надеюсь, что вы не пытаетесь использовать SDG 1.3.3.RELEASE
с Pivotal GemFire 9.7
. SDG 1.3.3 устарел, основан на Pivotal GemFire 7.0.1 и больше не поддерживается.
Я подозреваю, что вы не используете SDG 1.3.x
, поэтому вам, вероятно, следует всегда ссылаться на последнюю документацию, доступную здесь и, в частности, здесь. Даже Поиск Google показывает последние документы.
Кроме того, вы можете обратиться к Spring Data для Pivotal GemFire Матрица совместимости версий для получения более подробной информации. Так или иначе...
Итак, я написал тестовый класс, чтобы лучше понять ваш UC.
В своем тесте я смоделировал класс Person с запрашиваемым age
поле/свойство. У меня есть PersonRepository для типа Person
, и я написал запрос, очень похожий на ваш пример выше, для запроса человека по возрасту в обычный, а также null-safe.
Дело в том, что ваше желание защититься от отсутствия результатов, возвращая null
или 0
, неоднозначно в случае с Хранилищами.
Для 1, если вы вернули несколько результатов (например, как в SELECT a.num FROM /SomeRegion a
, то есть без предиката), и результаты не были упорядочены, то у вас не было бы способа узнать, какое значение было null
для какого ключа, если вы также не вернули ключ в набор результатов.
Конечно, здесь это не так, и вы уточнили запрос по идентификатору, используя предикат (например, ... WHERE a.id = $1
). Однако в этом случае при просмотре запроса (например, SELECT a.num FROM ...
) неясно, не было ли результата для данного идентификатора (например, ключа) или просто num было null
. Вы действительно не знаете.
Мой тест продолжает иллюстрировать этот момент.
У меня есть 2 человека ["Jon Doe", "Jane Doe"]
, здесь. У Джона есть заявленный возраст, а у Джейн - нет. Конечно, оба человека существуют в кэше (т.е. в регионе «Люди»).
Когда я запрашиваю Джона и утверждаю его возраст, я получаю ожидаемый результат.
В качестве альтернативы, когда я запрашиваю Джейн и указываю ее возраст, я могу получить одно из двух значений, null
или 0
, как ожидается здесь (в настоящее время 0
, так как я использую нулевой безопасный запрос). Если я изменю запрос на обычный запрос, то возвращаемое значение для age
Джейн на самом деле будет null
, и я мог утверждать это как таковое.
Однако когда мы приступаем к запросу несуществующего человека, поведение GemFire становится очевидным; нет результатов. Я проиллюстрировал это двумя разными способами (кроме использования репозитория), используя GemFire QueryService
API напрямую, а затем снова с помощью удобного GemfireTemplate
class a>, который просто является оболочкой GemFire Query API (это также метод, используемый репозиторием под капотом; на самом деле репозиторий добавляет значение возможностей сопоставления/преобразования).
Как видите, я должен обработать отсутствие результатов для пример.
В случае репозитория, поскольку вы использовали пользовательский запрос, инфраструктуре репозитория не сразу становится понятно, какая часть (то есть промежуточный результат запроса) была null
.
Что, если бы у вас было SELECT person.address.city.name FROM ...
?
Лицо null
, адрес null
или город null
? Как это должно быть обработано последовательно? Человек может выйти, но адрес может быть null
, что может потребовать другого поведения, чем если бы человек или город были null
.
На самом деле абстракция Репозиторий обрабатывает null
возвращаемых значений, как видно здесь.
Так куда же мы пойдем отсюда?
У нас есть несколько вариантов:
Вы можете выполнить и проверка существования, сначала...
вернуть personRepository.existsById(bobDoe.getName()) ? personRepository.getAge(bobDoe.getName()): 0;
Вы можете справиться с этим за пределами Репозитория в DAO (возможно, адаптировав/украсив Репозиторий и делегировав его для простых запросов, а не для сложных запросов, которые включают использование аннотации @Query
, которые следует использовать с осторожностью).
@Repository class PersonDao {
@Autowired
private PersonRepository personRepository;
Person findById(String name) {
return this.personRepository.findById(name).orElse(null);
}
Integer getAge(String name) {
// Either use the approach in #1, or do https://github.com/jxblum/contacts-application/blob/master/tests-example/src/test/java/example/tests/spring/data/gemfire/NullValueReturningRepositoryQueryMethodIntegrationTests.java#L143-L151.
}
...
}
Все еще
Другим вариантом для нас было бы предоставление дополнительной поддержки в SD, что-то вроде...
@Nullable Integer getAge(String name);
Или, возможно...
Optional<Integer> getAge(String name);
Тем не менее, этот подход не решает проблему неоднозначности и требует дополнительных размышлений.
Что было бы действительно хорошо, если бы OQL обрабатывал оператор Элвиса, что-то вроде (в моем более сложном примере выше)...
ВЫБЕРИТЕ человека?.адрес?.город?.имя ОТ /Люди...
В любом случае, я надеюсь, что это даст вам некоторые идеи. Мне нужно будет еще подумать над пунктом 3 выше, прежде чем двигаться дальше.
Если у вас есть дополнительные вопросы/отзывы/идеи, задайте их в комментариях или не стесняйтесь подать заявку на JIRA. а>.
Спасибо!
person
John Blum
schedule
15.02.2019